diff --git a/patches/tetgen-1.4.3.patch b/patches/tetgen-1.4.3.patch deleted file mode 100644 index 75a88cc0eea8a653d17945c1c764a364f61e6117..0000000000000000000000000000000000000000 --- a/patches/tetgen-1.4.3.patch +++ /dev/null @@ -1,214 +0,0 @@ ---- /home/andreas/pack/tetgen1.4.3/tetgen.h 2009-12-13 16:20:33.000000000 -0500 -+++ ../src/cpp/tetgen.h 2010-01-22 19:41:28.590818901 -0500 -@@ -87,6 +87,7 @@ - #include - #include - #include -+#include - - // 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. -@@ -213,10 +214,13 @@ - // Note that the points of the polygon must be given in either counter- - // clockwise or clockwise order and they form a ring, so every two - // consective points forms an edge of the polygon. -- typedef struct { -+ struct polygon : public boost::noncopyable { - int *vertexlist; - int numberofvertices; -- } polygon; -+ -+ polygon(); -+ ~polygon(); -+ }; - - static void init(polygon* p) { - p->vertexlist = (int *) NULL; -@@ -225,12 +229,15 @@ - - // The facet data structure. A "facet" describes a facet. Each facet is - // a polygonal region possibly with holes, edges, and points in it. -- typedef struct { -+ struct facet { - polygon *polygonlist; - int numberofpolygons; - REAL *holelist; - int numberofholes; -- } facet; -+ -+ facet(); -+ ~facet(); -+ }; - - static void init(facet* f) { - f->polygonlist = (polygon *) NULL; -@@ -270,12 +277,15 @@ - // 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 { -+ struct pbcgroup { - int fmark1, fmark2; - REAL transmat[4][4]; - int numberofpointpairs; - int *pointpairlist; -- } pbcgroup; -+ -+ pbcgroup(); -+ ~pbcgroup(); -+ }; - - // A callback function for mesh refinement. - typedef bool (* TetSizeFunc)(REAL*, REAL*, REAL*, REAL*, REAL*, REAL); -@@ -549,17 +559,6 @@ - } - - if (facetlist != (facet *) NULL) { -- for (i = 0; i < numberoffacets; i++) { -- f = &facetlist[i]; -- for (j = 0; j < f->numberofpolygons; j++) { -- p = &f->polygonlist[j]; -- delete [] p->vertexlist; -- } -- delete [] f->polygonlist; -- if (f->holelist != (REAL *) NULL) { -- delete [] f->holelist; -- } -- } - delete [] facetlist; - } - if (facetmarkerlist != (int *) NULL) { -@@ -579,12 +578,6 @@ - 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) { -@@ -2381,6 +2374,7 @@ - /////////////////////////////////////////////////////////////////////////////// - - REAL exactinit(); -+void exactdeinit(); - REAL orient3d(REAL *pa, REAL *pb, REAL *pc, REAL *pd); - REAL insphere(REAL *pa, REAL *pb, REAL *pc, REAL *pd, REAL *pe); - ---- /home/andreas/pack/tetgen1.4.3/tetgen.cxx 2009-12-13 16:21:08.000000000 -0500 -+++ ../src/cpp/tetgen.cpp 2010-01-22 19:41:28.590818901 -0500 -@@ -34,6 +34,45 @@ - //// //// - //// //// - -+tetgenio::polygon::polygon() -+{ -+ vertexlist = 0; -+ numberofvertices = 0; -+} -+ -+tetgenio::polygon::~polygon() -+{ -+ if (vertexlist) -+ delete [] vertexlist; -+} -+ -+tetgenio::facet::facet() -+{ -+ polygonlist = 0; -+ numberofpolygons = 0; -+ holelist = 0; -+ numberofholes = 0; -+} -+ -+tetgenio::facet::~facet() -+{ -+ if (polygonlist) -+ delete[] polygonlist; -+ if (holelist) -+ delete[] holelist; -+} -+ -+tetgenio::pbcgroup::pbcgroup() -+{ -+ numberofpointpairs = 0; -+ pointpairlist = 0; -+} -+ -+tetgenio::pbcgroup::~pbcgroup() -+{ -+ delete[] pointpairlist; -+} -+ - /////////////////////////////////////////////////////////////////////////////// - // // - // load_node_call() Read a list of points from a file. // -@@ -34751,6 +34790,7 @@ - if (b->metric) { - delete m.bgm; - } -+ exactdeinit(); - } - - #ifndef TETLIBRARY ---- /home/andreas/pack/tetgen1.4.3/predicates.cxx 2009-12-13 16:18:56.000000000 -0500 -+++ ../src/cpp/predicates.cpp 2010-01-22 19:41:28.576309963 -0500 -@@ -113,6 +113,10 @@ - /* */ - /*****************************************************************************/ - -+#if defined(__linux__) && defined(__i386__) -+ #define LINUX 1 -+#endif -+ - #include - #include - #include -@@ -149,8 +153,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 */ -@@ -660,6 +664,8 @@ - /* */ - /*****************************************************************************/ - -+static int previous_cword; -+ - REAL exactinit() - { - REAL half; -@@ -676,7 +682,9 @@ - _control87(_PC_53, _MCW_PC); /* Set FPU control word for double precision. */ - #endif /* not SINGLE */ - #endif /* CPU86 */ -+ - #ifdef LINUX -+ _FPU_GETCW(previous_cword); - #ifdef SINGLE - /* cword = 4223; */ - cword = 4210; /* set FPU control word for single precision */ -@@ -725,6 +733,13 @@ - return epsilon; /* Added by H. Si 30 Juli, 2004. */ - } - -+void exactdeinit() -+{ -+#ifdef LINUX -+ _FPU_SETCW(previous_cword); -+#endif /* LINUX */ -+} -+ - /*****************************************************************************/ - /* */ - /* grow_expansion() Add a scalar to an expansion. */ diff --git a/src/cpp/tetgen.cpp b/src/cpp/tetgen.cpp index b4743b808adc3d74616e1b5d0bbc7243a2e450fa..79006bfc0219b65b3a0dc577aa3bcc33674e2da6 100644 --- a/src/cpp/tetgen.cpp +++ b/src/cpp/tetgen.cpp @@ -5,12 +5,12 @@ // A Quality Tetrahedral Mesh Generator and 3D Delaunay Triangulator // // // // Version 1.4 // -// September 6, December 13, 2009 // +// April 16, 2007 // // // -// Copyright (C) 2002--2009 // +// Copyright (C) 2002--2007 // // Hang Si // -// Research Group: Numerical Mathematics and Scientific Computing // -// Weierstrass Institute for Applied Analysis and Stochastics (WIAS) // +// Research Group Numerical Mathematics and Scientific Computing // +// Weierstrass Institute for Applied Analysis and Stochastics // // Mohrenstr. 39, 10117 Berlin, Germany // // si@wias-berlin.de // // // @@ -30,9 +30,24 @@ #include "tetgen.h" -//// io_cxx /////////////////////////////////////////////////////////////////// -//// //// -//// //// +/////////////////////////////////////////////////////////////////////////////// +// // +// terminatetetgen() Terminate TetGen with a given exit code. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void terminatetetgen(int x) +{ +#ifdef TETLIBRARY + throw x; +#else + exit(x); +#endif // #ifdef TETLIBRARY +} + +// +// Begin of class 'tetgenio' implementation +// tetgenio::polygon::polygon() { @@ -75,13 +90,194 @@ tetgenio::pbcgroup::~pbcgroup() /////////////////////////////////////////////////////////////////////////////// // // -// load_node_call() Read a list of points from a file. // +// initialize() Initialize all variables of 'tetgenio'. // +// // +// It is called by the only class constructor 'tetgenio()' implicitly. Thus, // +// all variables are guaranteed to be initialized. Each array is initialized // +// to be a 'NULL' pointer, and its length is equal zero. Some variables have // +// their default value, 'firstnumber' equals zero, 'mesh_dim' equals 3, and // +// 'numberofcorners' equals 4. Another possible use of this routine is to // +// call it before to re-use an object. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenio::initialize() +{ + firstnumber = 0; // Default item index is numbered from Zero. + mesh_dim = 3; // Default mesh dimension is 3. + useindex = true; + + pointlist = (REAL *) NULL; + pointattributelist = (REAL *) NULL; + pointmtrlist = (REAL *) NULL; + pointmarkerlist = (int *) NULL; + numberofpoints = 0; + numberofpointattributes = 0; + numberofpointmtrs = 0; + + tetrahedronlist = (int *) NULL; + tetrahedronattributelist = (REAL *) NULL; + tetrahedronvolumelist = (REAL *) NULL; + neighborlist = (int *) NULL; + numberoftetrahedra = 0; + numberofcorners = 4; // Default is 4 nodes per element. + numberoftetrahedronattributes = 0; + + trifacelist = (int *) NULL; + adjtetlist = (int *) NULL; + trifacemarkerlist = (int *) NULL; + numberoftrifaces = 0; + + facetlist = (facet *) NULL; + facetmarkerlist = (int *) NULL; + numberoffacets = 0; + + edgelist = (int *) NULL; + edgemarkerlist = (int *) NULL; + numberofedges = 0; + + holelist = (REAL *) NULL; + numberofholes = 0; + + regionlist = (REAL *) NULL; + numberofregions = 0; + + facetconstraintlist = (REAL *) NULL; + numberoffacetconstraints = 0; + segmentconstraintlist = (REAL *) NULL; + numberofsegmentconstraints = 0; + + pbcgrouplist = (pbcgroup *) NULL; + numberofpbcgroups = 0; + + vpointlist = (REAL *) NULL; + vedgelist = (voroedge *) NULL; + vfacetlist = (vorofacet *) NULL; + vcelllist = (int **) NULL; + numberofvpoints = 0; + numberofvedges = 0; + numberofvfacets = 0; + numberofvcells = 0; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// deinitialize() Free the memory allocated in 'tetgenio'. // +// // +// It is called by the class destructor '~tetgenio()' implicitly. Hence, the // +// occupied memory by arrays of an object will be automatically released on // +// the deletion of the object. However, this routine assumes that the memory // +// is allocated by C++ memory allocation operator 'new', thus it is freed by // +// the C++ array deletor 'delete []'. If one uses the C/C++ library function // +// 'malloc()' to allocate memory for arrays, one has to free them with the // +// 'free()' function, and call routine 'initialize()' once to disable this // +// routine on deletion of the object. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenio::deinitialize() +{ + facet *f; + polygon *p; + pbcgroup *pg; + int i, j; + + using namespace std; + if (pointlist != (REAL *) NULL) { + delete [] pointlist; + } + if (pointattributelist != (REAL *) NULL) { + delete [] pointattributelist; + } + if (pointmtrlist != (REAL *) NULL) { + delete [] pointmtrlist; + } + if (pointmarkerlist != (int *) NULL) { + delete [] pointmarkerlist; + } + + if (tetrahedronlist != (int *) NULL) { + delete [] tetrahedronlist; + } + if (tetrahedronattributelist != (REAL *) NULL) { + delete [] tetrahedronattributelist; + } + if (tetrahedronvolumelist != (REAL *) NULL) { + delete [] tetrahedronvolumelist; + } + if (neighborlist != (int *) NULL) { + delete [] neighborlist; + } + + if (trifacelist != (int *) NULL) { + delete [] trifacelist; + } + if (adjtetlist != (int *) NULL) { + delete [] adjtetlist; + } + if (trifacemarkerlist != (int *) NULL) { + delete [] trifacemarkerlist; + } + + if (edgelist != (int *) NULL) { + delete [] edgelist; + } + if (edgemarkerlist != (int *) NULL) { + delete [] edgemarkerlist; + } + + if (facetlist != (facet *) NULL) { + delete [] facetlist; + } + + if (facetmarkerlist != (int *) NULL) { + delete [] facetmarkerlist; + } + + if (holelist != (REAL *) NULL) { + delete [] holelist; + } + if (regionlist != (REAL *) NULL) { + delete [] regionlist; + } + if (facetconstraintlist != (REAL *) NULL) { + delete [] facetconstraintlist; + } + if (segmentconstraintlist != (REAL *) NULL) { + delete [] segmentconstraintlist; + } + if (pbcgrouplist != (pbcgroup *) NULL) { + delete [] pbcgrouplist; + } + if (vpointlist != (REAL *) NULL) { + delete [] vpointlist; + } + if (vedgelist != (voroedge *) NULL) { + delete [] vedgelist; + } + if (vfacetlist != (vorofacet *) NULL) { + for (i = 0; i < numberofvfacets; i++) { + delete [] vfacetlist[i].elist; + } + delete [] vfacetlist; + } + if (vcelllist != (int **) NULL) { + for (i = 0; i < numberofvcells; i++) { + delete [] vcelllist[i]; + } + delete [] vcelllist; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// load_node_call() Load a list of nodes. // // // // It is a support routine for routines: 'load_nodes()', 'load_poly()', and // // 'load_tetmesh()'. 'infile' is the file handle contains the node list. It // // may point to a .node, or .poly or .smesh file. 'markers' indicates each // // node contains an additional marker (integer) or not. 'infilename' is the // -// name of the file being read, it is only used in error messages. // +// name of the file being read, it is only appeared in error message. // // // // The 'firstnumber' (0 or 1) is automatically determined by the number of // // the first index of the first point. // @@ -100,17 +296,20 @@ bool tetgenio::load_node_call(FILE* infile, int markers, char* infilename) // Initialize 'pointlist', 'pointattributelist', and 'pointmarkerlist'. pointlist = new REAL[numberofpoints * 3]; if (pointlist == (REAL *) NULL) { + printf("Error: Out of memory.\n"); terminatetetgen(1); } if (numberofpointattributes > 0) { pointattributelist = new REAL[numberofpoints * numberofpointattributes]; if (pointattributelist == (REAL *) NULL) { + printf("Error: Out of memory.\n"); terminatetetgen(1); } } if (markers) { pointmarkerlist = new int[numberofpoints]; if (pointmarkerlist == (int *) NULL) { + printf("Error: Out of memory.\n"); terminatetetgen(1); } } @@ -194,21 +393,23 @@ bool tetgenio::load_node_call(FILE* infile, int markers, char* infilename) /////////////////////////////////////////////////////////////////////////////// // // -// load_node() Load a list of points from a .node file. // +// load_node() Load a list of nodes from a .node file. // +// // +// 'filename' is the inputfile without suffix. The node list is in 'filename.// +// node'. On completion, the node list is returned in 'pointlist'. // // // /////////////////////////////////////////////////////////////////////////////// -bool tetgenio::load_node(char* filebasename) +bool tetgenio::load_node(char* filename) { FILE *infile; char innodefilename[FILENAMESIZE]; char inputline[INPUTLINESIZE]; char *stringptr; - bool okflag; int markers; // Assembling the actual file names we want to open. - strcpy(innodefilename, filebasename); + strcpy(innodefilename, filename); strcat(innodefilename, ".node"); // Try to open a .node file. @@ -220,7 +421,7 @@ bool tetgenio::load_node(char* filebasename) printf("Opening %s.\n", innodefilename); // Read the first line of the file. stringptr = readnumberline(inputline, infile, innodefilename); - // Does this file contain an index colume? + // Is this list of points generated from rbox? stringptr = strstr(inputline, "rbox"); if (stringptr == NULL) { // Read number of points, number of dimensions, number of point @@ -257,21 +458,141 @@ bool tetgenio::load_node(char* filebasename) useindex = 0; } + // if ((mesh_dim != 3) && (mesh_dim != 2)) { + // printf("Input error: TetGen only works for 2D & 3D point sets.\n"); + // fclose(infile); + // return false; + // } + if (numberofpoints < (mesh_dim + 1)) { + printf("Input error: TetGen needs at least %d points.\n", mesh_dim + 1); + fclose(infile); + return false; + } + // Load the list of nodes. - okflag = load_node_call(infile, markers, innodefilename); + if (!load_node_call(infile, markers, innodefilename)) { + fclose(infile); + return false; + } + fclose(infile); + return true; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// load_pbc() Load a list of pbc groups into 'pbcgrouplist'. // +// // +// 'filename' is the filename of the original inputfile without suffix. The // +// pbc groups are found in file 'filename.pbc'. // +// // +// This routine will be called both in load_poly() and load_tetmesh(). // +// // +/////////////////////////////////////////////////////////////////////////////// +bool tetgenio::load_pbc(char* filename) +{ + FILE *infile; + char pbcfilename[FILENAMESIZE]; + char inputline[INPUTLINESIZE]; + char *stringptr; + pbcgroup *pg; + int index, p1, p2; + int i, j, k; + + // Pbc groups are saved in file "filename.pbc". + strcpy(pbcfilename, filename); + strcat(pbcfilename, ".pbc"); + infile = fopen(pbcfilename, "r"); + if (infile != (FILE *) NULL) { + printf("Opening %s.\n", pbcfilename); + } else { + // No such file. Return. + return false; + } + + // Read the number of pbc groups. + stringptr = readnumberline(inputline, infile, pbcfilename); + numberofpbcgroups = (int) strtol (stringptr, &stringptr, 0); + if (numberofpbcgroups == 0) { + // It looks this file contains no point. + fclose(infile); + return false; + } + // Initialize 'pbcgrouplist'; + pbcgrouplist = new pbcgroup[numberofpbcgroups]; + + // Read the list of pbc groups. + for (i = 0; i < numberofpbcgroups; i++) { + pg = &(pbcgrouplist[i]); + // Initialize pbcgroup i; + pg->numberofpointpairs = 0; + pg->pointpairlist = (int *) NULL; + // Read 'fmark1', 'fmark2'. + stringptr = readnumberline(inputline, infile, pbcfilename); + if (*stringptr == '\0') break; + pg->fmark1 = (int) strtol(stringptr, &stringptr, 0); + stringptr = findnextnumber(stringptr); + if (*stringptr == '\0') break; + pg->fmark2 = (int) strtol(stringptr, &stringptr, 0); + // Read 'transmat'. + do { + stringptr = readline(inputline, infile, NULL); + } while ((*stringptr != '[') && (*stringptr != '\0')); + if (*stringptr == '\0') break; + for (j = 0; j < 4; j++) { + for (k = 0; k < 4; k++) { + // Read the entry of [j, k]. + stringptr = findnextnumber(stringptr); + if (*stringptr == '\0') { + // Try to read another line. + stringptr = readnumberline(inputline, infile, pbcfilename); + if (*stringptr == '\0') break; + } + pg->transmat[j][k] = (REAL) strtod(stringptr, &stringptr); + } + if (k < 4) break; // Not complete! + } + if (j < 4) break; // Not complete! + // Read 'numberofpointpairs'. + stringptr = readnumberline(inputline, infile, pbcfilename); + if (*stringptr == '\0') break; + pg->numberofpointpairs = (int) strtol(stringptr, &stringptr, 0); + if (pg->numberofpointpairs > 0) { + pg->pointpairlist = new int[pg->numberofpointpairs * 2]; + // Read the point pairs. + index = 0; + for (j = 0; j < pg->numberofpointpairs; j++) { + stringptr = readnumberline(inputline, infile, pbcfilename); + p1 = (int) strtol(stringptr, &stringptr, 0); + stringptr = findnextnumber(stringptr); + p2 = (int) strtol(stringptr, &stringptr, 0); + pg->pointpairlist[index++] = p1; + pg->pointpairlist[index++] = p2; + } + } + } fclose(infile); - return okflag; + + if (i < numberofpbcgroups) { + // Failed to read to additional points due to some error. + delete [] pbcgrouplist; + pbcgrouplist = (pbcgroup *) NULL; + numberofpbcgroups = 0; + return false; + } + return true; } /////////////////////////////////////////////////////////////////////////////// // // -// load_var() Load constraints applied on facets, segments, and nodes // -// from a .var file. // +// load_var() Load variant constraints applied on facets, segments, nodes.// +// // +// 'filename' is the filename of the original inputfile without suffix. The // +// constraints are found in file 'filename.var'. // // // /////////////////////////////////////////////////////////////////////////////// -bool tetgenio::load_var(char* filebasename) +bool tetgenio::load_var(char* filename) { FILE *infile; char varfilename[FILENAMESIZE]; @@ -281,13 +602,13 @@ bool tetgenio::load_var(char* filebasename) int i; // Variant constraints are saved in file "filename.var". - strcpy(varfilename, filebasename); + strcpy(varfilename, filename); strcat(varfilename, ".var"); infile = fopen(varfilename, "r"); if (infile != (FILE *) NULL) { printf("Opening %s.\n", varfilename); } else { - // No such file. Ignore it without a message. + // No such file. Return. return false; } @@ -379,11 +700,14 @@ bool tetgenio::load_var(char* filebasename) /////////////////////////////////////////////////////////////////////////////// // // -// load_mtr() Load a size specification map from a .mtr file. // +// load_mtr() Load a size specification map from file. // +// // +// 'filename' is the filename of the original inputfile without suffix. The // +// size map is found in file 'filename.mtr'. // // // /////////////////////////////////////////////////////////////////////////////// -bool tetgenio::load_mtr(char* filebasename) +bool tetgenio::load_mtr(char* filename) { FILE *infile; char mtrfilename[FILENAMESIZE]; @@ -393,7 +717,7 @@ bool tetgenio::load_mtr(char* filebasename) int mtrindex; int i, j; - strcpy(mtrfilename, filebasename); + strcpy(mtrfilename, filename); strcat(mtrfilename, ".mtr"); infile = fopen(mtrfilename, "r"); if (infile != (FILE *) NULL) { @@ -417,6 +741,7 @@ bool tetgenio::load_mtr(char* filebasename) // Allocate space for pointmtrlist. pointmtrlist = new REAL[numberofpoints * numberofpointmtrs]; if (pointmtrlist == (REAL *) NULL) { + printf("Error: Out of memory.\n"); terminatetetgen(1); } mtrindex = 0; @@ -441,11 +766,15 @@ bool tetgenio::load_mtr(char* filebasename) /////////////////////////////////////////////////////////////////////////////// // // -// load_poly() Load a PL complex from a .poly or a .smesh file. // +// load_poly() Load a piecewise linear complex from a .poly or .smesh. // +// // +// 'filename' is the inputfile without suffix. The PLC is in 'filename.poly' // +// or 'filename.smesh', and possibly plus 'filename.node' (when the first // +// line of the file starts with a zero). // // // /////////////////////////////////////////////////////////////////////////////// -bool tetgenio::load_poly(char* filebasename) +bool tetgenio::load_poly(char* filename) { FILE *infile, *polyfile; char innodefilename[FILENAMESIZE]; @@ -458,9 +787,9 @@ bool tetgenio::load_poly(char* filebasename) int i, j, k; // Assembling the actual file names we want to open. - strcpy(innodefilename, filebasename); - strcpy(inpolyfilename, filebasename); - strcpy(insmeshfilename, filebasename); + strcpy(innodefilename, filename); + strcpy(inpolyfilename, filename); + strcpy(insmeshfilename, filename); strcat(innodefilename, ".node"); strcat(inpolyfilename, ".poly"); strcat(insmeshfilename, ".smesh"); @@ -912,25 +1241,30 @@ bool tetgenio::load_poly(char* filebasename) fclose(polyfile); // Try to load a .var file if it exists. - load_var(filebasename); - + load_var(filename); // Try to load a .mtr file if it exists. - load_mtr(filebasename); + load_mtr(filename); + // Try to read a .pbc file if it exists. + load_pbc(filename); return true; } /////////////////////////////////////////////////////////////////////////////// // // -// load_off() Load a polyhedron from a .off file. // +// load_off() Load a polyhedron described in a .off file. // // // // The .off format is one of file formats of the Geomview, an interactive // // program for viewing and manipulating geometric objects. More information // // is available form: http://www.geomview.org. // // // +// 'filename' is a input filename with extension .off or without extension ( // +// the .off will be added in this case). On completion, the polyhedron is // +// returned in 'pointlist' and 'facetlist'. // +// // /////////////////////////////////////////////////////////////////////////////// -bool tetgenio::load_off(char* filebasename) +bool tetgenio::load_off(char* filename) { FILE *fp; tetgenio::facet *f; @@ -944,7 +1278,7 @@ bool tetgenio::load_off(char* filebasename) int nedges = 0; int line_count = 0, i; - strncpy(infilename, filebasename, 1024 - 1); + strncpy(infilename, filename, 1024 - 1); infilename[FILENAMESIZE - 1] = '\0'; if (infilename[0] == '\0') { printf("Error: No filename.\n"); @@ -1066,7 +1400,10 @@ bool tetgenio::load_off(char* filebasename) /////////////////////////////////////////////////////////////////////////////// // // -// load_ply() Load a polyhedron from a .ply file. // +// load_ply() Load a polyhedron described in a .ply file. // +// // +// 'filename' is the file name with extension .ply or without extension (the // +// .ply will be added in this case). // // // // This is a simplified version of reading .ply files, which only reads the // // set of vertices and the set of faces. Other informations (such as color, // @@ -1076,9 +1413,11 @@ bool tetgenio::load_off(char* filebasename) // format has exactly the same format for listing vertices and polygons as // // off file format. // // // +// On completion, 'pointlist' and 'facetlist' together return the polyhedron.// +// // /////////////////////////////////////////////////////////////////////////////// -bool tetgenio::load_ply(char* filebasename) +bool tetgenio::load_ply(char* filename) { FILE *fp; tetgenio::facet *f; @@ -1092,7 +1431,7 @@ bool tetgenio::load_ply(char* filebasename) int nfaces = 0, ifaces = 0; int line_count = 0, i; - strncpy(infilename, filebasename, FILENAMESIZE - 1); + strncpy(infilename, filename, FILENAMESIZE - 1); infilename[FILENAMESIZE - 1] = '\0'; if (infilename[0] == '\0') { printf("Error: No filename.\n"); @@ -1272,19 +1611,23 @@ bool tetgenio::load_ply(char* filebasename) /////////////////////////////////////////////////////////////////////////////// // // -// load_stl() Load a surface mesh from a .stl file. // +// load_stl() Load a surface mesh described in a .stl file. // +// // +// 'filename' is the file name with extension .stl or without extension (the // +// .stl will be added in this case). // // // // The .stl or stereolithography format is an ASCII or binary file used in // // manufacturing. It is a list of the triangular surfaces that describe a // // computer generated solid model. This is the standard input for most rapid // // prototyping machines. // // // -// Comment: A .stl file many contain many duplicated points. They will be // -// unified during the Delaunay tetrahedralization process. // +// On completion, 'pointlist' and 'facetlist' together return the polyhedron.// +// Note: After load_stl(), there exist many duplicated points in 'pointlist'.// +// They will be unified during the Delaunay tetrahedralization process. // // // /////////////////////////////////////////////////////////////////////////////// -bool tetgenio::load_stl(char* filebasename) +bool tetgenio::load_stl(char* filename) { FILE *fp; tetgenmesh::list *plist; @@ -1299,7 +1642,7 @@ bool tetgenio::load_stl(char* filebasename) int nfaces = 0; int line_count = 0, i; - strncpy(infilename, filebasename, FILENAMESIZE - 1); + strncpy(infilename, filename, FILENAMESIZE - 1); infilename[FILENAMESIZE - 1] = '\0'; if (infilename[0] == '\0') { printf("Error: No filename.\n"); @@ -1404,17 +1747,18 @@ bool tetgenio::load_stl(char* filebasename) /////////////////////////////////////////////////////////////////////////////// // // -// load_medit() Load a surface mesh from a .mesh file. // +// load_medit() Load a surface mesh described in .mesh file. // // // -// The .mesh format is the file format of Medit, a user-friendly interactive // -// mesh viewer program. // +// 'filename' is the file name with extension .mesh or without entension ( // +// the .mesh will be added in this case). .mesh is the file format of Medit, // +// a user-friendly interactive mesh viewing program. // // // // This routine ONLY reads the sections containing vertices, triangles, and // // quadrilaters. Other sections (such as tetrahedra, edges, ...) are ignored.// // // /////////////////////////////////////////////////////////////////////////////// -bool tetgenio::load_medit(char* filebasename) +bool tetgenio::load_medit(char* filename) { FILE *fp; tetgenio::facet *tmpflist, *f; @@ -1431,7 +1775,7 @@ bool tetgenio::load_medit(char* filebasename) int corners = 0; // 3 (triangle) or 4 (quad). int i, j; - strncpy(infilename, filebasename, FILENAMESIZE - 1); + strncpy(infilename, filename, FILENAMESIZE - 1); infilename[FILENAMESIZE - 1] = '\0'; if (infilename[0] == '\0') { printf("Error: No filename.\n"); @@ -1630,255 +1974,34 @@ bool tetgenio::load_medit(char* filebasename) /////////////////////////////////////////////////////////////////////////////// // // -// load_vtk() Load VTK surface mesh from file (.vtk ascii or binary). // -// // -// This function is contributed by: Bryn Lloyd, Computer Vision Laborator, // -// ETH, Zuerich. May 7, 2007. // -// // -/////////////////////////////////////////////////////////////////////////////// - -bool tetgenio::load_vtk(char* filebasename) -{ - FILE *fp; - tetgenio::facet *f; - tetgenio::polygon *p; - char infilename[FILENAMESIZE]; - char line[INPUTLINESIZE]; - char mode[128], id[256], fmt[64]; - char *bufferp; - double *coord; - float _x, _y, _z; - int nverts = 0; - int nfaces = 0; - int line_count = 0; - int dummy; - int id1, id2, id3; - int nn = -1; - int nn_old = -1; - int i, j; - bool ImALittleEndian = !testIsBigEndian(); - - strncpy(infilename, filebasename, FILENAMESIZE - 1); - infilename[FILENAMESIZE - 1] = '\0'; - if (infilename[0] == '\0') { - printf("Error: No filename.\n"); - return false; - } - if (strcmp(&infilename[strlen(infilename) - 4], ".vtk") != 0) { - strcat(infilename, ".vtk"); - } - if (!(fp = fopen(infilename, "r"))) { - printf("Error: Unable to open file %s\n", infilename); - return false; - } - printf("Opening %s.\n", infilename); - - // Default uses the index starts from '0'. - firstnumber = 0; - strcpy(mode, "BINARY"); - - while((bufferp = readline(line, fp, &line_count)) != NULL) { - if(strlen(line) == 0) continue; - //swallow lines beginning with a comment sign or white space - if(line[0] == '#' || line[0]=='\n' || line[0] == 10 || line[0] == 13 || - line[0] == 32) continue; - - sscanf(line, "%s", id); - if(!strcmp(id, "ASCII")) { - strcpy(mode, "ASCII"); - } - - if(!strcmp(id, "POINTS")) { - sscanf(line, "%s %d %s", id, &nverts, fmt); - if (nverts > 0) { - numberofpoints = nverts; - pointlist = new REAL[nverts * 3]; - } - - if(!strcmp(mode, "BINARY")) { - for(i = 0; i < nverts; i++) { - coord = &pointlist[i * 3]; - if(!strcmp(fmt, "double")) { - fread((char*)(&(coord[0])), sizeof(double), 1, fp); - fread((char*)(&(coord[1])), sizeof(double), 1, fp); - fread((char*)(&(coord[2])), sizeof(double), 1, fp); - if(ImALittleEndian){ - swapBytes((unsigned char *) &(coord[0]), sizeof(coord[0])); - swapBytes((unsigned char *) &(coord[1]), sizeof(coord[1])); - swapBytes((unsigned char *) &(coord[2]), sizeof(coord[2])); - } - } else if(!strcmp(fmt, "float")) { - fread((char*)(&_x), sizeof(float), 1, fp); - fread((char*)(&_y), sizeof(float), 1, fp); - fread((char*)(&_z), sizeof(float), 1, fp); - if(ImALittleEndian){ - swapBytes((unsigned char *) &_x, sizeof(_x)); - swapBytes((unsigned char *) &_y, sizeof(_y)); - swapBytes((unsigned char *) &_z, sizeof(_z)); - } - coord[0] = double(_x); - coord[1] = double(_y); - coord[2] = double(_z); - } else { - printf("Error: Only float or double formats are supported!\n"); - return false; - } - } - } else if(!strcmp(mode, "ASCII")) { - for(i = 0; i < nverts; i++){ - bufferp = readline(line, fp, &line_count); - if (bufferp == NULL) { - printf("Unexpected end of file on line %d in file %s\n", - line_count, infilename); - fclose(fp); - return false; - } - // Read vertex coordinates - coord = &pointlist[i * 3]; - for (j = 0; j < 3; j++) { - if (*bufferp == '\0') { - printf("Syntax error reading vertex coords on line"); - printf(" %d in file %s\n", line_count, infilename); - fclose(fp); - return false; - } - coord[j] = (REAL) strtod(bufferp, &bufferp); - bufferp = findnextnumber(bufferp); - } - } - } - continue; - } - - if(!strcmp(id, "POLYGONS")) { - sscanf(line, "%s %d %d", id, &nfaces, &dummy); - if (nfaces > 0) { - numberoffacets = nfaces; - facetlist = new tetgenio::facet[nfaces]; - } - - if(!strcmp(mode, "BINARY")) { - for(i = 0; i < nfaces; i++){ - fread((char*)(&nn), sizeof(int), 1, fp); - if(ImALittleEndian){ - swapBytes((unsigned char *) &nn, sizeof(nn)); - } - if (i == 0) - nn_old = nn; - if (nn != nn_old) { - printf("Error: No mixed cells are allowed.\n"); - return false; - } - - if(nn == 3){ - fread((char*)(&id1), sizeof(int), 1, fp); - fread((char*)(&id2), sizeof(int), 1, fp); - fread((char*)(&id3), sizeof(int), 1, fp); - if(ImALittleEndian){ - swapBytes((unsigned char *) &id1, sizeof(id1)); - swapBytes((unsigned char *) &id2, sizeof(id2)); - swapBytes((unsigned char *) &id3, sizeof(id3)); - } - f = &facetlist[i]; - init(f); - // In .off format, each facet has one polygon, no hole. - f->numberofpolygons = 1; - f->polygonlist = new tetgenio::polygon[1]; - p = &f->polygonlist[0]; - init(p); - // Set number of vertices - p->numberofvertices = 3; - // Allocate memory for face vertices - p->vertexlist = new int[p->numberofvertices]; - p->vertexlist[0] = id1; - p->vertexlist[1] = id2; - p->vertexlist[2] = id3; - } else { - printf("Error: Only triangles are supported\n"); - return false; - } - } - } else if(!strcmp(mode, "ASCII")) { - for(i = 0; i < nfaces; i++) { - bufferp = readline(line, fp, &line_count); - nn = (int) strtol(bufferp, &bufferp, 0); - if (i == 0) - nn_old = nn; - if (nn != nn_old) { - printf("Error: No mixed cells are allowed.\n"); - return false; - } - - if (nn == 3) { - bufferp = findnextnumber(bufferp); // Skip the first field. - id1 = (int) strtol(bufferp, &bufferp, 0); - bufferp = findnextnumber(bufferp); - id2 = (int) strtol(bufferp, &bufferp, 0); - bufferp = findnextnumber(bufferp); - id3 = (int) strtol(bufferp, &bufferp, 0); - f = &facetlist[i]; - init(f); - // In .off format, each facet has one polygon, no hole. - f->numberofpolygons = 1; - f->polygonlist = new tetgenio::polygon[1]; - p = &f->polygonlist[0]; - init(p); - // Set number of vertices - p->numberofvertices = 3; - // Allocate memory for face vertices - p->vertexlist = new int[p->numberofvertices]; - p->vertexlist[0] = id1; - p->vertexlist[1] = id2; - p->vertexlist[2] = id3; - } else { - printf("Error: Only triangles are supported.\n"); - return false; - } - } - } - - fclose(fp); - return true; - } - - if(!strcmp(id,"LINES") || !strcmp(id,"CELLS")){ - printf("Warning: load_vtk(): cannot read formats LINES, CELLS.\n"); - } - } // while () - - return true; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// load_plc() Load a piecewise linear complex from file(s). // +// load_plc() Load a piecewise linear complex from file. // // // -// 'object' indicates which file format is used to describ the plc. // +// This is main entrance for loading plcs from different file formats into // +// tetgenio. 'filename' is the input file name without extention. 'object' // +// indicates which file format is used to describ the plc. // // // /////////////////////////////////////////////////////////////////////////////// -bool tetgenio::load_plc(char* filebasename, int object) +bool tetgenio::load_plc(char* filename, int object) { enum tetgenbehavior::objecttype type; type = (enum tetgenbehavior::objecttype) object; switch (type) { case tetgenbehavior::NODES: - return load_node(filebasename); + return load_node(filename); case tetgenbehavior::POLY: - return load_poly(filebasename); + return load_poly(filename); case tetgenbehavior::OFF: - return load_off(filebasename); + return load_off(filename); case tetgenbehavior::PLY: - return load_ply(filebasename); + return load_ply(filename); case tetgenbehavior::STL: - return load_stl(filebasename); + return load_stl(filename); case tetgenbehavior::MEDIT: - return load_medit(filebasename); - case tetgenbehavior::VTK: - return load_vtk(filebasename); + return load_medit(filename); default: - return load_poly(filebasename); + return load_poly(filename); } } @@ -1886,9 +2009,13 @@ bool tetgenio::load_plc(char* filebasename, int object) // // // load_tetmesh() Load a tetrahedral mesh from files. // // // +// 'filename' is the inputfile without suffix. The nodes of the tetrahedral // +// mesh is in "filename.node", the elements is in "filename.ele", if the // +// "filename.face" and "filename.vol" exists, they will also be read. // +// // /////////////////////////////////////////////////////////////////////////////// -bool tetgenio::load_tetmesh(char* filebasename) +bool tetgenio::load_tetmesh(char* filename) { FILE *infile; char innodefilename[FILENAMESIZE]; @@ -1905,11 +2032,11 @@ bool tetgenio::load_tetmesh(char* filebasename) int i, j; // Assembling the actual file names we want to open. - strcpy(innodefilename, filebasename); - strcpy(inelefilename, filebasename); - strcpy(infacefilename, filebasename); - strcpy(inedgefilename, filebasename); - strcpy(involfilename, filebasename); + strcpy(innodefilename, filename); + strcpy(inelefilename, filename); + strcpy(infacefilename, filename); + strcpy(inedgefilename, filename); + strcpy(involfilename, filename); strcat(innodefilename, ".node"); strcat(inelefilename, ".ele"); strcat(infacefilename, ".face"); @@ -2002,6 +2129,7 @@ bool tetgenio::load_tetmesh(char* filebasename) if (numberoftetrahedra > 0) { tetrahedronlist = new int[numberoftetrahedra * numberofcorners]; if (tetrahedronlist == (int *) NULL) { + printf("Error: Out of memory.\n"); terminatetetgen(1); } // Allocate memory for output tetrahedron attributes if necessary. @@ -2009,6 +2137,7 @@ bool tetgenio::load_tetmesh(char* filebasename) tetrahedronattributelist = new REAL[numberoftetrahedra * numberoftetrahedronattributes]; if (tetrahedronattributelist == (REAL *) NULL) { + printf("Error: Out of memory.\n"); terminatetetgen(1); } } @@ -2074,11 +2203,13 @@ bool tetgenio::load_tetmesh(char* filebasename) if (numberoftrifaces > 0) { trifacelist = new int[numberoftrifaces * 3]; if (trifacelist == (int *) NULL) { + printf("Error: Out of memory.\n"); terminatetetgen(1); } if (markers) { - trifacemarkerlist = new int[numberoftrifaces]; + trifacemarkerlist = new int[numberoftrifaces * 3]; if (trifacemarkerlist == (int *) NULL) { + printf("Error: Out of memory.\n"); terminatetetgen(1); } } @@ -2128,17 +2259,9 @@ bool tetgenio::load_tetmesh(char* filebasename) if (numberofedges > 0) { edgelist = new int[numberofedges * 2]; if (edgelist == (int *) NULL) { + printf("Error: Out of memory.\n"); terminatetetgen(1); } - stringptr = findnextnumber(stringptr); - if (*stringptr == '\0') { - markers = 0; // Default value. - } else { - markers = (int) strtol (stringptr, &stringptr, 0); - } - if (markers > 0) { - edgemarkerlist = new int[numberofedges]; - } } // Read the list of faces. index = 0; @@ -2160,11 +2283,6 @@ bool tetgenio::load_tetmesh(char* filebasename) } edgelist[index++] = corner; } - // Read the edge marker if it has. - if (markers) { - stringptr = findnextnumber(stringptr); - edgemarkerlist[i] = (int) strtol(stringptr, &stringptr, 0); - } } fclose(infile); } @@ -2185,6 +2303,7 @@ bool tetgenio::load_tetmesh(char* filebasename) if (volelements > 0) { tetrahedronvolumelist = new REAL[volelements]; if (tetrahedronvolumelist == (REAL *) NULL) { + printf("Error: Out of memory.\n"); terminatetetgen(1); } } @@ -2203,36 +2322,197 @@ bool tetgenio::load_tetmesh(char* filebasename) } // Try to load a .mtr file if it exists. - load_mtr(filebasename); - + load_mtr(filename); // Try to read a .pbc file if it exists. - // load_pbc(filebasename); + load_pbc(filename); return true; } /////////////////////////////////////////////////////////////////////////////// // // -// save_nodes() Save points to a .node file. // +// load_voronoi() Load a Voronoi diagram from files. // +// // +// 'filename' is the inputfile without suffix. The Voronoi diagram is read // +// from files: filename.v.node, filename.v.edge, and filename.v.face. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenio::save_nodes(char* filebasename) +bool tetgenio::load_voronoi(char* filename) { - FILE *fout; - char outnodefilename[FILENAMESIZE]; - char outmtrfilename[FILENAMESIZE]; + FILE *infile; + char innodefilename[FILENAMESIZE]; + char inedgefilename[FILENAMESIZE]; + char inputline[INPUTLINESIZE]; + char *stringptr, *infilename; + voroedge *vedge; + REAL x, y, z; + int firstnode, corner; + int index; int i, j; - sprintf(outnodefilename, "%s.node", filebasename); - printf("Saving nodes to %s\n", outnodefilename); - fout = fopen(outnodefilename, "w"); - fprintf(fout, "%d %d %d %d\n", numberofpoints, mesh_dim, - numberofpointattributes, pointmarkerlist != NULL ? 1 : 0); + // Assembling the actual file names we want to open. + strcpy(innodefilename, filename); + strcpy(inedgefilename, filename); + strcat(innodefilename, ".v.node"); + strcat(inedgefilename, ".v.edge"); + + // Read the points from a .v.node file. + infilename = innodefilename; + printf("Opening %s.\n", infilename); + infile = fopen(infilename, "r"); + if (infile == (FILE *) NULL) { + printf("File I/O Error: Cannot access file %s.\n", infilename); + return false; + } + // Read the first line of the file. + stringptr = readnumberline(inputline, infile, infilename); + // Is this list of points generated from rbox? + stringptr = strstr(inputline, "rbox"); + if (stringptr == NULL) { + // Read number of points, number of dimensions, number of point + // attributes, and number of boundary markers. + stringptr = inputline; + numberofvpoints = (int) strtol (stringptr, &stringptr, 0); + stringptr = findnextnumber(stringptr); + if (*stringptr == '\0') { + mesh_dim = 3; // Default. + } else { + mesh_dim = (int) strtol (stringptr, &stringptr, 0); + } + useindex = 1; // There is an index column. + } else { + // It is a rbox (qhull) input file. + stringptr = inputline; + // Get the dimension. + mesh_dim = (int) strtol (stringptr, &stringptr, 0); + // Get the number of points. + stringptr = readnumberline(inputline, infile, infilename); + numberofvpoints = (int) strtol (stringptr, &stringptr, 0); + useindex = 0; // No index column. + } + // Initialize 'vpointlist'. + vpointlist = new REAL[numberofvpoints * 3]; + if (vpointlist == (REAL *) NULL) { + printf("Error: Out of memory.\n"); + terminatetetgen(1); + } + // Read the point section. + index = 0; + for (i = 0; i < numberofvpoints; i++) { + stringptr = readnumberline(inputline, infile, infilename); + if (useindex) { + if (i == 0) { + firstnode = (int) strtol (stringptr, &stringptr, 0); + if ((firstnode == 0) || (firstnode == 1)) { + firstnumber = firstnode; + } + } + stringptr = findnextnumber(stringptr); + } // if (useindex) + if (*stringptr == '\0') { + printf("Error: Point %d has no x coordinate.\n", firstnumber + i); + terminatetetgen(1); + } + x = (REAL) strtod(stringptr, &stringptr); + stringptr = findnextnumber(stringptr); + if (*stringptr == '\0') { + printf("Error: Point %d has no y coordinate.\n", firstnumber + i); + terminatetetgen(1); + } + y = (REAL) strtod(stringptr, &stringptr); + if (mesh_dim == 3) { + stringptr = findnextnumber(stringptr); + if (*stringptr == '\0') { + printf("Error: Point %d has no z coordinate.\n", firstnumber + i); + terminatetetgen(1); + } + z = (REAL) strtod(stringptr, &stringptr); + } else { + z = 0.0; // mesh_dim == 2; + } + vpointlist[index++] = x; + vpointlist[index++] = y; + vpointlist[index++] = z; + } + fclose(infile); + + // Read the Voronoi edges from a .v.edge file if it exists. + infilename = inedgefilename; + infile = fopen(infilename, "r"); + if (infile != (FILE *) NULL) { + printf("Opening %s.\n", infilename); + // Read number of boundary edges. + stringptr = readnumberline(inputline, infile, infilename); + numberofvedges = (int) strtol (stringptr, &stringptr, 0); + if (numberofvedges > 0) { + vedgelist = new voroedge[numberofvedges]; + } + // Read the list of faces. + index = 0; + for (i = 0; i < numberofvedges; i++) { + // Read edge index and the edge's two endpoints. + stringptr = readnumberline(inputline, infile, infilename); + vedge = &(vedgelist[i]); + for (j = 0; j < 2; j++) { + stringptr = findnextnumber(stringptr); + if (*stringptr == '\0') { + printf("Error: Edge %d is missing vertex %d in %s.\n", + i + firstnumber, j + 1, infilename); + terminatetetgen(1); + } + corner = (int) strtol(stringptr, &stringptr, 0); + j == 0 ? vedge->v1 = corner : vedge->v2 = corner; + } + if (vedge->v2 < 0) { + for (j = 0; j < mesh_dim; j++) { + stringptr = findnextnumber(stringptr); + if (*stringptr == '\0') { + printf("Error: Edge %d is missing normal in %s.\n", + i + firstnumber, infilename); + terminatetetgen(1); + } + vedge->vnormal[j] = (REAL) strtod(stringptr, &stringptr); + } + if (mesh_dim == 2) { + vedge->vnormal[2] = 0.0; + } + } else { + vedge->vnormal[0] = 0.0; + vedge->vnormal[1] = 0.0; + vedge->vnormal[2] = 0.0; + } + } + fclose(infile); + } + + return true; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// save_nodes() Save points to a .node file. // +// // +// 'filename' is a string containing the file name without suffix. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenio::save_nodes(char* filename) +{ + FILE *fout; + char outnodefilename[FILENAMESIZE]; + char outmtrfilename[FILENAMESIZE]; + int i, j; + + sprintf(outnodefilename, "%s.node", filename); + printf("Saving nodes to %s\n", outnodefilename); + fout = fopen(outnodefilename, "w"); + fprintf(fout, "%d %d %d %d\n", numberofpoints, mesh_dim, + numberofpointattributes, pointmarkerlist != NULL ? 1 : 0); for (i = 0; i < numberofpoints; i++) { if (mesh_dim == 2) { - fprintf(fout, "%d %.16g %.16g", i + firstnumber, pointlist[i * 3], - pointlist[i * 3 + 1]); + fprintf(fout, "%d %.16g %.16g", i + firstnumber, pointlist[i * 2], + pointlist[i * 2 + 1]); } else { fprintf(fout, "%d %.16g %.16g %.16g", i + firstnumber, pointlist[i * 3], pointlist[i * 3 + 1], pointlist[i * 3 + 2]); @@ -2250,7 +2530,7 @@ void tetgenio::save_nodes(char* filebasename) // If the point metrics exist, output them to a .mtr file. if ((numberofpointmtrs > 0) && (pointmtrlist != (REAL *) NULL)) { - sprintf(outmtrfilename, "%s.mtr", filebasename); + sprintf(outmtrfilename, "%s.mtr", filename); printf("Saving metrics to %s\n", outmtrfilename); fout = fopen(outmtrfilename, "w"); fprintf(fout, "%d %d\n", numberofpoints, numberofpointmtrs); @@ -2268,44 +2548,31 @@ void tetgenio::save_nodes(char* filebasename) // // // save_elements() Save elements to a .ele file. // // // +// 'filename' is a string containing the file name without suffix. // +// // /////////////////////////////////////////////////////////////////////////////// -void tetgenio::save_elements(char* filebasename) +void tetgenio::save_elements(char* filename) { FILE *fout; char outelefilename[FILENAMESIZE]; int i, j; - sprintf(outelefilename, "%s.ele", filebasename); + sprintf(outelefilename, "%s.ele", filename); printf("Saving elements to %s\n", outelefilename); fout = fopen(outelefilename, "w"); - if (mesh_dim == 3) { - fprintf(fout, "%d %d %d\n", numberoftetrahedra, numberofcorners, - numberoftetrahedronattributes); - for (i = 0; i < numberoftetrahedra; i++) { - fprintf(fout, "%d", i + firstnumber); - for (j = 0; j < numberofcorners; j++) { - fprintf(fout, " %5d", tetrahedronlist[i * numberofcorners + j]); - } - for (j = 0; j < numberoftetrahedronattributes; j++) { - fprintf(fout, " %g", - tetrahedronattributelist[i * numberoftetrahedronattributes + j]); - } - fprintf(fout, "\n"); + fprintf(fout, "%d %d %d\n", numberoftetrahedra, numberofcorners, + numberoftetrahedronattributes); + for (i = 0; i < numberoftetrahedra; i++) { + fprintf(fout, "%d", i + firstnumber); + for (j = 0; j < numberofcorners; j++) { + fprintf(fout, " %5d", tetrahedronlist[i * numberofcorners + j]); } - } else { - // Save a two-dimensional mesh. - fprintf(fout, "%d %d %d\n",numberoftrifaces,3,trifacemarkerlist ? 1 : 0); - for (i = 0; i < numberoftrifaces; i++) { - fprintf(fout, "%d", i + firstnumber); - for (j = 0; j < 3; j++) { - fprintf(fout, " %5d", trifacelist[i * 3 + j]); - } - if (trifacemarkerlist != NULL) { - fprintf(fout, " %d", trifacemarkerlist[i]); - } - fprintf(fout, "\n"); + for (j = 0; j < numberoftetrahedronattributes; j++) { + fprintf(fout, " %g", + tetrahedronattributelist[i * numberoftetrahedronattributes + j]); } + fprintf(fout, "\n"); } fclose(fout); @@ -2315,15 +2582,17 @@ void tetgenio::save_elements(char* filebasename) // // // save_faces() Save faces to a .face file. // // // +// 'filename' is a string containing the file name without suffix. // +// // /////////////////////////////////////////////////////////////////////////////// -void tetgenio::save_faces(char* filebasename) +void tetgenio::save_faces(char* filename) { FILE *fout; char outfacefilename[FILENAMESIZE]; int i; - sprintf(outfacefilename, "%s.face", filebasename); + sprintf(outfacefilename, "%s.face", filename); printf("Saving faces to %s\n", outfacefilename); fout = fopen(outfacefilename, "w"); fprintf(fout, "%d %d\n", numberoftrifaces, @@ -2344,15 +2613,17 @@ void tetgenio::save_faces(char* filebasename) // // // save_edges() Save egdes to a .edge file. // // // +// 'filename' is a string containing the file name without suffix. // +// // /////////////////////////////////////////////////////////////////////////////// -void tetgenio::save_edges(char* filebasename) +void tetgenio::save_edges(char* filename) { FILE *fout; char outedgefilename[FILENAMESIZE]; int i; - sprintf(outedgefilename, "%s.edge", filebasename); + sprintf(outedgefilename, "%s.edge", filename); printf("Saving edges to %s\n", outedgefilename); fout = fopen(outedgefilename, "w"); fprintf(fout, "%d %d\n", numberofedges, edgemarkerlist != NULL ? 1 : 0); @@ -2372,15 +2643,17 @@ void tetgenio::save_edges(char* filebasename) // // // save_neighbors() Save egdes to a .neigh file. // // // +// 'filename' is a string containing the file name without suffix. // +// // /////////////////////////////////////////////////////////////////////////////// -void tetgenio::save_neighbors(char* filebasename) +void tetgenio::save_neighbors(char* filename) { FILE *fout; char outneighborfilename[FILENAMESIZE]; int i; - sprintf(outneighborfilename, "%s.neigh", filebasename); + sprintf(outneighborfilename, "%s.neigh", filename); printf("Saving neighbors to %s\n", outneighborfilename); fout = fopen(outneighborfilename, "w"); fprintf(fout, "%d %d\n", numberoftetrahedra, mesh_dim + 1); @@ -2403,11 +2676,13 @@ void tetgenio::save_neighbors(char* filebasename) // // // save_poly() Save segments or facets to a .poly file. // // // -// It only save the facets, holes and regions. No .node file is saved. // +// 'filename' is a string containing the file name without suffix. It only // +// save the facets, holes and regions. The nodes are saved in a .node file // +// by routine save_nodes(). // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenio::save_poly(char* filebasename) +void tetgenio::save_poly(char* filename) { FILE *fout; facet *f; @@ -2415,7 +2690,7 @@ void tetgenio::save_poly(char* filebasename) char outpolyfilename[FILENAMESIZE]; int i, j, k; - sprintf(outpolyfilename, "%s.poly", filebasename); + sprintf(outpolyfilename, "%s.poly", filename); printf("Saving poly to %s\n", outpolyfilename); fout = fopen(outpolyfilename, "w"); @@ -2626,44 +2901,133 @@ char* tetgenio::findnextnumber(char *string) return result; } -//// //// -//// //// -//// io_cxx /////////////////////////////////////////////////////////////////// - -//// behavior_cxx ///////////////////////////////////////////////////////////// -//// //// -//// //// +// +// End of class 'tetgenio' implementation +// + +static REAL PI = 3.14159265358979323846264338327950288419716939937510582; + +// +// Begin of class 'tetgenbehavior' implementation +// + +/////////////////////////////////////////////////////////////////////////////// +// // +// tetgenbehavior() Initialize veriables of 'tetgenbehavior'. // +// // +/////////////////////////////////////////////////////////////////////////////// + +tetgenbehavior::tetgenbehavior() +{ + // Initialize command line switches. + plc = 0; + quality = 0; + refine = 0; + coarse = 0; + metric = 0; + minratio = 2.0; + goodratio = 0.0; + minangle = 20.0; + goodangle = 0.0; + maxdihedral = 165.0; + mindihedral = 5.0; + varvolume = 0; + fixedvolume = 0; + maxvolume = -1.0; + regionattrib = 0; + insertaddpoints = 0; + diagnose = 0; + offcenter = 0; + conformdel = 0; + alpha1 = sqrt(2.0); + alpha2 = 1.0; + alpha3 = 0.6; + zeroindex = 0; + facesout = 0; + edgesout = 0; + neighout = 0; + voroout = 0; + meditview = 0; + gidview = 0; + geomview = 0; + optlevel = 3; + optpasses = 3; + order = 1; + nojettison = 0; + nobound = 0; + nonodewritten = 0; + noelewritten = 0; + nofacewritten = 0; + noiterationnum = 0; + nobisect = 0; + noflip = 0; + steiner = -1; + fliprepair = 1; + nomerge = 0; + docheck = 0; + quiet = 0; + verbose = 0; + useshelles = 0; + epsilon = 1.0e-8; + epsilon2 = 1.0e-5; + object = NONE; + // Initialize strings + commandline[0] = '\0'; + infilename[0] = '\0'; + outfilename[0] = '\0'; + addinfilename[0] = '\0'; + bgmeshfilename[0] = '\0'; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// versioninfo() Print the version information of TetGen. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenbehavior::versioninfo() +{ + printf("Version 1.4.2 (April 16, 2007).\n"); + printf("\n"); + printf("Copyright (C) 2002 - 2007\n"); + printf("Hang Si\n"); + printf("Mohrenstr. 39, 10117 Berlin, Germany\n"); + printf("si@wias-berlin.de\n"); +} /////////////////////////////////////////////////////////////////////////////// // // -// syntax() Print list of command line switches. // +// syntax() Print list of command line switches and exit the program. // // // /////////////////////////////////////////////////////////////////////////////// void tetgenbehavior::syntax() { - printf(" tetgen [-prq_a_AiMYS_T_dzo_fenvgGOJBNEFICQVh] input_file\n"); + printf(" tetgen [-prq_Ra_AiMYS_T_dzo_fenvgGOJBNEFICQVh] input_file\n"); printf(" -p Tetrahedralizes a piecewise linear complex (PLC).\n"); printf(" -r Reconstructs a previously generated mesh.\n"); - printf(" -q Refines mesh (to improve mesh quality).\n"); + printf(" -q Quality mesh generation (adding new mesh points to "); + printf("improve mesh quality).\n"); + printf(" -R Mesh coarsening (deleting redundant mesh points).\n"); printf(" -a Applies a maximum tetrahedron volume constraint.\n"); - printf(" -A Assigns attributes to tetrahedra in different regions.\n"); + printf(" -A Assigns attributes to identify tetrahedra in different "); + printf("regions.\n"); printf(" -i Inserts a list of additional points into mesh.\n"); - printf(" -M No merge of coplanar facets.\n"); - printf(" -Y No splitting of input boundaries (facets and segments).\n"); + printf(" -M Does not merge coplanar facets.\n"); + printf(" -Y Suppresses boundary facets/segments splitting.\n"); printf(" -S Specifies maximum number of added points.\n"); printf(" -T Sets a tolerance for coplanar test (default 1e-8).\n"); printf(" -d Detects self-intersections of facets of the PLC.\n"); printf(" -z Numbers all output items starting from zero.\n"); printf(" -o2 Generates second-order subparametric elements.\n"); - printf(" -f Outputs all faces to .face file.\n"); + printf(" -f Outputs all faces to .face file."); + printf("file.\n"); printf(" -e Outputs all edges to .edge file.\n"); 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(" -G Outputs mesh to .msh file for viewing by Gid.\n"); printf(" -O Outputs mesh to .off file for viewing by Geomview.\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"); @@ -2687,13 +3051,7 @@ void tetgenbehavior::usage() printf("TetGen\n"); printf("A Quality Tetrahedral Mesh Generator and 3D Delaunay "); printf("Triangulator\n"); - //versioninfo(); - printf("Version 1.4.3 (September 6, December 13, 2009).\n"); - printf("\n"); - printf("Copyright (C) 2002 - 2009\n"); - printf("Hang Si\n"); - printf("Mohrenstr. 39, 10117 Berlin, Germany\n"); - printf("si@wias-berlin.de\n"); + versioninfo(); printf("\n"); printf("What Can TetGen Do?\n"); printf("\n"); @@ -2737,7 +3095,6 @@ void tetgenbehavior::usage() printf("object.1.node, object.1.ele\n and object.1.face.\n"); printf("\n"); printf("Please send bugs/comments to Hang Si \n"); - terminatetetgen(0); } /////////////////////////////////////////////////////////////////////////////// @@ -2805,7 +3162,7 @@ bool tetgenbehavior::parse_commandline(int argc, char **argv) if (argv[i][j] == 'p') { plc = 1; } else if (argv[i][j] == 'r') { - refine++; + refine = 1; } else if (argv[i][j] == 'R') { coarse = 1; } else if (argv[i][j] == 'q') { @@ -2826,6 +3183,10 @@ bool tetgenbehavior::parse_commandline(int argc, char **argv) mindihedral = (REAL) strtod(workstring, (char **) NULL); } else if (quality == 3) { maxdihedral = (REAL) strtod(workstring, (char **) NULL); + } else if (quality == 4) { + alpha2 = (REAL) strtod(workstring, (char **) NULL); + } else if (quality == 5) { + alpha1 = (REAL) strtod(workstring, (char **) NULL); } } } else if (argv[i][j] == 'm') { @@ -2865,23 +3226,6 @@ bool tetgenbehavior::parse_commandline(int argc, char **argv) } } else if (argv[i][j] == 'A') { regionattrib++; - } else if (argv[i][j] == 'u') { - // Set the maximum btree node size, -u0 means do not use btree. - if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || - (argv[i][j + 1] == '.')) { - k = 0; - while (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || - (argv[i][j + 1] == '.')) { - j++; - workstring[k] = argv[i][j]; - k++; - } - workstring[k] = '\0'; - max_btreenode_size = (int) strtol(workstring, (char **) NULL, 0); - } - if (max_btreenode_size == 0) { - btree = 0; - } } else if (argv[i][j] == 'i') { insertaddpoints = 1; } else if (argv[i][j] == 'd') { @@ -2902,8 +3246,6 @@ bool tetgenbehavior::parse_commandline(int argc, char **argv) gidview = 1; } else if (argv[i][j] == 'O') { geomview = 1; - } else if (argv[i][j] == 'K') { - vtkview = 1; } else if (argv[i][j] == 'M') { nomerge = 1; } else if (argv[i][j] == 'Y') { @@ -2916,10 +3258,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') { @@ -2986,9 +3324,13 @@ bool tetgenbehavior::parse_commandline(int argc, char **argv) quiet = 1; } else if (argv[i][j] == 'V') { verbose++; + // } else if (argv[i][j] == 'v') { + // versioninfo(); + // terminatetetgen(0); } else if ((argv[i][j] == 'h') || (argv[i][j] == 'H') || (argv[i][j] == '?')) { usage(); + terminatetetgen(0); } else { printf("Warning: Unknown switch -%c.\n", argv[i][j]); } @@ -3032,10 +3374,6 @@ bool tetgenbehavior::parse_commandline(int argc, char **argv) infilename[strlen(infilename) - 5] = '\0'; object = MEDIT; plc = 1; - } else if (!strcmp(&infilename[strlen(infilename) - 4], ".vtk")) { - infilename[strlen(infilename) - 4] = '\0'; - object = VTK; - plc = 1; } else if (!strcmp(&infilename[strlen(infilename) - 4], ".ele")) { infilename[strlen(infilename) - 4] = '\0'; object = MESH; @@ -3081,7 +3419,7 @@ bool tetgenbehavior::parse_commandline(int argc, char **argv) } } // Calculate the goodangle for testing bad subfaces. - goodangle = cos(minangle * tetgenmesh::PI / 180.0); + goodangle = cos(minangle * PI / 180.0); goodangle *= goodangle; increment = 0; @@ -3126,4104 +3464,2734 @@ bool tetgenbehavior::parse_commandline(int argc, char **argv) return true; } -//// //// -//// //// -//// behavior_cxx ///////////////////////////////////////////////////////////// - -//// prim_cxx ///////////////////////////////////////////////////////////////// -//// //// -//// //// - -// For enumerating three edges of a triangle. - -int tetgenmesh::plus1mod3[3] = {1, 2, 0}; -int tetgenmesh::minus1mod3[3] = {2, 0, 1}; - -// Table 've' takes an edge version as input, returns the next edge version -// in the same edge ring. - -int tetgenmesh::ve[6] = { 2, 5, 4, 1, 0, 3 }; - -// Tables 'vo', 'vd' and 'va' take an edge version, return the positions of -// the origin, destination and apex in the triangle. - -int tetgenmesh::vo[6] = { 0, 1, 1, 2, 2, 0 }; -int tetgenmesh::vd[6] = { 1, 0, 2, 1, 0, 2 }; -int tetgenmesh::va[6] = { 2, 2, 0, 0, 1, 1 }; - -// The following tables are for tetrahedron primitives (operate on trifaces). - -// For 'org()', 'dest()' and 'apex()'. Use 'loc' as the first index and -// 'ver' as the second index. - -int tetgenmesh::locver2org[4][6] = { - {0, 1, 1, 2, 2, 0}, - {0, 3, 3, 1, 1, 0}, - {1, 3, 3, 2, 2, 1}, - {2, 3, 3, 0, 0, 2} -}; -int tetgenmesh::locver2dest[4][6] = { - {1, 0, 2, 1, 0, 2}, - {3, 0, 1, 3, 0, 1}, - {3, 1, 2, 3, 1, 2}, - {3, 2, 0, 3, 2, 0} -}; -int tetgenmesh::locver2apex[4][6] = { - {2, 2, 0, 0, 1, 1}, - {1, 1, 0, 0, 3, 3}, - {2, 2, 1, 1, 3, 3}, - {0, 0, 2, 2, 3, 3} -}; - -// For oppo() primitives, use 'loc' as the index. - -int tetgenmesh::loc2oppo[4] = { 3, 2, 0, 1 }; - -// For fnext() primitive. Use 'loc' as the first index and 'ver' as the -// second index. Returns a new 'loc' and new 'ver' in an array. (It is -// only valid for edge version equals one of {0, 2, 4}.) +// +// End of class 'tetgenbehavior' implementation +// -int tetgenmesh::locver2nextf[4][6][2] = { - { {1, 5}, {-1, -1}, {2, 5}, {-1, -1}, {3, 5}, {-1, -1} }, - { {3, 3}, {-1, -1}, {2, 1}, {-1, -1}, {0, 1}, {-1, -1} }, - { {1, 3}, {-1, -1}, {3, 1}, {-1, -1}, {0, 3}, {-1, -1} }, - { {2, 3}, {-1, -1}, {1, 1}, {-1, -1}, {0, 5}, {-1, -1} } -}; +// +// Begin of class 'tetgenmesh' implementation +// -// The edge number (from 0 to 5) of a tet is defined as follows: -// 0 - (v0, v1), 1 - (v1, v2), 2 - (v2, v0) -// 3 - (v3, v0), 4 - (v3, v1), 5 - (v3, v2). +// +// Begin of class 'list', 'memorypool' and 'link' implementation +// -int tetgenmesh::locver2edge[4][6] = { - {0, 0, 1, 1, 2, 2}, - {3, 3, 4, 4, 0, 0}, - {4, 4, 5, 5, 1, 1}, - {5, 5, 3, 3, 2, 2} -}; +// Following are predefined compare functions for primitive data types. +// These functions take two pointers of the corresponding date type, +// perform the comparation. Return -1, 0 or 1 indicating the default +// linear order of two operators. -int tetgenmesh::edge2locver[6][2] = { - {0, 0}, // 0 v0 -> v1 (a -> b) - {0, 2}, // 1 v1 -> v2 (b -> c) - {0, 4}, // 2 v2 -> v0 (c -> a) - {1, 0}, // 3 v0 -> v3 (a -> d) - {1, 2}, // 4 v1 -> v3 (b -> d - {2, 2} // 5 v2 -> v3 (c -> d) -}; +// Compare two 'integers'. +int tetgenmesh::compare_2_ints(const void* x, const void* y) { + if (* (int *) x < * (int *) y) { + return -1; + } else if (* (int *) x > * (int *) y) { + return 1; + } else { + return 0; + } +} -int tetgenmesh::locpivot[4][3] = { - {1, 2, 3}, - {0, 2, 3}, - {0, 1, 3}, - {0, 1, 2} -}; +// Compare two 'longs'. Note: in 64-bit machine the 'long' type is 64-bit +// (8-byte) where the 'int' only 32-bit (4-byte). +int tetgenmesh::compare_2_longs(const void* x, const void* y) { + if (* (long *) x < * (long *) y) { + return -1; + } else if (* (long *) x > * (long *) y) { + return 1; + } else { + return 0; + } +} -int tetgenmesh::locverpivot[4][6][2] = { - {{2, 3}, {2, 3}, {1, 3}, {1, 3}, {1, 2}, {1, 2}}, - {{0, 2}, {0, 2}, {0, 3}, {0, 3}, {2, 3}, {2, 3}}, - {{0, 3}, {0, 3}, {0, 1}, {0, 1}, {1, 3}, {1, 3}}, - {{0, 1}, {0, 1}, {0, 2}, {0, 2}, {1, 2}, {1, 2}} -}; +// Compare two 'unsigned longs'. +int tetgenmesh::compare_2_unsignedlongs(const void* x, const void* y) { + if (* (unsigned long *) x < * (unsigned long *) y) { + return -1; + } else if (* (unsigned long *) x > * (unsigned long *) y) { + return 1; + } else { + return 0; + } +} /////////////////////////////////////////////////////////////////////////////// // // -// getnextsface() Finds the next subface in the face ring. // +// set_compfunc() Determine the size of primitive data types and set the // +// corresponding predefined linear order functions. // // // -// For saving space in the data structure of subface, there only exists one // -// face ring around a segment (see programming manual). This routine imple- // -// ments the double face ring as desired in Muecke's data structure. // +// 'str' is a zero-end string indicating a primitive data type, like 'int', // +// 'long' or 'unsigned long'. Every string ending with a '*' is though as a // +// type of pointer and the type 'unsign long' is used for it. // +// // +// When the type of 'str' is determined, the size of this type (in byte) is // +// returned in 'itbytes', and the pointer of corresponding predefined linear // +// order functions is returned in 'pcomp'. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::getnextsface(face* s1, face* s2) +void tetgenmesh::set_compfunc(char* str, int* itbytes, compfunc* pcomp) { - face neighsh, spinsh; - face testseg; - - sspivot(*s1, testseg); - if (testseg.sh != dummysh) { - testseg.shver = 0; - if (sorg(testseg) == sorg(*s1)) { - spivot(*s1, neighsh); - } else { - spinsh = *s1; - do { - neighsh = spinsh; - spivotself(spinsh); - } while (spinsh.sh != s1->sh); - } - } else { - spivot(*s1, neighsh); - } - if (sorg(neighsh) != sorg(*s1)) { - sesymself(neighsh); + // First figure out whether it is a pointer or not. + if (str[strlen(str) - 1] == '*') { + *itbytes = sizeof(unsigned long); + *pcomp = &compare_2_unsignedlongs; + return; } - if (s2 != (face *) NULL) { - *s2 = neighsh; + // Then determine other types. + if (strcmp(str, "int") == 0) { + *itbytes = sizeof(int); + *pcomp = &compare_2_ints; + } else if (strcmp(str, "long") == 0) { + *itbytes = sizeof(long); + *pcomp = &compare_2_longs; + } else if (strcmp(str, "unsigned long") == 0) { + *itbytes = sizeof(unsigned long); + *pcomp = &compare_2_unsignedlongs; } else { - *s1 = neighsh; + // It is an unknown type. + printf("Error in set_compfunc(): unknown type %s.\n", str); + terminatetetgen(1); } } /////////////////////////////////////////////////////////////////////////////// // // -// tsspivot() Finds a subsegment abutting on a tetrahderon's edge. // -// // -// The edge is represented in the primary edge of 'checkedge'. If there is a // -// subsegment bonded at this edge, it is returned in handle 'checkseg', the // -// edge direction of 'checkseg' is conformed to 'checkedge'. If there isn't, // -// set 'checkseg.sh = dummysh' to indicate it is not a subsegment. // +// listinit() Initialize a list for storing a data type. // // // -// To find whether an edge of a tetrahedron is a subsegment or not. First we // -// need find a subface around this edge to see if it contains a subsegment. // -// The reason is there is no direct connection between a tetrahedron and its // -// adjoining subsegments. // +// Determine the size of each item, set the maximum size allocated at onece, // +// set the expand size in case the list is full, and set the linear order // +// function if it is provided (default is NULL). // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::tsspivot(triface* checkedge, face* checkseg) +void tetgenmesh::list:: +listinit(int itbytes, compfunc pcomp, int mitems,int exsize) { - triface spintet; - face parentsh; - point tapex; - int hitbdry; - - spintet = *checkedge; - tapex = apex(*checkedge); - hitbdry = 0; - do { - tspivot(spintet, parentsh); - // Does spintet have a (non-fake) subface attached? - if ((parentsh.sh != dummysh) && (sapex(parentsh) != NULL)) { - // Find a subface! Find the edge in it. - findedge(&parentsh, org(*checkedge), dest(*checkedge)); - sspivot(parentsh, *checkseg); - if (checkseg->sh != dummysh) { - // Find a subsegment! Correct its edge direction before return. - if (sorg(*checkseg) != org(*checkedge)) { - sesymself(*checkseg); - } - } - return; - } - if (!fnextself(spintet)) { - hitbdry++; - if (hitbdry < 2) { - esym(*checkedge, spintet); - if (!fnextself(spintet)) { - hitbdry++; - } - } - } - } while ((apex(spintet) != tapex) && (hitbdry < 2)); - // Not find. - checkseg->sh = dummysh; +#ifdef SELF_CHECK + assert(itbytes > 0 && mitems > 0 && exsize > 0); +#endif + itembytes = itbytes; + comp = pcomp; + maxitems = mitems; + expandsize = exsize; + base = (char *) malloc(maxitems * itembytes); + if (base == (char *) NULL) { + printf("Error: Out of memory.\n"); + terminatetetgen(1); + } + items = 0; } /////////////////////////////////////////////////////////////////////////////// // // -// sstpivot() Finds a tetrahedron abutting a subsegment. // +// append() Add a new item at the end of the list. // // // -// This is the inverse operation of 'tsspivot()'. One subsegment shared by // -// arbitrary number of tetrahedron, the returned tetrahedron is not unique. // -// The edge direction of the returned tetrahedron is conformed to the given // -// subsegment. // +// A new space at the end of this list will be allocated for storing the new // +// item. If the memory is not sufficient, reallocation will be performed. If // +// 'appitem' is not NULL, the contents of this pointer will be copied to the // +// new allocated space. Returns the pointer to the new allocated space. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::sstpivot(face* checkseg, triface* retedge) +void* tetgenmesh::list::append(void *appitem) { - face parentsh; - - // Get the subface which holds the subsegment. - sdecode(checkseg->sh[0], parentsh); -#ifdef SELF_CHECK - assert(parentsh.sh != dummysh); -#endif - // Get a tetraheron to which the subface attches. - stpivot(parentsh, *retedge); - if (retedge->tet == dummytet) { - sesymself(parentsh); - stpivot(parentsh, *retedge); -#ifdef SELF_CHECK - assert(retedge->tet != dummytet); -#endif + // Do we have enough space? + if (items == maxitems) { + char* newbase = (char *) realloc(base, (maxitems + expandsize) * + itembytes); + if (newbase == (char *) NULL) { + printf("Error: Out of memory.\n"); + terminatetetgen(1); + } + base = newbase; + maxitems += expandsize; } - // Correct the edge direction before return. - findedge(retedge, sorg(*checkseg), sdest(*checkseg)); + if (appitem != (void *) NULL) { + memcpy(base + items * itembytes, appitem, itembytes); + } + items++; + return (void *) (base + (items - 1) * itembytes); } /////////////////////////////////////////////////////////////////////////////// // // -// point2tetorg(), point2shorg(), point2segorg() // +// insert() Insert an item before 'pos' (range from 0 to items - 1). // // // -// Return a tet, a subface, or a subsegment whose origin is the given point. // -// These routines assume the maps between points to tets (subfaces, segments // -// ) have been built and maintained. // +// A new space will be inserted at the position 'pos', that is, items lie // +// after pos (including the item at pos) will be moved one space downwords. // +// If 'insitem' is not NULL, its contents will be copied into the new // +// inserted space. Return a pointer to the new inserted space. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::point2tetorg(point pa, triface& searchtet) +void* tetgenmesh::list::insert(int pos, void* insitem) { - int i; - - // Search a tet whose origin is pa. - decode(point2tet(pa), searchtet); - if (searchtet.tet == NULL) { - printf("Internal error: %d contains bad tet pointer.\n", pointmark(pa)); - terminatetetgen(2); - } - for (i = 4; i < 8; i++) { - if ((point) searchtet.tet[i] == pa) { - // Found. Set pa as its origin. - switch (i) { - case 4: searchtet.loc = 0; searchtet.ver = 0; break; - case 5: searchtet.loc = 0; searchtet.ver = 2; break; - case 6: searchtet.loc = 0; searchtet.ver = 4; break; - case 7: searchtet.loc = 1; searchtet.ver = 2; break; - } - break; + if (pos >= items) { + return append(insitem); + } + // Do we have enough space. + if (items == maxitems) { + char* newbase = (char *) realloc(base, (maxitems + expandsize) * + itembytes); + if (newbase == (char *) NULL) { + printf("Error: Out of memory.\n"); + terminatetetgen(1); } + base = newbase; + maxitems += expandsize; } - if (i == 8) { - printf("Internal error: %d contains bad tet pointer.\n", pointmark(pa)); - terminatetetgen(2); + // Do block move. + memmove(base + (pos + 1) * itembytes, // dest + base + pos * itembytes, // src + (items - pos) * itembytes); // size in bytes + // Insert the item. + if (insitem != (void *) NULL) { + memcpy(base + pos * itembytes, insitem, itembytes); } + items++; + return (void *) (base + pos * itembytes); } -void tetgenmesh::point2shorg(point pa, face& searchsh) -{ - sdecode(point2sh(pa), searchsh); - if (searchsh.sh == NULL) { - printf("Internal error: %d contains bad sub pointer.\n", pointmark(pa)); - terminatetetgen(2); - } - if (((point) searchsh.sh[3]) == pa) { - searchsh.shver = 0; - } else if (((point) searchsh.sh[4]) == pa) { - searchsh.shver = 2; - } else if (((point) searchsh.sh[5]) == pa) { - searchsh.shver = 4; - } else { - printf("Internal error: %d contains bad sub pointer.\n", pointmark(pa)); - terminatetetgen(2); - } -} +/////////////////////////////////////////////////////////////////////////////// +// // +// del() Delete an item at 'pos' (range from 0 to items - 1). // +// // +// The space at 'pos' will be overlapped by other item. If 'order' is 1, the // +// remaining items of the list have the same order as usual, i.e., items lie // +// after pos will be moved one space upwords. If 'order' is 0, the last item // +// of the list will be moved up to pos. // +// // +/////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::point2segorg(point pa, face& searchsh) +void tetgenmesh::list::del(int pos, int order) { - sdecode(point2seg(pa), searchsh); - if (searchsh.sh == NULL) { - printf("Internal error: %d contains bad seg pointer.\n", pointmark(pa)); - terminatetetgen(2); + // If 'pos' is the last item of the list, nothing need to do. + if (pos >= 0 && pos < items - 1) { + if (order == 1) { + // Do block move. + memmove(base + pos * itembytes, // dest + base + (pos + 1) * itembytes, // src + (items - pos - 1) * itembytes); + } else { + // Use the last item to overlap the del item. + memcpy(base + pos * itembytes, // item at pos + base + (items - 1) * itembytes, // item at last + itembytes); + } } - if (((point) searchsh.sh[3]) == pa) { - searchsh.shver = 0; - } else if (((point) searchsh.sh[4]) == pa) { - searchsh.shver = 1; - } else { - printf("Internal error: %d contains bad sub pointer.\n", pointmark(pa)); - terminatetetgen(2); + if (items > 0) { + items--; } } /////////////////////////////////////////////////////////////////////////////// // // -// findorg() Find a point in the given tet or subface. // +// hasitem() Search in this list to find if 'checkitem' exists. // // // -// If 'dorg' is a one of vertices of the given handle, set the origin of // -// this handle be that point and return TRUE. Otherwise, return FALSE and // -// 'tface' remains unchanged. // +// This routine assumes that a linear order function has been set. It loops // +// through the entire list, compares each item to 'checkitem'. If it exists, // +// return its position (between 0 to items - 1), otherwise, return -1. // // // /////////////////////////////////////////////////////////////////////////////// -bool tetgenmesh::findorg(triface* tface, point dorg) +int tetgenmesh::list::hasitem(void* checkitem) { - if (org(*tface) == dorg) { - return true; - } else { - if (dest(*tface) == dorg) { - enextself(*tface); - return true; - } else { - if (apex(*tface) == dorg) { - enext2self(*tface); - return true; - } else { - if (oppo(*tface) == dorg) { - // Keep 'tface' referring to the same tet after fnext(). - adjustedgering(*tface, CCW); - fnextself(*tface); - enext2self(*tface); - return true; - } + int i; + + for (i = 0; i < items; i++) { + if (comp != (compfunc) NULL) { + if ((* comp)((void *)(base + i * itembytes), checkitem) == 0) { + return i; } } } - return false; + return -1; } -bool tetgenmesh::findorg(face* sface, point dorg) +/////////////////////////////////////////////////////////////////////////////// +// // +// sort() Sort the items with respect to a linear order function. // +// // +// Uses QuickSort routines (qsort) of the standard C/C++ library (stdlib.h). // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::list::sort() { - if (sorg(*sface) == dorg) { - return true; - } else { - if (sdest(*sface) == dorg) { - senextself(*sface); - return true; - } else { - if (sapex(*sface) == dorg) { - senext2self(*sface); - return true; - } - } - } - return false; + qsort((void *) base, (size_t) items, (size_t) itembytes, comp); } /////////////////////////////////////////////////////////////////////////////// // // -// findedge() Find an edge in the given tet or subface. // -// // -// The edge is given in two points 'eorg' and 'edest'. It is assumed that // -// the edge must exist in the given handle (tetrahedron or subface). This // -// routine sets the right edge version for the input handle. // +// memorypool() The constructors of memorypool. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::findedge(triface* tface, point eorg, point edest) +tetgenmesh::memorypool::memorypool() { - int i; - - for (i = 0; i < 3; i++) { - if (org(*tface) == eorg) { - if (dest(*tface) == edest) { - // Edge is found, return. - return; - } - } else { - if (org(*tface) == edest) { - if (dest(*tface) == eorg) { - // Edge is found, inverse the direction and return. - esymself(*tface); - return; - } - } - } - enextself(*tface); - } - // It should never be here. - printf("Internalerror in findedge(): Unable to find an edge in tet.\n"); - terminatetetgen(2); + firstblock = nowblock = (void **) NULL; + nextitem = (void *) NULL; + deaditemstack = (void *) NULL; + pathblock = (void **) NULL; + pathitem = (void *) NULL; + itemwordtype = POINTER; + alignbytes = 0; + itembytes = itemwords = 0; + itemsperblock = 0; + items = maxitems = 0l; + unallocateditems = 0; + pathitemsleft = 0; } -void tetgenmesh::findedge(face* sface, point eorg, point edest) +tetgenmesh::memorypool:: +memorypool(int bytecount, int itemcount, enum wordtype wtype, int alignment) { - int i; - - for (i = 0; i < 3; i++) { - if (sorg(*sface) == eorg) { - if (sdest(*sface) == edest) { - // Edge is found, return. - return; - } - } else { - if (sorg(*sface) == edest) { - if (sdest(*sface) == eorg) { - // Edge is found, inverse the direction and return. - sesymself(*sface); - return; - } - } - } - senextself(*sface); - } - printf("Internalerror in findedge(): Unable to find an edge in subface.\n"); - terminatetetgen(2); + poolinit(bytecount, itemcount, wtype, alignment); } /////////////////////////////////////////////////////////////////////////////// // // -// getonextseg() Get the next segment counterclockwise with the same org. // -// // -// 's' is a subface. This routine reteuns the segment which is counterclock- // -// wise with the origin of s. // +// ~memorypool() Free to the operating system all memory taken by a pool. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::getonextseg(face* s, face* lseg) +tetgenmesh::memorypool::~memorypool() { - face checksh, checkseg; - point forg; - - forg = sorg(*s); - checksh = *s; - do { - // Go to the edge at forg's left side. - senext2self(checksh); - // Check if there is a segment attaching this edge. - sspivot(checksh, checkseg); - if (checkseg.sh != dummysh) break; - // No segment! Go to the neighbor of this subface. - spivotself(checksh); -#ifdef SELF_CHECK - // It should always meet a segment before come back. - assert(checksh.sh != s->sh); -#endif - if (sorg(checksh) != forg) { - sesymself(checksh); -#ifdef SELF_CHECK - assert(sorg(checksh) == forg); -#endif - } - } while (true); - if (sorg(checkseg) != forg) sesymself(checkseg); - *lseg = checkseg; + while (firstblock != (void **) NULL) { + nowblock = (void **) *(firstblock); + free(firstblock); + firstblock = nowblock; + } } /////////////////////////////////////////////////////////////////////////////// // // -// getseghasorg() Get the segment containing the given point. // +// poolinit() Initialize a pool of memory for allocation of items. // // // -// 'dorg' is an endpoint of a segment S. 'sseg' is a subsegment of S. This // -// routine search a subsegment (along sseg) of S containing dorg. On return, // -// 'sseg' contains 'dorg' as its origin. // +// A `pool' is created whose records have size at least `bytecount'. Items // +// will be allocated in `itemcount'-item blocks. Each item is assumed to be // +// a collection of words, and either pointers or floating-point values are // +// assumed to be the "primary" word type. (The "primary" word type is used // +// to determine alignment of items.) If `alignment' isn't zero, all items // +// will be `alignment'-byte aligned in memory. `alignment' must be either a // +// multiple or a factor of the primary word size; powers of two are safe. // +// `alignment' is normally used to create a few unused bits at the bottom of // +// each item's pointer, in which information may be stored. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::getseghasorg(face* sseg, point dorg) +void tetgenmesh::memorypool:: +poolinit(int bytecount, int itemcount, enum wordtype wtype, int alignment) { - face nextseg; - point checkpt; + int wordsize; - nextseg = *sseg; - checkpt = sorg(nextseg); - while ((checkpt != dorg) && (pointtype(checkpt) == FREESEGVERTEX)) { - // Search dorg along the original direction of sseg. - senext2self(nextseg); - spivotself(nextseg); - nextseg.shver = 0; - if (sdest(nextseg) != checkpt) sesymself(nextseg); - checkpt = sorg(nextseg); - } - if (checkpt == dorg) { - *sseg = nextseg; - return; - } - nextseg = *sseg; - checkpt = sdest(nextseg); - while ((checkpt != dorg) && (pointtype(checkpt) == FREESEGVERTEX)) { - // Search dorg along the destinational direction of sseg. - senextself(nextseg); - spivotself(nextseg); - nextseg.shver = 0; - if (sorg(nextseg) != checkpt) sesymself(nextseg); - checkpt = sdest(nextseg); + // 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. + // - sizeof(void *), so the stack of dead items can be maintained + // without unaligned accesses. + if (alignment > wordsize) { + alignbytes = alignment; + } else { + alignbytes = wordsize; } - if (checkpt == dorg) { - sesym(nextseg, *sseg); - return; + if ((int) sizeof(void *) > alignbytes) { + alignbytes = (int) sizeof(void *); } - // Should never be here. - printf("Internalerror in getseghasorg(): Unable to find the subseg.\n"); - terminatetetgen(2); -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// getsubsegfarorg() Get the origin of the parent segment of a subseg. // -// // -/////////////////////////////////////////////////////////////////////////////// - -tetgenmesh::point tetgenmesh::getsubsegfarorg(face* sseg) -{ - face prevseg; - point checkpt; + itemwords = ((bytecount + alignbytes - 1) / alignbytes) + * (alignbytes / wordsize); + itembytes = itemwords * wordsize; + itemsperblock = itemcount; - checkpt = sorg(*sseg); - senext2(*sseg, prevseg); - spivotself(prevseg); - // Search dorg along the original direction of sseg. - while (prevseg.sh != dummysh) { - prevseg.shver = 0; - if (sdest(prevseg) != checkpt) sesymself(prevseg); - checkpt = sorg(prevseg); - senext2self(prevseg); - spivotself(prevseg); + // Allocate a block of items. Space for `itemsperblock' items and one + // pointer (to point to the next block) are allocated, as well as space + // to ensure alignment of the items. + firstblock = (void **) malloc(itemsperblock * itembytes + sizeof(void *) + + alignbytes); + if (firstblock == (void **) NULL) { + printf("Error: Out of memory.\n"); + terminatetetgen(1); } - return checkpt; + // Set the next block pointer to NULL. + *(firstblock) = (void *) NULL; + restart(); } /////////////////////////////////////////////////////////////////////////////// // // -// getsubsegfardest() Get the dest. of the parent segment of a subseg. // +// restart() Deallocate all items in this pool. // +// // +// The pool is returned to its starting state, except that no memory is // +// freed to the operating system. Rather, the previously allocated blocks // +// are ready to be reused. // // // /////////////////////////////////////////////////////////////////////////////// -tetgenmesh::point tetgenmesh::getsubsegfardest(face* sseg) +void tetgenmesh::memorypool::restart() { - face nextseg; - point checkpt; + unsigned long alignptr; - checkpt = sdest(*sseg); - senext(*sseg, nextseg); - spivotself(nextseg); - // Search dorg along the destinational direction of sseg. - while (nextseg.sh != dummysh) { - nextseg.shver = 0; - if (sorg(nextseg) != checkpt) sesymself(nextseg); - checkpt = sdest(nextseg); - senextself(nextseg); - spivotself(nextseg); - } - return checkpt; + items = 0; + maxitems = 0; + + // Set the currently active block. + nowblock = firstblock; + // Find the first item in the pool. Increment by the size of (void *). + alignptr = (unsigned long) (nowblock + 1); + // Align the item on an `alignbytes'-byte boundary. + nextitem = (void *) + (alignptr + (unsigned long) alignbytes - + (alignptr % (unsigned long) alignbytes)); + // There are lots of unallocated items left in this block. + unallocateditems = itemsperblock; + // The stack of deallocated items is empty. + deaditemstack = (void *) NULL; } /////////////////////////////////////////////////////////////////////////////// // // -// printtet() Print out the details of a tetrahedron on screen. // -// // -// It's also used when the highest level of verbosity (`-VVV') is specified. // +// alloc() Allocate space for an item. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::printtet(triface* tface) +void* tetgenmesh::memorypool::alloc() { - triface tmpface, prtface; - shellface *shells; - point tmppt; - face checksh; - int facecount; - - printf("Tetra x%lx with loc(%i) and ver(%i):", - (uintptr_t)(tface->tet), tface->loc, tface->ver); - if (infected(*tface)) { - printf(" (infected)"); - } - if (marktested(*tface)) { - printf(" (marked)"); - } - printf("\n"); + void *newitem; + void **newblock; + unsigned long alignptr; - tmpface = *tface; - facecount = 0; - while(facecount < 4) { - tmpface.loc = facecount; - sym(tmpface, prtface); - if(prtface.tet == dummytet) { - printf(" [%i] Outer space.\n", facecount); - } else { - if (!isdead(&prtface)) { - printf(" [%i] x%lx loc(%i).", facecount, - (uintptr_t)(prtface.tet), prtface.loc); - if (infected(prtface)) { - printf(" (infected)"); - } - printf("\n"); - } else { - printf(" [%i] NULL\n", facecount); - } - } - facecount ++; - } - - tmppt = org(*tface); - if(tmppt == (point) NULL) { - printf(" Org [%i] NULL\n", locver2org[tface->loc][tface->ver]); - } else { - printf(" Org [%i] x%lx (%.12g,%.12g,%.12g) %d\n", - locver2org[tface->loc][tface->ver], (uintptr_t)(tmppt), - tmppt[0], tmppt[1], tmppt[2], pointmark(tmppt)); - } - tmppt = dest(*tface); - if(tmppt == (point) NULL) { - printf(" Dest[%i] NULL\n", locver2dest[tface->loc][tface->ver]); - } else { - printf(" Dest[%i] x%lx (%.12g,%.12g,%.12g) %d\n", - locver2dest[tface->loc][tface->ver], (uintptr_t)(tmppt), - tmppt[0], tmppt[1], tmppt[2], pointmark(tmppt)); - } - tmppt = apex(*tface); - if(tmppt == (point) NULL) { - printf(" Apex[%i] NULL\n", locver2apex[tface->loc][tface->ver]); - } else { - printf(" Apex[%i] x%lx (%.12g,%.12g,%.12g) %d\n", - locver2apex[tface->loc][tface->ver], (uintptr_t)(tmppt), - tmppt[0], tmppt[1], tmppt[2], pointmark(tmppt)); - } - tmppt = oppo(*tface); - if(tmppt == (point) NULL) { - printf(" Oppo[%i] NULL\n", loc2oppo[tface->loc]); + // First check the linked list of dead items. If the list is not + // empty, allocate an item from the list rather than a fresh one. + if (deaditemstack != (void *) NULL) { + newitem = deaditemstack; // Take first item in list. + deaditemstack = * (void **) deaditemstack; } else { - printf(" Oppo[%i] x%lx (%.12g,%.12g,%.12g) %d\n", - loc2oppo[tface->loc], (uintptr_t)(tmppt), - tmppt[0], tmppt[1], tmppt[2], pointmark(tmppt)); - } - - if (b->useshelles) { - if (tface->tet[8] != NULL) { - shells = (shellface *) tface->tet[8]; - for (facecount = 0; facecount < 6; facecount++) { - sdecode(shells[facecount], checksh); - if (checksh.sh != dummysh) { - printf(" [%d] x%lx %d.", facecount, (uintptr_t) checksh.sh, - checksh.shver); - } else { - printf(" [%d] NULL.", facecount); - } - if (locver2edge[tface->loc][tface->ver] == facecount) { - printf(" (*)"); // It is the current edge. + // Check if there are any free items left in the current block. + if (unallocateditems == 0) { + // Check if another block must be allocated. + if (*nowblock == (void *) NULL) { + // Allocate a new block of items, pointed to by the previous block. + newblock = (void **) malloc(itemsperblock * itembytes + sizeof(void *) + + alignbytes); + if (newblock == (void **) NULL) { + printf("Error: Out of memory.\n"); + terminatetetgen(1); } - printf("\n"); + *nowblock = (void *) newblock; + // The next block pointer is NULL. + *newblock = (void *) NULL; } + // Move to the new block. + nowblock = (void **) *nowblock; + // Find the first item in the block. + // Increment by the size of (void *). + alignptr = (unsigned long) (nowblock + 1); + // Align the item on an `alignbytes'-byte boundary. + nextitem = (void *) + (alignptr + (unsigned long) alignbytes - + (alignptr % (unsigned long) alignbytes)); + // There are lots of unallocated items left in this block. + unallocateditems = itemsperblock; } - if (tface->tet[9] != NULL) { - shells = (shellface *) tface->tet[9]; - for (facecount = 0; facecount < 4; facecount++) { - sdecode(shells[facecount], checksh); - if (checksh.sh != dummysh) { - printf(" [%d] x%lx %d.", facecount, (uintptr_t) checksh.sh, - checksh.shver); - } else { - printf(" [%d] NULL.", facecount); - } - if (tface->loc == facecount) { - printf(" (*)"); // It is the current face. - } - printf("\n"); - } + // 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); } + unallocateditems--; + maxitems++; } + items++; + return newitem; } /////////////////////////////////////////////////////////////////////////////// // // -// printsh() Print out the details of a subface or subsegment on screen. // +// dealloc() Deallocate space for an item. // // // -// It's also used when the highest level of verbosity (`-VVV') is specified. // +// The deallocated space is stored in a queue for later reuse. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::printsh(face* sface) +void tetgenmesh::memorypool::dealloc(void *dyingitem) { - face prtsh; - triface prttet; - point printpoint; - - if (sapex(*sface) != NULL) { - printf("subface x%lx, ver %d, mark %d:", - (uintptr_t)(sface->sh), sface->shver, shellmark(*sface)); - } else { - printf("Subsegment x%lx, ver %d, mark %d:", - (uintptr_t)(sface->sh), sface->shver, shellmark(*sface)); - } - if (sinfected(*sface)) { - printf(" (infected)"); - } - if (smarktested(*sface)) { - printf(" (marked)"); - } - if (shell2badface(*sface)) { - printf(" (queued)"); - } - if (sapex(*sface) != NULL) { - if (shelltype(*sface) == SHARP) { - printf(" (sharp)"); - } - } else { - if (shelltype(*sface) == SHARP) { - printf(" (sharp)"); - } - } - if (checkpbcs) { - if (shellpbcgroup(*sface) >= 0) { - printf(" (pbc %d)", shellpbcgroup(*sface)); - } - } - printf("\n"); - - sdecode(sface->sh[0], prtsh); - if (prtsh.sh == dummysh) { - printf(" [0] = No shell\n"); - } else { - printf(" [0] = x%lx %d\n", (uintptr_t)(prtsh.sh), prtsh.shver); - } - sdecode(sface->sh[1], prtsh); - if (prtsh.sh == dummysh) { - printf(" [1] = No shell\n"); - } else { - printf(" [1] = x%lx %d\n", (uintptr_t)(prtsh.sh), prtsh.shver); - } - sdecode(sface->sh[2], prtsh); - if (prtsh.sh == dummysh) { - printf(" [2] = No shell\n"); - } else { - printf(" [2] = x%lx %d\n", (uintptr_t)(prtsh.sh), prtsh.shver); - } - - printpoint = sorg(*sface); - if (printpoint == (point) NULL) - printf(" Org [%d] = NULL\n", vo[sface->shver]); - else - printf(" Org [%d] = x%lx (%.12g,%.12g,%.12g) %d\n", - vo[sface->shver], (uintptr_t)(printpoint), printpoint[0], - printpoint[1], printpoint[2], pointmark(printpoint)); - printpoint = sdest(*sface); - if (printpoint == (point) NULL) - printf(" Dest[%d] = NULL\n", vd[sface->shver]); - else - printf(" Dest[%d] = x%lx (%.12g,%.12g,%.12g) %d\n", - vd[sface->shver], (uintptr_t)(printpoint), printpoint[0], - printpoint[1], printpoint[2], pointmark(printpoint)); - - if (sapex(*sface) != NULL) { - printpoint = sapex(*sface); - if (printpoint == (point) NULL) - printf(" Apex[%d] = NULL\n", va[sface->shver]); - else - printf(" Apex[%d] = x%lx (%.12g,%.12g,%.12g) %d\n", - va[sface->shver], (uintptr_t)(printpoint), printpoint[0], - printpoint[1], printpoint[2], pointmark(printpoint)); - - decode(sface->sh[6], prttet); - if (prttet.tet == dummytet) { - printf(" [6] = Outer space\n"); - } else { - printf(" [6] = x%lx %d\n", - (uintptr_t)(prttet.tet), prttet.loc); - } - decode(sface->sh[7], prttet); - if (prttet.tet == dummytet) { - printf(" [7] = Outer space\n"); - } else { - printf(" [7] = x%lx %d\n", - (uintptr_t)(prttet.tet), prttet.loc); - } - - sdecode(sface->sh[8], prtsh); - if (prtsh.sh == dummysh) { - printf(" [8] = No subsegment\n"); - } else { - printf(" [8] = x%lx %d\n", - (uintptr_t)(prtsh.sh), prtsh.shver); - } - sdecode(sface->sh[9], prtsh); - if (prtsh.sh == dummysh) { - printf(" [9] = No subsegment\n"); - } else { - printf(" [9] = x%lx %d\n", - (uintptr_t)(prtsh.sh), prtsh.shver); - } - sdecode(sface->sh[10], prtsh); - if (prtsh.sh == dummysh) { - printf(" [10]= No subsegment\n"); - } else { - printf(" [10]= x%lx %d\n", - (uintptr_t)(prtsh.sh), prtsh.shver); - } - } + // Push freshly killed item onto stack. + *((void **) dyingitem) = deaditemstack; + deaditemstack = dyingitem; + items--; } -//// //// -//// //// -//// prim_cxx ///////////////////////////////////////////////////////////////// - -//// mempool_cxx ////////////////////////////////////////////////////////////// -//// //// -//// //// - /////////////////////////////////////////////////////////////////////////////// // // -// restart() Deallocate all objects in this pool. // +// traversalinit() Prepare to traverse the entire list of items. // // // -// The pool returns to a fresh state, like after it was initialized, except // -// that no memory is freed to the operating system. Rather, the previously // -// allocated blocks are ready to be used. // +// This routine is used in conjunction with traverse(). // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::arraypool::restart() +void tetgenmesh::memorypool::traversalinit() { - objects = 0l; + unsigned long alignptr; + + // Begin the traversal in the first block. + pathblock = firstblock; + // Find the first item in the block. Increment by the size of (void *). + alignptr = (unsigned long) (pathblock + 1); + // Align with item on an `alignbytes'-byte boundary. + pathitem = (void *) + (alignptr + (unsigned long) alignbytes - + (alignptr % (unsigned long) alignbytes)); + // Set the number of items left in the current block. + pathitemsleft = itemsperblock; } /////////////////////////////////////////////////////////////////////////////// // // -// poolinit() Initialize an arraypool for allocation of objects. // +// traverse() Find the next item in the list. // // // -// Before the pool may be used, it must be initialized by this procedure. // -// After initialization, memory can be allocated and freed in this pool. // +// This routine is used in conjunction with traversalinit(). Be forewarned // +// that this routine successively returns all items in the list, including // +// deallocated ones on the deaditemqueue. It's up to you to figure out which // +// ones are actually dead. It can usually be done more space-efficiently by // +// a routine that knows something about the structure of the item. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::arraypool::poolinit(int sizeofobject, int log2objperblk) +void* tetgenmesh::memorypool::traverse() { - // Each object must be at least one byte long. - objectbytes = sizeofobject > 1 ? sizeofobject : 1; - - log2objectsperblock = log2objperblk; - // Compute the number of objects in each block. - objectsperblock = ((int) 1) << log2objectsperblock; - - // No memory has been allocated. - totalmemory = 0l; - // The top array has not been allocated yet. - toparray = (char **) NULL; - toparraylen = 0; + void *newitem; + unsigned long alignptr; - // Ready all indices to be allocated. - restart(); + // Stop upon exhausting the list of items. + if (pathitem == nextitem) { + return (void *) NULL; + } + // Check whether any untraversed items remain in the current block. + if (pathitemsleft == 0) { + // Find the next block. + pathblock = (void **) *pathblock; + // Find the first item in the block. Increment by the size of (void *). + alignptr = (unsigned long) (pathblock + 1); + // Align with item on an `alignbytes'-byte boundary. + pathitem = (void *) + (alignptr + (unsigned long) alignbytes - + (alignptr % (unsigned long) alignbytes)); + // Set the number of items left in the current block. + pathitemsleft = itemsperblock; + } + newitem = pathitem; + // Find the next item in the block. + if (itemwordtype == POINTER) { + pathitem = (void *) ((void **) pathitem + itemwords); + } else { + pathitem = (void *) ((REAL *) pathitem + itemwords); + } + pathitemsleft--; + return newitem; } /////////////////////////////////////////////////////////////////////////////// // // -// arraypool() The constructor and destructor. // +// linkinit() Initialize a link for storing items. // +// // +// The input parameters are the size of each item, a pointer of a linear // +// order function and the number of items allocating in one memory bulk. // // // /////////////////////////////////////////////////////////////////////////////// -tetgenmesh::arraypool::arraypool(int sizeofobject, int log2objperblk) -{ - poolinit(sizeofobject, log2objperblk); -} - -tetgenmesh::arraypool::~arraypool() +void tetgenmesh::link::linkinit(int bytecount, compfunc pcomp, int itemcount) { - int i; - - // Has anything been allocated at all? - if (toparray != (char **) NULL) { - // Walk through the top array. - for (i = 0; i < toparraylen; i++) { - // Check every pointer; NULLs may be scattered randomly. - if (toparray[i] != (char *) NULL) { - // Free an allocated block. - free((void *) toparray[i]); - } - } - // Free the top array. - free((void *) toparray); - } +#ifdef SELF_CHECK + assert(bytecount > 0 && itemcount > 0); +#endif + // Remember the real size of each item. + linkitembytes = bytecount; + // Set the linear order function for this link. + comp = pcomp; - // The top array is no longer allocated. - toparray = (char **) NULL; - toparraylen = 0; - objects = 0; - totalmemory = 0; + // Call the constructor of 'memorypool' to initialize its variables. + // like: itembytes, itemwords, items, ... Each node has size + // bytecount + 2 * sizeof(void **), and total 'itemcount + 2' (because + // link has additional two nodes 'head' and 'tail'). + poolinit(bytecount + 2 * sizeof(void **), itemcount + 2, POINTER, 0); + + // Initial state of this link. + head = (void **) alloc(); + tail = (void **) alloc(); + *head = (void *) tail; + *(head + 1) = NULL; + *tail = NULL; + *(tail + 1) = (void *) head; + nextlinkitem = *head; + curpos = 1; + linkitems = 0; } /////////////////////////////////////////////////////////////////////////////// // // -// getblock() Return (and perhaps create) the block containing the object // -// with a given index. // -// // -// This function takes care of allocating or resizing the top array if nece- // -// ssary, and of allocating the block if it hasn't yet been allocated. // +// clear() Deallocate all nodes in this link. // // // -// Return a pointer to the beginning of the block (NOT the object). // +// The link is returned to its starting state, except that no memory is // +// freed to the operating system. Rather, the previously allocated blocks // +// are ready to be reused. // // // /////////////////////////////////////////////////////////////////////////////// -char* tetgenmesh::arraypool::getblock(int objectindex) +void tetgenmesh::link::clear() { - char **newarray; - char *block; - int newsize; - int topindex; - int i; - - // Compute the index in the top array (upper bits). - topindex = objectindex >> log2objectsperblock; - // Does the top array need to be allocated or resized? - if (toparray == (char **) NULL) { - // Allocate the top array big enough to hold 'topindex', and NULL out - // its contents. - newsize = topindex + 128; - toparray = (char **) malloc((size_t) (newsize * sizeof(char *))); - toparraylen = newsize; - for (i = 0; i < newsize; i++) { - toparray[i] = (char *) NULL; - } - // Account for the memory. - totalmemory = newsize * (unsigned long) sizeof(char *); - } else if (topindex >= toparraylen) { - // Resize the top array, making sure it holds 'topindex'. - newsize = 3 * toparraylen; - if (topindex >= newsize) { - newsize = topindex + 128; - } - // Allocate the new array, copy the contents, NULL out the rest, and - // free the old array. - newarray = (char **) malloc((size_t) (newsize * sizeof(char *))); - for (i = 0; i < toparraylen; i++) { - newarray[i] = toparray[i]; - } - for (i = toparraylen; i < newsize; i++) { - newarray[i] = (char *) NULL; - } - free(toparray); - // Account for the memory. - totalmemory += (newsize - toparraylen) * sizeof(char *); - toparray = newarray; - toparraylen = newsize; - } - - // Find the block, or learn that it hasn't been allocated yet. - block = toparray[topindex]; - if (block == (char *) NULL) { - // Allocate a block at this index. - block = (char *) malloc((size_t) (objectsperblock * objectbytes)); - toparray[topindex] = block; - // Account for the memory. - totalmemory += objectsperblock * objectbytes; - } + // Reset the pool. + restart(); - // Return a pointer to the block. - return block; + // Initial state of this link. + head = (void **) alloc(); + tail = (void **) alloc(); + *head = (void *) tail; + *(head + 1) = NULL; + *tail = NULL; + *(tail + 1) = (void *) head; + nextlinkitem = *head; + curpos = 1; + linkitems = 0; } /////////////////////////////////////////////////////////////////////////////// // // -// lookup() Return the pointer to the object with a given index, or NULL // -// if the object's block doesn't exist yet. // +// move() Causes 'nextlinkitem' to traverse the specified number of nodes,// +// updates 'curpos' to be the node to which 'nextlinkitem' points. // +// // +// 'numberofnodes' is a number indicating how many nodes need be traversed // +// (not counter the current node) need be traversed. It may be positive(move // +// forward) or negative (move backward). Return TRUE if it is successful. // // // /////////////////////////////////////////////////////////////////////////////// -void* tetgenmesh::arraypool::lookup(int objectindex) +bool tetgenmesh::link::move(int numberofnodes) { - char *block; - int topindex; - - // Has the top array been allocated yet? - if (toparray == (char **) NULL) { - return (void *) NULL; - } - - // Compute the index in the top array (upper bits). - topindex = objectindex >> log2objectsperblock; - // Does the top index fit in the top array? - if (topindex >= toparraylen) { - return (void *) NULL; - } + void **nownode; + int i; - // Find the block, or learn that it hasn't been allocated yet. - block = toparray[topindex]; - if (block == (char *) NULL) { - return (void *) NULL; + nownode = (void **) nextlinkitem; + if (numberofnodes > 0) { + // Move forward. + i = 0; + while ((i < numberofnodes) && *nownode) { + nownode = (void **) *nownode; + i++; + } + if (*nownode == NULL) return false; + nextlinkitem = (void *) nownode; + curpos += numberofnodes; + } else if (numberofnodes < 0) { + // Move backward. + i = 0; + numberofnodes = -numberofnodes; + while ((i < numberofnodes) && *(nownode + 1)) { + nownode = (void **) *(nownode + 1); + i++; + } + if (*(nownode + 1) == NULL) return false; + nextlinkitem = (void *) nownode; + curpos -= numberofnodes; } - - // Compute a pointer to the object with the given index. Note that - // 'objectsperblock' is a power of two, so the & operation is a bit mask - // that preserves the lower bits. - return (void *)(block + (objectindex & (objectsperblock - 1)) * objectbytes); + return true; } /////////////////////////////////////////////////////////////////////////////// // // -// newindex() Allocate space for a fresh object from the pool. // +// locate() Locates the node at the specified position. // +// // +// The number 'pos' (between 1 and 'linkitems') indicates the location. This // +// routine first decides the shortest path traversing from 'curpos' to 'pos',// +// i.e., from head, tail or 'curpos'. Routine 'move()' is called to really // +// traverse the link. If success, 'nextlinkitem' points to the node, 'curpos'// +// and 'pos' are equal. Otherwise, return FALSE. // // // /////////////////////////////////////////////////////////////////////////////// -int tetgenmesh::arraypool::newindex(void **newptr) +bool tetgenmesh::link::locate(int pos) { - void *newobject; - int newindex; + int headdist, taildist, curdist; + int abscurdist, mindist; + + if (pos < 1 || pos > linkitems) return false; - // Allocate an object at index 'firstvirgin'. - newindex = objects; - newobject = (void *) (getblock(objects) + - (objects & (objectsperblock - 1)) * objectbytes); - objects++; + headdist = pos - 1; + taildist = linkitems - pos; + curdist = pos - curpos; + abscurdist = curdist >= 0 ? curdist : -curdist; - // If 'newptr' is not NULL, use it to return a pointer to the object. - if (newptr != (void **) NULL) { - *newptr = newobject; + if (headdist > taildist) { + if (taildist > abscurdist) { + mindist = curdist; + } else { + // taildist <= abs(curdist) + mindist = -taildist; + goend(); + } + } else { + // headdist <= taildist + if (headdist > abscurdist) { + mindist = curdist; + } else { + // headdist <= abs(curdist) + mindist = headdist; + rewind(); + } } - return newindex; + + return move(mindist); } /////////////////////////////////////////////////////////////////////////////// // // -// listinit() Initialize a list for storing a data type. // +// add() Add a node at the end of this link. // // // -// Determine the size of each item, set the maximum size allocated at onece, // -// set the expand size in case the list is full, and set the linear order // -// function if it is provided (default is NULL). // +// A new node is appended to the end of the link. If 'newitem' is not NULL, // +// its conents will be copied to the data slot of the new node. Returns the // +// pointer to the newest added node. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::list::listinit(int itbytes, compfunc pcomp, int mitems, - int exsize) +void* tetgenmesh::link::add(void* newitem) { - itembytes = itbytes; - comp = pcomp; - maxitems = mitems; - expandsize = exsize; - base = (char *) malloc(maxitems * itembytes); - if (base == (char *) NULL) { - terminatetetgen(1); + void **newnode = tail; + if (newitem != (void *) NULL) { + memcpy((void *)(newnode + 2), newitem, linkitembytes); } - items = 0; + tail = (void **) alloc(); + *tail = NULL; + *newnode = (void*) tail; + *(tail + 1) = (void*) newnode; + linkitems++; + return (void *)(newnode + 2); } /////////////////////////////////////////////////////////////////////////////// // // -// append() Add a new item at the end of the list. // +// insert() Inserts a node before the specified position. // // // -// A new space at the end of this list will be allocated for storing the new // -// item. If the memory is not sufficient, reallocation will be performed. If // -// 'appitem' is not NULL, the contents of this pointer will be copied to the // -// new allocated space. Returns the pointer to the new allocated space. // +// 'pos' (between 1 and 'linkitems') indicates the inserting position. This // +// routine inserts a new node before the node of 'pos'. If 'newitem' is not // +// NULL, its conents will be copied into the data slot of the new node. If // +// 'pos' is larger than 'linkitems', it is equal as 'add()'. A pointer to // +// the newest inserted item is returned. // // // /////////////////////////////////////////////////////////////////////////////// -void* tetgenmesh::list::append(void *appitem) +void* tetgenmesh::link::insert(int pos, void* insitem) { - // Do we have enough space? - if (items == maxitems) { - char* newbase = (char *) realloc(base, (maxitems + expandsize) * - itembytes); - if (newbase == (char *) NULL) { - terminatetetgen(1); - } - base = newbase; - maxitems += expandsize; - } - if (appitem != (void *) NULL) { - memcpy(base + items * itembytes, appitem, itembytes); + if (!locate(pos)) { + return add(insitem); } - items++; - return (void *) (base + (items - 1) * itembytes); -} -/////////////////////////////////////////////////////////////////////////////// -// // -// insert() Insert an item before 'pos' (range from 0 to items - 1). // -// // -// A new space will be inserted at the position 'pos', that is, items lie // -// after pos (including the item at pos) will be moved one space downwords. // -// If 'insitem' is not NULL, its contents will be copied into the new // -// inserted space. Return a pointer to the new inserted space. // -// // -/////////////////////////////////////////////////////////////////////////////// + void **nownode = (void **) nextlinkitem; -void* tetgenmesh::list::insert(int pos, void* insitem) -{ - if (pos >= items) { - return append(insitem); - } - // Do we have enough space. - if (items == maxitems) { - char* newbase = (char *) realloc(base, (maxitems + expandsize) * - itembytes); - if (newbase == (char *) NULL) { - terminatetetgen(1); - } - base = newbase; - maxitems += expandsize; - } - // Do block move. - memmove(base + (pos + 1) * itembytes, // dest - base + pos * itembytes, // src - (items - pos) * itembytes); // size in bytes - // Insert the item. + // Insert a node before 'nownode'. + void **newnode = (void **) alloc(); if (insitem != (void *) NULL) { - memcpy(base + pos * itembytes, insitem, itembytes); + memcpy((void *)(newnode + 2), insitem, linkitembytes); } - items++; - return (void *) (base + pos * itembytes); + + *(void **)(*(nownode + 1)) = (void *) newnode; + *newnode = (void *) nownode; + *(newnode + 1) = *(nownode + 1); + *(nownode + 1) = (void *) newnode; + + linkitems++; + + nextlinkitem = (void *) newnode; + return (void *)(newnode + 2); } /////////////////////////////////////////////////////////////////////////////// // // -// del() Delete an item at 'pos' (range from 0 to items - 1). // +// del() Delete a node. // // // -// The space at 'pos' will be overlapped by other item. If 'order' is 1, the // -// remaining items of the list have the same order as usual, i.e., items lie // -// after pos will be moved one space upwords. If 'order' is 0, the last item // -// of the list will be moved up to pos. // +// Returns a pointer of the deleted data. If you try to delete a non-existed // +// node (e.g. link is empty or a wrong index is given) return NULL. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::list::del(int pos, int order) +void* tetgenmesh::link::deletenode(void** deadnode) { - // If 'pos' is the last item of the list, nothing need to do. - if (pos >= 0 && pos < items - 1) { - if (order == 1) { - // Do block move. - memmove(base + pos * itembytes, // dest - base + (pos + 1) * itembytes, // src - (items - pos - 1) * itembytes); - } else { - // Use the last item to overlap the del item. - memcpy(base + pos * itembytes, // item at pos - base + (items - 1) * itembytes, // item at last - itembytes); - } - } - if (items > 0) { - items--; - } + void **nextnode = (void **) *deadnode; + void **prevnode = (void **) *(deadnode + 1); + *prevnode = (void *) nextnode; + *(nextnode + 1) = (void *) prevnode; + + dealloc((void *) deadnode); + linkitems--; + + nextlinkitem = (void *) nextnode; + return (void *)(deadnode + 2); } /////////////////////////////////////////////////////////////////////////////// // // -// hasitem() Search in this list to find if 'checkitem' exists. // +// del() Delete a node at the specified position. // // // -// This routine assumes that a linear order function has been set. It loops // -// through the entire list, compares each item to 'checkitem'. If it exists, // -// return its position (between 0 to items - 1), otherwise, return -1. // +// 'pos' between 1 and 'linkitems'. Returns a pointer of the deleted data. // +// If you try to delete a non-existed node (e.g. link is empty or a wrong // +// index is given) return NULL. // // // /////////////////////////////////////////////////////////////////////////////// -int tetgenmesh::list::hasitem(void* checkitem) +void* tetgenmesh::link::del(int pos) { - int i; - - for (i = 0; i < items; i++) { - if (comp != (compfunc) NULL) { - if ((* comp)((void *)(base + i * itembytes), checkitem) == 0) { - return i; - } - } + if (!locate(pos) || (linkitems == 0)) { + return (void *) NULL; } - return -1; + return deletenode((void **) nextlinkitem); } /////////////////////////////////////////////////////////////////////////////// // // -// memorypool() The constructors of memorypool. // +// getitem() The link traversal routine. // +// // +// Returns the node to which 'nextlinkitem' points. Returns a 'NULL' if the // +// end of the link is reaching. Both 'nextlinkitem' and 'curpos' will be // +// updated after this operation. // // // /////////////////////////////////////////////////////////////////////////////// -tetgenmesh::memorypool::memorypool() -{ - firstblock = nowblock = (void **) NULL; - nextitem = (void *) NULL; - deaditemstack = (void *) NULL; - pathblock = (void **) NULL; - pathitem = (void *) NULL; - itemwordtype = POINTER; - alignbytes = 0; - itembytes = itemwords = 0; - itemsperblock = 0; - items = maxitems = 0l; - unallocateditems = 0; - pathitemsleft = 0; -} - -tetgenmesh::memorypool:: -memorypool(int bytecount, int itemcount, enum wordtype wtype, int alignment) +void* tetgenmesh::link::getitem() { - poolinit(bytecount, itemcount, wtype, alignment); + if (nextlinkitem == (void *) tail) return NULL; + void **nownode = (void **) nextlinkitem; + nextlinkitem = *nownode; + curpos += 1; + return (void *)(nownode + 2); } /////////////////////////////////////////////////////////////////////////////// // // -// ~memorypool() Free to the operating system all memory taken by a pool. // +// getnitem() Returns the node at a specified position. // +// // +// 'pos' between 1 and 'linkitems'. After this operation, 'nextlinkitem' and // +// 'curpos' will be updated to indicate this node. // // // /////////////////////////////////////////////////////////////////////////////// -tetgenmesh::memorypool::~memorypool() +void* tetgenmesh::link::getnitem(int pos) { - while (firstblock != (void **) NULL) { - nowblock = (void **) *(firstblock); - free(firstblock); - firstblock = nowblock; - } + if (!locate(pos)) return NULL; + return (void *)((void **) nextlinkitem + 2); } /////////////////////////////////////////////////////////////////////////////// // // -// poolinit() Initialize a pool of memory for allocation of items. // +// hasitem() Search in this link to find if 'checkitem' exists. // // // -// A `pool' is created whose records have size at least `bytecount'. Items // -// will be allocated in `itemcount'-item blocks. Each item is assumed to be // -// a collection of words, and either pointers or floating-point values are // -// assumed to be the "primary" word type. (The "primary" word type is used // -// to determine alignment of items.) If `alignment' isn't zero, all items // -// will be `alignment'-byte aligned in memory. `alignment' must be either a // -// multiple or a factor of the primary word size; powers of two are safe. // -// `alignment' is normally used to create a few unused bits at the bottom of // -// each item's pointer, in which information may be stored. // +// If 'checkitem' exists, return its position (between 1 to 'linkitems'), // +// otherwise, return -1. This routine requires the linear order function has // +// been set. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::memorypool:: -poolinit(int bytecount, int itemcount, enum wordtype wtype, int alignment) +int tetgenmesh::link::hasitem(void* checkitem) { - 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. - // - sizeof(void *), so the stack of dead items can be maintained - // without unaligned accesses. - if (alignment > wordsize) { - alignbytes = alignment; - } else { - alignbytes = wordsize; - } - if ((int) sizeof(void *) > alignbytes) { - alignbytes = (int) sizeof(void *); - } - itemwords = ((bytecount + alignbytes - 1) / alignbytes) - * (alignbytes / wordsize); - itembytes = itemwords * wordsize; - itemsperblock = itemcount; + void *pathitem; + int count; - // Allocate a block of items. Space for `itemsperblock' items and one - // pointer (to point to the next block) are allocated, as well as space - // to ensure alignment of the items. - firstblock = (void **) malloc(itemsperblock * itembytes + sizeof(void *) - + alignbytes); - if (firstblock == (void **) NULL) { - terminatetetgen(1); + rewind(); + pathitem = getitem(); + count = 0; + while (pathitem) { + count ++; + if (comp) { + if ((* comp)(pathitem, checkitem) == 0) { + return count; + } + } + pathitem = getitem(); } - // Set the next block pointer to NULL. - *(firstblock) = (void *) NULL; - restart(); + return -1; } -/////////////////////////////////////////////////////////////////////////////// -// // -// restart() Deallocate all items in this pool. // -// // -// The pool is returned to its starting state, except that no memory is // -// freed to the operating system. Rather, the previously allocated blocks // -// are ready to be reused. // -// // -/////////////////////////////////////////////////////////////////////////////// +// +// End of class 'list', 'memorypool' and 'link' implementation +// -void tetgenmesh::memorypool::restart() -{ - // unsigned long alignptr; - uintptr_t alignptr; +// +// Begin of mesh manipulation primitives +// - items = 0; - maxitems = 0; +// +// Begin of tables initialization. +// - // Set the currently active block. - nowblock = firstblock; - // Find the first item in the pool. Increment by the size of (void *). - // alignptr = (unsigned long) (nowblock + 1); - alignptr = (uintptr_t) (nowblock + 1); - // Align the item on an `alignbytes'-byte boundary. - // nextitem = (void *) - // (alignptr + (unsigned long) alignbytes - - // (alignptr % (unsigned long) alignbytes)); - nextitem = (void *) - (alignptr + (uintptr_t) alignbytes - - (alignptr % (uintptr_t) alignbytes)); - // There are lots of unallocated items left in this block. - unallocateditems = itemsperblock; - // The stack of deallocated items is empty. - deaditemstack = (void *) NULL; -} +// For enumerating three edges of a triangle. -/////////////////////////////////////////////////////////////////////////////// -// // -// alloc() Allocate space for an item. // -// // -/////////////////////////////////////////////////////////////////////////////// +int tetgenmesh::plus1mod3[3] = {1, 2, 0}; +int tetgenmesh::minus1mod3[3] = {2, 0, 1}; -void* tetgenmesh::memorypool::alloc() -{ - void *newitem; - void **newblock; - // unsigned long alignptr; - uintptr_t alignptr; +// Table 've' takes an edge version as input, returns the next edge version +// in the same edge ring. - // First check the linked list of dead items. If the list is not - // empty, allocate an item from the list rather than a fresh one. - if (deaditemstack != (void *) NULL) { - newitem = deaditemstack; // Take first item in list. - deaditemstack = * (void **) deaditemstack; - } else { - // Check if there are any free items left in the current block. - if (unallocateditems == 0) { - // Check if another block must be allocated. - if (*nowblock == (void *) NULL) { - // Allocate a new block of items, pointed to by the previous block. - newblock = (void **) malloc(itemsperblock * itembytes + sizeof(void *) - + alignbytes); - if (newblock == (void **) NULL) { - terminatetetgen(1); - } - *nowblock = (void *) newblock; - // The next block pointer is NULL. - *newblock = (void *) NULL; - } - // Move to the new block. - nowblock = (void **) *nowblock; - // Find the first item in the block. - // Increment by the size of (void *). - // alignptr = (unsigned long) (nowblock + 1); - alignptr = (uintptr_t) (nowblock + 1); - // Align the item on an `alignbytes'-byte boundary. - // nextitem = (void *) - // (alignptr + (unsigned long) alignbytes - - // (alignptr % (unsigned long) alignbytes)); - nextitem = (void *) - (alignptr + (uintptr_t) alignbytes - - (alignptr % (uintptr_t) alignbytes)); - // There are lots of unallocated items left in this block. - unallocateditems = itemsperblock; - } - // 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); - } - unallocateditems--; - maxitems++; - } - items++; - return newitem; -} +int tetgenmesh::ve[6] = { 2, 5, 4, 1, 0, 3 }; -/////////////////////////////////////////////////////////////////////////////// -// // -// dealloc() Deallocate space for an item. // -// // -// The deallocated space is stored in a queue for later reuse. // -// // -/////////////////////////////////////////////////////////////////////////////// +// Tables 'vo', 'vd' and 'va' take an edge version, return the positions of +// the origin, destination and apex in the triangle. -void tetgenmesh::memorypool::dealloc(void *dyingitem) -{ - // Push freshly killed item onto stack. - *((void **) dyingitem) = deaditemstack; - deaditemstack = dyingitem; - items--; -} +int tetgenmesh::vo[6] = { 0, 1, 1, 2, 2, 0 }; +int tetgenmesh::vd[6] = { 1, 0, 2, 1, 0, 2 }; +int tetgenmesh::va[6] = { 2, 2, 0, 0, 1, 1 }; -/////////////////////////////////////////////////////////////////////////////// -// // -// traversalinit() Prepare to traverse the entire list of items. // -// // -// This routine is used in conjunction with traverse(). // -// // -/////////////////////////////////////////////////////////////////////////////// +// The following tables are for tetrahedron primitives (operate on trifaces). -void tetgenmesh::memorypool::traversalinit() -{ - // unsigned long alignptr; - uintptr_t alignptr; +// For 'org()', 'dest()' and 'apex()'. Use 'loc' as the first index and +// 'ver' as the second index. - // Begin the traversal in the first block. - pathblock = firstblock; - // Find the first item in the block. Increment by the size of (void *). - // alignptr = (unsigned long) (pathblock + 1); - alignptr = (uintptr_t) (pathblock + 1); - // Align with item on an `alignbytes'-byte boundary. - // pathitem = (void *) - // (alignptr + (unsigned long) alignbytes - - // (alignptr % (unsigned long) alignbytes)); - pathitem = (void *) - (alignptr + (uintptr_t) alignbytes - - (alignptr % (uintptr_t) alignbytes)); - // Set the number of items left in the current block. - pathitemsleft = itemsperblock; -} +int tetgenmesh::locver2org[4][6] = { + {0, 1, 1, 2, 2, 0}, + {0, 3, 3, 1, 1, 0}, + {1, 3, 3, 2, 2, 1}, + {2, 3, 3, 0, 0, 2} +}; +int tetgenmesh::locver2dest[4][6] = { + {1, 0, 2, 1, 0, 2}, + {3, 0, 1, 3, 0, 1}, + {3, 1, 2, 3, 1, 2}, + {3, 2, 0, 3, 2, 0} +}; +int tetgenmesh::locver2apex[4][6] = { + {2, 2, 0, 0, 1, 1}, + {1, 1, 0, 0, 3, 3}, + {2, 2, 1, 1, 3, 3}, + {0, 0, 2, 2, 3, 3} +}; -/////////////////////////////////////////////////////////////////////////////// -// // -// traverse() Find the next item in the list. // -// // -// This routine is used in conjunction with traversalinit(). Be forewarned // -// that this routine successively returns all items in the list, including // -// deallocated ones on the deaditemqueue. It's up to you to figure out which // -// ones are actually dead. It can usually be done more space-efficiently by // -// a routine that knows something about the structure of the item. // -// // -/////////////////////////////////////////////////////////////////////////////// +// For oppo() primitives, use 'loc' as the index. -void* tetgenmesh::memorypool::traverse() -{ - void *newitem; - // unsigned long alignptr; - uintptr_t alignptr; +int tetgenmesh::loc2oppo[4] = { 3, 2, 0, 1 }; - // Stop upon exhausting the list of items. - if (pathitem == nextitem) { - return (void *) NULL; - } - // Check whether any untraversed items remain in the current block. - if (pathitemsleft == 0) { - // Find the next block. - pathblock = (void **) *pathblock; - // Find the first item in the block. Increment by the size of (void *). - // alignptr = (unsigned long) (pathblock + 1); - alignptr = (uintptr_t) (pathblock + 1); - // Align with item on an `alignbytes'-byte boundary. - // pathitem = (void *) - // (alignptr + (unsigned long) alignbytes - - // (alignptr % (unsigned long) alignbytes)); - pathitem = (void *) - (alignptr + (uintptr_t) alignbytes - - (alignptr % (uintptr_t) alignbytes)); - // Set the number of items left in the current block. - pathitemsleft = itemsperblock; - } - newitem = pathitem; - // Find the next item in the block. - if (itemwordtype == POINTER) { - pathitem = (void *) ((void **) pathitem + itemwords); - } else { - pathitem = (void *) ((REAL *) pathitem + itemwords); - } - pathitemsleft--; - return newitem; -} +// For fnext() primitive. Use 'loc' as the first index and 'ver' as the +// second index. Returns a new 'loc' and new 'ver' in an array. (It is +// only valid for edge version equals one of {0, 2, 4}.) -/////////////////////////////////////////////////////////////////////////////// -// // -// makepoint2tetmap() Construct a mapping from points to tetrahedra. // -// // -// Traverses all the tetrahedra, provides each corner of each tetrahedron // -// with a pointer to that tetrahedera. Some pointers will be overwritten by // -// other pointers because each point may be a corner of several tetrahedra, // -// but in the end every point will point to a tetrahedron that contains it. // -// // -/////////////////////////////////////////////////////////////////////////////// +int tetgenmesh::locver2nextf[4][6][2] = { + { {1, 5}, {-1, -1}, {2, 5}, {-1, -1}, {3, 5}, {-1, -1} }, + { {3, 3}, {-1, -1}, {2, 1}, {-1, -1}, {0, 1}, {-1, -1} }, + { {1, 3}, {-1, -1}, {3, 1}, {-1, -1}, {0, 3}, {-1, -1} }, + { {2, 3}, {-1, -1}, {1, 1}, {-1, -1}, {0, 5}, {-1, -1} } +}; -void tetgenmesh::makepoint2tetmap() -{ - triface tetloop; - point pointptr; +// The edge number (from 0 to 5) of a tet is defined as follows: +// 0 - (v0, v1), 1 - (v1, v2), 2 - (v2, v0) +// 3 - (v3, v0), 4 - (v3, v1), 5 - (v3, v2). - if (b->verbose > 2) { - printf(" Constructing mapping from points to tetrahedra.\n"); - } +int tetgenmesh::locver2edge[4][6] = { + {0, 0, 1, 1, 2, 2}, + {3, 3, 4, 4, 0, 0}, + {4, 4, 5, 5, 1, 1}, + {5, 5, 3, 3, 2, 2} +}; - // Initialize the point2tet field of each point. - points->traversalinit(); - pointptr = pointtraverse(); - while (pointptr != (point) NULL) { - setpoint2tet(pointptr, (tetrahedron) NULL); - pointptr = pointtraverse(); - } +int tetgenmesh::edge2locver[6][2] = { + {0, 0}, // 0 v0 -> v1 + {0, 2}, // 1 v1 -> v2 + {0, 4}, // 2 v2 -> v1 + {1, 0}, // 3 v0 -> v3 + {1, 2}, // 4 v1 -> v3 + {2, 2} // 5 v2 -> v3 +}; - tetrahedrons->traversalinit(); - tetloop.tet = tetrahedrontraverse(); - while (tetloop.tet != (tetrahedron *) NULL) { - // Check all four points of the tetrahedron. - tetloop.loc = 0; - pointptr = org(tetloop); - setpoint2tet(pointptr, encode(tetloop)); - pointptr = dest(tetloop); - setpoint2tet(pointptr, encode(tetloop)); - pointptr = apex(tetloop); - setpoint2tet(pointptr, encode(tetloop)); - pointptr = oppo(tetloop); - setpoint2tet(pointptr, encode(tetloop)); - // Get the next tetrahedron in the list. - tetloop.tet = tetrahedrontraverse(); - } -} +// +// End of tables initialization. +// -void tetgenmesh::makepoint2segmap() -{ - face segloop; - point *ppt; +// Some macros for convenience - if (b->verbose > 2) { - printf(" Constructing mapping from points to segments.\n"); - } +#define Div2 >> 1 +#define Mod2 & 01 - segloop.shver = 0; - subsegs->traversalinit(); - segloop.sh = shellfacetraverse(subsegs); - while (segloop.sh != NULL) { - ppt = (point *) &(segloop.sh[3]); - setpoint2seg(ppt[0], sencode(segloop)); - setpoint2seg(ppt[1], sencode(segloop)); - segloop.sh = shellfacetraverse(subsegs); - } -} +// NOTE: These bit operators should only be used in macros below. -/////////////////////////////////////////////////////////////////////////////// -// // -// 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. // -// // -/////////////////////////////////////////////////////////////////////////////// +// Get orient(Range from 0 to 2) from face version(Range from 0 to 5). -void tetgenmesh::makeindex2pointmap(point*& idx2verlist) -{ - point pointloop; - int idx; +#define Orient(V) ((V) Div2) - if (b->verbose > 1) { - printf(" Constructing mapping from indices to points.\n"); - } +// Determine edge ring(0 or 1) from face version(Range from 0 to 5). - idx2verlist = new point[points->items]; +#define EdgeRing(V) ((V) Mod2) - points->traversalinit(); - pointloop = pointtraverse(); - idx = 0; - while (pointloop != (point) NULL) { - idx2verlist[idx] = pointloop; - idx++; - pointloop = pointtraverse(); - } +// +// Begin of primitives for tetrahedra +// + +// Each tetrahedron contains four pointers to its neighboring tetrahedra, +// with face indices. To save memory, both information are kept in a +// single pointer. To make this possible, all tetrahedra are aligned to +// eight-byte boundaries, so that the last three bits of each pointer are +// zeros. A face index (in the range 0 to 3) is compressed into the last +// two bits of each pointer by the function 'encode()'. The function +// 'decode()' decodes a pointer, extracting a face index and a pointer to +// the beginning of a tetrahedron. + +inline void tetgenmesh::decode(tetrahedron ptr, triface& t) { + t.loc = (int) ((unsigned long) (ptr) & (unsigned long) 3l); + t.tet = (tetrahedron *) ((unsigned long) (ptr) & ~(unsigned long) 7l); } -/////////////////////////////////////////////////////////////////////////////// -// // -// makesegmentmap(), makesubfacemap(), maketetrahedronmap() // -// // -// Create a map from vertex indices to segments, subfaces, and tetrahedra // -// sharing at the same vertices. // -// // -// The map is stored in two arrays: 'idx2___list' and '___sperverlist', they // -// form a sparse matrix whose size is (n+1)x(n+1), where n is the number of // -// segments, subfaces, or tetrahedra. 'idx2___list' contains row information // -// and '___sperverlist' contains all non-zero elements. The i-th entry of // -// 'idx2___list' is the starting position of i-th row's non-zero elements in // -// '___sperverlist'. The number of elements of i-th row is (i+1)-th entry // -// minus i-th entry of 'idx2___list'. // -// // -// NOTE: These two arrays will be created inside this routine, don't forget // -// to free them after using. // -// // -/////////////////////////////////////////////////////////////////////////////// +inline tetgenmesh::tetrahedron tetgenmesh::encode(triface& t) { + return (tetrahedron) ((unsigned long) t.tet | (unsigned long) t.loc); +} -void tetgenmesh::makesegmentmap(int*& idx2seglist, shellface**& segsperverlist) -{ - shellface *shloop; - int i, j, k; +// sym() finds the abutting tetrahedron on the same face. - if (b->verbose > 1) { - printf(" Constructing mapping from points to segments.\n"); - } +inline void tetgenmesh::sym(triface& t1, triface& t2) { + tetrahedron ptr = t1.tet[t1.loc]; + decode(ptr, t2); +} - // Create and initialize 'idx2seglist'. - idx2seglist = new int[points->items + 1]; - for (i = 0; i < points->items + 1; i++) idx2seglist[i] = 0; +inline void tetgenmesh::symself(triface& t) { + tetrahedron ptr = t.tet[t.loc]; + decode(ptr, t); +} - // Loop the set of segments once, counter the number of segments sharing - // each vertex. - subsegs->traversalinit(); - shloop = shellfacetraverse(subsegs); - while (shloop != (shellface *) NULL) { - // Increment the number of sharing segments for each endpoint. - for (i = 0; i < 2; i++) { - j = pointmark((point) shloop[3 + i]) - in->firstnumber; - idx2seglist[j]++; - } - shloop = shellfacetraverse(subsegs); - } +// Bond two tetrahedra together at their faces. - // Calculate the total length of array 'facesperverlist'. - j = idx2seglist[0]; - idx2seglist[0] = 0; // Array starts from 0 element. - for (i = 0; i < points->items; i++) { - k = idx2seglist[i + 1]; - idx2seglist[i + 1] = idx2seglist[i] + j; - j = k; - } - // The total length is in the last unit of idx2seglist. - segsperverlist = new shellface*[idx2seglist[i]]; - // Loop the set of segments again, set the info. of segments per vertex. - subsegs->traversalinit(); - shloop = shellfacetraverse(subsegs); - while (shloop != (shellface *) NULL) { - for (i = 0; i < 2; i++) { - j = pointmark((point) shloop[3 + i]) - in->firstnumber; - segsperverlist[idx2seglist[j]] = shloop; - idx2seglist[j]++; - } - shloop = shellfacetraverse(subsegs); - } - // Contents in 'idx2seglist' are shifted, now shift them back. - for (i = points->items - 1; i >= 0; i--) { - idx2seglist[i + 1] = idx2seglist[i]; - } - idx2seglist[0] = 0; +inline void tetgenmesh::bond(triface& t1, triface& t2) { + t1.tet[t1.loc] = encode(t2); + t2.tet[t2.loc] = encode(t1); } -void tetgenmesh::makesubfacemap(int*& idx2facelist, - shellface**& facesperverlist) -{ - shellface *shloop; - int i, j, k; +// Dissolve a bond (from one side). Note that the other tetrahedron will +// still think it is connected to this tetrahedron. Usually, however, +// the other tetrahedron is being deleted entirely, or bonded to another +// tetrahedron, so it doesn't matter. - if (b->verbose > 1) { - printf(" Constructing mapping from points to subfaces.\n"); - } +inline void tetgenmesh::dissolve(triface& t) { + t.tet[t.loc] = (tetrahedron) dummytet; +} - // Create and initialize 'idx2facelist'. - idx2facelist = new int[points->items + 1]; - for (i = 0; i < points->items + 1; i++) idx2facelist[i] = 0; +// These primitives determine or set the origin, destination, apex or +// opposition of a tetrahedron with respect to 'loc' and 'ver'. - // Loop the set of subfaces once, counter the number of subfaces sharing - // each vertex. - subfaces->traversalinit(); - shloop = shellfacetraverse(subfaces); - while (shloop != (shellface *) NULL) { - // Increment the number of sharing segments for each endpoint. - for (i = 0; i < 3; i++) { - j = pointmark((point) shloop[3 + i]) - in->firstnumber; - idx2facelist[j]++; - } - shloop = shellfacetraverse(subfaces); - } +inline tetgenmesh::point tetgenmesh::org(triface& t) { + return (point) t.tet[locver2org[t.loc][t.ver] + 4]; +} - // Calculate the total length of array 'facesperverlist'. - j = idx2facelist[0]; - idx2facelist[0] = 0; // Array starts from 0 element. - for (i = 0; i < points->items; i++) { - k = idx2facelist[i + 1]; - idx2facelist[i + 1] = idx2facelist[i] + j; - j = k; - } - // The total length is in the last unit of idx2facelist. - facesperverlist = new shellface*[idx2facelist[i]]; - // Loop the set of segments again, set the info. of segments per vertex. - subfaces->traversalinit(); - shloop = shellfacetraverse(subfaces); - while (shloop != (shellface *) NULL) { - for (i = 0; i < 3; i++) { - j = pointmark((point) shloop[3 + i]) - in->firstnumber; - facesperverlist[idx2facelist[j]] = shloop; - idx2facelist[j]++; - } - shloop = shellfacetraverse(subfaces); - } - // Contents in 'idx2facelist' are shifted, now shift them back. - for (i = points->items - 1; i >= 0; i--) { - idx2facelist[i + 1] = idx2facelist[i]; - } - idx2facelist[0] = 0; +inline tetgenmesh::point tetgenmesh::dest(triface& t) { + return (point) t.tet[locver2dest[t.loc][t.ver] + 4]; } -void tetgenmesh::maketetrahedronmap(int*& idx2tetlist, - tetrahedron**& tetsperverlist) -{ - tetrahedron *tetloop; - int i, j, k; +inline tetgenmesh::point tetgenmesh::apex(triface& t) { + return (point) t.tet[locver2apex[t.loc][t.ver] + 4]; +} - if (b->verbose > 1) { - printf(" Constructing mapping from points to tetrahedra.\n"); - } +inline tetgenmesh::point tetgenmesh::oppo(triface& t) { + return (point) t.tet[loc2oppo[t.loc] + 4]; +} - // Create and initialize 'idx2tetlist'. - idx2tetlist = new int[points->items + 1]; - for (i = 0; i < points->items + 1; i++) idx2tetlist[i] = 0; +inline void tetgenmesh::setorg(triface& t, point pointptr) { + t.tet[locver2org[t.loc][t.ver] + 4] = (tetrahedron) pointptr; +} - // Loop the set of tetrahedra once, counter the number of tetrahedra - // sharing each vertex. - tetrahedrons->traversalinit(); - tetloop = tetrahedrontraverse(); - while (tetloop != (tetrahedron *) NULL) { - // Increment the number of sharing tetrahedra for each endpoint. - for (i = 0; i < 4; i++) { - j = pointmark((point) tetloop[4 + i]) - in->firstnumber; - idx2tetlist[j]++; - } - tetloop = tetrahedrontraverse(); - } +inline void tetgenmesh::setdest(triface& t, point pointptr) { + t.tet[locver2dest[t.loc][t.ver] + 4] = (tetrahedron) pointptr; +} - // Calculate the total length of array 'tetsperverlist'. - j = idx2tetlist[0]; - idx2tetlist[0] = 0; // Array starts from 0 element. - for (i = 0; i < points->items; i++) { - k = idx2tetlist[i + 1]; - idx2tetlist[i + 1] = idx2tetlist[i] + j; - j = k; - } - // The total length is in the last unit of idx2tetlist. - tetsperverlist = new tetrahedron*[idx2tetlist[i]]; - // Loop the set of tetrahedra again, set the info. of tet. per vertex. - tetrahedrons->traversalinit(); - tetloop = tetrahedrontraverse(); - while (tetloop != (tetrahedron *) NULL) { - for (i = 0; i < 4; i++) { - j = pointmark((point) tetloop[4 + i]) - in->firstnumber; - tetsperverlist[idx2tetlist[j]] = tetloop; - idx2tetlist[j]++; - } - tetloop = tetrahedrontraverse(); - } - // Contents in 'idx2tetlist' are shifted, now shift them back. - for (i = points->items - 1; i >= 0; i--) { - idx2tetlist[i + 1] = idx2tetlist[i]; - } - idx2tetlist[0] = 0; +inline void tetgenmesh::setapex(triface& t, point pointptr) { + t.tet[locver2apex[t.loc][t.ver] + 4] = (tetrahedron) pointptr; } -/////////////////////////////////////////////////////////////////////////////// -// // -// dummyinit() Initialize the tetrahedron that fills "outer space" and // -// the omnipresent subface. // -// // -// The tetrahedron that fills "outer space" called 'dummytet', is pointed to // -// by every tetrahedron and subface on a boundary (be it outer or inner) of // -// the tetrahedralization. Also, 'dummytet' points to one of the tetrahedron // -// on the convex hull(until the holes and concavities are carved), making it // -// possible to find a starting tetrahedron for point location. // -// // -// The omnipresent subface,'dummysh', is pointed to by every tetrahedron or // -// subface that doesn't have a full complement of real subface to point to. // -// // -/////////////////////////////////////////////////////////////////////////////// +inline void tetgenmesh::setoppo(triface& t, point pointptr) { + t.tet[loc2oppo[t.loc] + 4] = (tetrahedron) pointptr; +} -void tetgenmesh::dummyinit(int tetwords, int shwords) -{ - unsigned long alignptr; +// These primitives were drived from Mucke's triangle-edge data structure +// to change face-edge relation in a tetrahedron (esym, enext and enext2) +// or between two tetrahedra (fnext). - // Set up 'dummytet', the 'tetrahedron' that occupies "outer space". - dummytetbase = (tetrahedron *) new char[tetwords * sizeof(tetrahedron) - + tetrahedrons->alignbytes]; - // Align 'dummytet' on a 'tetrahedrons->alignbytes'-byte boundary. - alignptr = (unsigned long) dummytetbase; - dummytet = (tetrahedron *) - (alignptr + (unsigned long) tetrahedrons->alignbytes - - (alignptr % (unsigned long) tetrahedrons->alignbytes)); - // Initialize the four adjoining tetrahedra to be "outer space". These - // will eventually be changed by various bonding operations, but their - // values don't really matter, as long as they can legally be - // dereferenced. - dummytet[0] = (tetrahedron) dummytet; - dummytet[1] = (tetrahedron) dummytet; - dummytet[2] = (tetrahedron) dummytet; - dummytet[3] = (tetrahedron) dummytet; - // Four null vertex points. - dummytet[4] = (tetrahedron) NULL; - dummytet[5] = (tetrahedron) NULL; - dummytet[6] = (tetrahedron) NULL; - dummytet[7] = (tetrahedron) NULL; +// If e0 = e(i, j), e1 = e(j, i), that is e0 and e1 are the two directions +// of the same undirected edge of a face. e0.sym() = e1 and vice versa. - if (b->useshelles) { - // Set up 'dummysh', the omnipresent "subface" pointed to by any - // tetrahedron side or subface end that isn't attached to a real - // subface. - dummyshbase = (shellface *) new char[shwords * sizeof(shellface) - + subfaces->alignbytes]; - // Align 'dummysh' on a 'subfaces->alignbytes'-byte boundary. - alignptr = (unsigned long) dummyshbase; - dummysh = (shellface *) - (alignptr + (unsigned long) subfaces->alignbytes - - (alignptr % (unsigned long) subfaces->alignbytes)); - // Initialize the three adjoining subfaces to be the omnipresent - // subface. These will eventually be changed by various bonding - // operations, but their values don't really matter, as long as they - // can legally be dereferenced. - dummysh[0] = (shellface) dummysh; - dummysh[1] = (shellface) dummysh; - dummysh[2] = (shellface) dummysh; - // Three null vertex points. - dummysh[3] = (shellface) NULL; - dummysh[4] = (shellface) NULL; - dummysh[5] = (shellface) NULL; - // Initialize the two adjoining tetrahedra to be "outer space". - dummysh[6] = (shellface) dummytet; - dummysh[7] = (shellface) dummytet; - // Initialize the three adjoining subsegments to be "out boundary". - dummysh[8] = (shellface) dummysh; - dummysh[9] = (shellface) dummysh; - dummysh[10] = (shellface) dummysh; - // Initialize the pointer to badface structure. - dummysh[11] = (shellface) NULL; - // Initialize the four adjoining subfaces of 'dummytet' to be the - // omnipresent subface. - dummytet[8 ] = NULL; - dummytet[9 ] = NULL; - } +inline void tetgenmesh::esym(triface& t1, triface& t2) { + t2.tet = t1.tet; + t2.loc = t1.loc; + t2.ver = t1.ver + (EdgeRing(t1.ver) ? -1 : 1); } -/////////////////////////////////////////////////////////////////////////////// -// // -// initializepools() Calculate the sizes of the point, tetrahedron, and // -// subface. Initialize their memory pools. // -// // -// This routine also computes the indices 'pointmarkindex', 'point2simindex',// -// and 'point2pbcptindex' used to find values within each point; computes // -// indices 'highorderindex', 'elemattribindex', and 'volumeboundindex' used // -// to find values within each tetrahedron. // -// // -// There are two types of boundary elements, which are subfaces and subsegs, // -// they are stored in seperate pools. However, the data structures of them // -// are the same. A subsegment can be regarded as a degenerate subface, i.e.,// -// one of its three corners is not used. We set the apex of it be 'NULL' to // -// distinguish it's a subsegment. // -// // -/////////////////////////////////////////////////////////////////////////////// +inline void tetgenmesh::esymself(triface& t) { + t.ver += (EdgeRing(t.ver) ? -1 : 1); +} -void tetgenmesh::initializepools() -{ - enum wordtype wtype; - int pointsize, elesize, shsize; +// If e0 and e1 are both in the same edge ring of a face, e1 = e0.enext(). - // Default checkpbc = 0; - if ((b->plc || b->refine) && (in->pbcgrouplist != NULL)) { - checkpbcs = 1; - } - // Default varconstraint = 0; - if (in->segmentconstraintlist || in->facetconstraintlist) { - varconstraint = 1; - } +inline void tetgenmesh::enext(triface& t1, triface& t2) { + t2.tet = t1.tet; + t2.loc = t1.loc; + t2.ver = ve[t1.ver]; +} - // The index within each point at which its metric tensor is found. It is - // saved directly after the list of point attributes. - pointmtrindex = 3 + in->numberofpointattributes; - // Decide the size (1, 3, or 6) of the metric tensor. - if (b->metric) { - // For '-m' option. A tensor field is provided (*.mtr or *.b.mtr file). - if (bgm != (tetgenmesh *) NULL) { - // A background mesh is allocated. It may not exist though. - sizeoftensor = (bgm->in != (tetgenio *) NULL) ? - bgm->in->numberofpointmtrs : in->numberofpointmtrs; - } else { - // No given background mesh - Itself is a background mesh. - sizeoftensor = in->numberofpointmtrs; - } - // Make sure sizeoftensor is at least 1. - sizeoftensor = (sizeoftensor > 0) ? sizeoftensor : 1; - } else { - // For '-q' option. Make sure to have space for saving a scalar value. - sizeoftensor = b->quality ? 1 : 0; - } - // The index within each point at which an element pointer is found, where - // the index is measured in pointers. Ensure the index is aligned to a - // sizeof(tetrahedron)-byte address. - point2simindex = ((pointmtrindex + sizeoftensor) * sizeof(REAL) - + sizeof(tetrahedron) - 1) / sizeof(tetrahedron); - if (b->plc || b->refine || b->voroout) { - // Increase the point size by four pointers, which are: - // - a pointer to a tet, read by point2tet(); - // - a pointer to a subface, read by point2sh(); - // - a pointer to a subsegment, read by point2seg(); - // - a pointer to a parent point, read by point2ppt()). - if (b->metric) { - // Increase one pointer to a tet of the background mesh. - pointsize = (point2simindex + 5) * sizeof(tetrahedron); - } else { - pointsize = (point2simindex + 4) * 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 one pointer, which is: - // - a pointer to a tet, read by point2tet(); - pointsize = (point2simindex + 1) * sizeof(tetrahedron); - } - // The index within each point at which the boundary marker is found, - // Ensure the point marker is aligned to a sizeof(int)-byte address. - pointmarkindex = (pointsize + sizeof(int) - 1) / sizeof(int); - // Now point size is the ints (inidcated by pointmarkindex) plus: - // - an integer for boundary marker; - // - an integer for vertex type; - pointsize = (pointmarkindex + 2) * sizeof(int); - // Decide the wordtype used in vertex pool. - wtype = (sizeof(REAL) >= sizeof(tetrahedron)) ? FLOATINGPOINT : POINTER; - // Initialize the pool of vertices. - points = new memorypool(pointsize, VERPERBLOCK, wtype, 0); +inline void tetgenmesh::enextself(triface& t) { + t.ver = ve[t.ver]; +} - if (b->useshelles) { - dummypoint = (point) new char[pointsize]; // For abovepoint. - } +// enext2() is equal to e2 = e0.enext().enext() - // The number of bytes occupied by a tetrahedron. There are four pointers - // to other tetrahedra, four pointers to corners, and possibly four - // pointers to subfaces (or six pointers to subsegments (used in - // segment recovery only)). - elesize = (8 + b->useshelles * 2) * sizeof(tetrahedron); - // If Voronoi diagram is wanted, make sure we have additional space. - if (b->voroout) { - elesize = (8 + 4) * sizeof(tetrahedron); - } - // 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); - // 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) { - elesize = volumeboundindex * sizeof(REAL); - } - // If element neighbor graph is requested (-n switch), an additional - // integer is allocated for each element. - elemmarkerindex = (elesize + sizeof(int) - 1) / sizeof(int); - // if (b->neighout || b->voroout) { - elesize = (elemmarkerindex + 1) * sizeof(int); - // } - // If -o2 switch is used, an additional pointer pointed to the list of - // higher order nodes is allocated for each element. - highorderindex = (elesize + sizeof(tetrahedron) - 1) / sizeof(tetrahedron); - if (b->order == 2) { - elesize = (highorderindex + 1) * sizeof(tetrahedron); - } - // Having determined the memory size of an element, initialize the pool. - tetrahedrons = new memorypool(elesize, ELEPERBLOCK, POINTER, 8); +inline void tetgenmesh::enext2(triface& t1, triface& t2) { + t2.tet = t1.tet; + t2.loc = t1.loc; + t2.ver = ve[ve[t1.ver]]; +} - if (b->useshelles) { - // The number of bytes occupied by a subface. The list of pointers - // stored in a subface are: three to other subfaces, three to corners, - // three to subsegments, two to tetrahedra, and one to a badface. - shsize = 12 * sizeof(shellface); - // The index within each subface at which the maximum area bound is - // found, where the index is measured in REALs. - areaboundindex = (shsize + sizeof(REAL) - 1) / sizeof(REAL); - // If -q switch is in use, increase the number of bytes occupied by - // a subface for saving maximum area bound. - if (b->quality && varconstraint) { - shsize = (areaboundindex + 1) * sizeof(REAL); - } else { - shsize = areaboundindex * sizeof(REAL); - } - // The index within subface at which the facet marker is found. Ensure - // the marker is aligned to a sizeof(int)-byte address. - 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(int); - // 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, SUBPERBLOCK, POINTER, 8); - // Initialize the pool of subsegments. The subsegment's record is same - // with subface. - subsegs = new memorypool(shsize, SUBPERBLOCK, POINTER, 8); - // Initialize the pool for tet-subseg connections. - tet2segpool = new memorypool(6*sizeof(shellface), SUBPERBLOCK, POINTER, 0); - // Initialize the pool for tet-subface connections. - tet2subpool = new memorypool(4*sizeof(shellface), SUBPERBLOCK, POINTER, 0); - // Initialize arraypools for segment & facet recovery. - subsegstack = new arraypool(sizeof(face), 10); - subfacstack = new arraypool(sizeof(face), 10); - // Initialize the "outer space" tetrahedron and omnipresent subface. - dummyinit(tetrahedrons->itemwords, subfaces->itemwords); - } else { - // Initialize the "outer space" tetrahedron. - dummyinit(tetrahedrons->itemwords, 0); - } +inline void tetgenmesh::enext2self(triface& t) { + t.ver = ve[ve[t.ver]]; } -/////////////////////////////////////////////////////////////////////////////// -// // -// tetrahedrondealloc() Deallocate space for a tet., marking it dead. // -// // -/////////////////////////////////////////////////////////////////////////////// +// If f0 and f1 are both in the same face ring of a face, f1 = f0.fnext(). +// If f1 exists, return true. Otherwise, return false, i.e., f0 is a +// boundary or hull face. -void tetgenmesh::tetrahedrondealloc(tetrahedron *dyingtetrahedron) +inline bool tetgenmesh::fnext(triface& t1, triface& t2) { - // Set tetrahedron's vertices to NULL. This makes it possible to detect - // dead tetrahedra when traversing the list of all tetrahedra. - dyingtetrahedron[4] = (tetrahedron) NULL; - // dyingtetrahedron[5] = (tetrahedron) NULL; - // dyingtetrahedron[6] = (tetrahedron) NULL; - dyingtetrahedron[7] = (tetrahedron) NULL; - - 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]); + // Get the next face. + t2.loc = locver2nextf[t1.loc][t1.ver][0]; + // Is the next face in the same tet? + if (t2.loc != -1) { + // It's in the same tet. Get the edge version. + t2.ver = locver2nextf[t1.loc][t1.ver][1]; + t2.tet = t1.tet; + } else { + // The next face is in the neigbhour of 't1'. + sym(t1, t2); + if (t2.tet != dummytet) { + // Find the corresponding edge in t2. + point torg; + int tloc, tver, i; + t2.ver = 0; + torg = org(t1); + for (i = 0; (i < 3) && (org(t2) != torg); i++) { + enextself(t2); + } + // Go to the next face in t2. + tloc = t2.loc; + tver = t2.ver; + t2.loc = locver2nextf[tloc][tver][0]; + t2.ver = locver2nextf[tloc][tver][1]; + } + } + return t2.tet != dummytet; +} + +inline bool tetgenmesh::fnextself(triface& t1) +{ + triface t2; + + // Get the next face. + t2.loc = locver2nextf[t1.loc][t1.ver][0]; + // Is the next face in the same tet? + if (t2.loc != -1) { + // It's in the same tet. Get the edge version. + t2.ver = locver2nextf[t1.loc][t1.ver][1]; + t1.loc = t2.loc; + t1.ver = t2.ver; + } else { + // The next face is in the neigbhour of 't1'. + sym(t1, t2); + if (t2.tet != dummytet) { + // Find the corresponding edge in t2. + point torg; + int i; + t2.ver = 0; + torg = org(t1); + for (i = 0; (i < 3) && (org(t2) != torg); i++) { + enextself(t2); + } + t1.loc = locver2nextf[t2.loc][t2.ver][0]; + t1.ver = locver2nextf[t2.loc][t2.ver][1]; + t1.tet = t2.tet; } } + return t2.tet != dummytet; +} - tetrahedrons->dealloc((void *) dyingtetrahedron); +// enextfnext() and enext2fnext() are combination primitives of enext(), +// enext2() and fnext(). + +inline void tetgenmesh::enextfnext(triface& t1, triface& t2) { + enext(t1, t2); + fnextself(t2); } -/////////////////////////////////////////////////////////////////////////////// -// // -// tetrahedrontraverse() Traverse the tetrahedra, skipping dead ones. // -// // -/////////////////////////////////////////////////////////////////////////////// +inline void tetgenmesh::enextfnextself(triface& t) { + enextself(t); + fnextself(t); +} -tetgenmesh::tetrahedron* tetgenmesh::tetrahedrontraverse() -{ - tetrahedron *newtetrahedron; +inline void tetgenmesh::enext2fnext(triface& t1, triface& t2) { + enext2(t1, t2); + fnextself(t2); +} - do { - newtetrahedron = (tetrahedron *) tetrahedrons->traverse(); - if (newtetrahedron == (tetrahedron *) NULL) { - return (tetrahedron *) NULL; - } - } while (newtetrahedron[7] == (tetrahedron) NULL); // Skip dead ones. - return newtetrahedron; +inline void tetgenmesh::enext2fnextself(triface& t) { + enext2self(t); + fnextself(t); } -/////////////////////////////////////////////////////////////////////////////// -// // -// shellfacedealloc() Deallocate space for a shellface, marking it dead. // -// Used both for dealloc a subface and subsegment. // -// // -/////////////////////////////////////////////////////////////////////////////// +// Primitives to infect or cure a tetrahedron with the virus. The last +// third bit of the pointer is marked for infection. These rely on the +// assumption that all tetrahedron are aligned to eight-byte boundaries. -void tetgenmesh::shellfacedealloc(memorypool *pool, shellface *dyingsh) -{ - // Set shellface's vertices to NULL. This makes it possible to detect dead - // shellfaces when traversing the list of all shellfaces. - dyingsh[3] = (shellface) NULL; - dyingsh[4] = (shellface) NULL; - dyingsh[5] = (shellface) NULL; - pool->dealloc((void *) dyingsh); +inline void tetgenmesh::infect(triface& t) { + t.tet[0] = (tetrahedron) ((unsigned long) t.tet[0] | (unsigned long) 4l); } -/////////////////////////////////////////////////////////////////////////////// -// // -// shellfacetraverse() Traverse the subfaces, skipping dead ones. Used // -// for both subfaces and subsegments pool traverse. // -// // -/////////////////////////////////////////////////////////////////////////////// +inline void tetgenmesh::uninfect(triface& t) { + t.tet[0] = (tetrahedron) ((unsigned long) t.tet[0] & ~ (unsigned long) 4l); +} -tetgenmesh::shellface* tetgenmesh::shellfacetraverse(memorypool *pool) -{ - shellface *newshellface; +// Test a tetrahedron for viral infection. - do { - newshellface = (shellface *) pool->traverse(); - if (newshellface == (shellface *) NULL) { - return (shellface *) NULL; - } - } while (newshellface[3] == (shellface) NULL); // Skip dead ones. - return newshellface; +inline bool tetgenmesh::infected(triface& t) { + return (((unsigned long) t.tet[0] & (unsigned long) 4l) != 0); } -/////////////////////////////////////////////////////////////////////////////// -// // -// badfacedealloc() Deallocate space for a badface, marking it dead. // -// // -/////////////////////////////////////////////////////////////////////////////// +// Check or set a tetrahedron's attributes. -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); +inline REAL tetgenmesh::elemattribute(tetrahedron* ptr, int attnum) { + return ((REAL *) (ptr))[elemattribindex + attnum]; } -/////////////////////////////////////////////////////////////////////////////// -// // -// badfacetraverse() Traverse the pools, skipping dead ones. // -// // -/////////////////////////////////////////////////////////////////////////////// +inline void tetgenmesh:: +setelemattribute(tetrahedron* ptr, int attnum, REAL value){ + ((REAL *) (ptr))[elemattribindex + attnum] = value; +} -tetgenmesh::badface* tetgenmesh::badfacetraverse(memorypool *pool) -{ - badface *newsh; +// Check or set a tetrahedron's maximum volume bound. - do { - newsh = (badface *) pool->traverse(); - if (newsh == (badface *) NULL) { - return (badface *) NULL; - } - } while (newsh->forg == (point) NULL); // Skip dead ones. - return newsh; +inline REAL tetgenmesh::volumebound(tetrahedron* ptr) { + return ((REAL *) (ptr))[volumeboundindex]; } -/////////////////////////////////////////////////////////////////////////////// -// // -// pointdealloc() Deallocate space for a point, marking it dead. // -// // -/////////////////////////////////////////////////////////////////////////////// +inline void tetgenmesh::setvolumebound(tetrahedron* ptr, REAL value) { + ((REAL *) (ptr))[volumeboundindex] = value; +} -void tetgenmesh::pointdealloc(point dyingpoint) -{ - // Mark the point as dead. This makes it possible to detect dead points - // when traversing the list of all points. - setpointtype(dyingpoint, DEADVERTEX); - points->dealloc((void *) dyingpoint); +// +// End of primitives for tetrahedra +// + +// +// Begin of primitives for subfaces/subsegments +// + +// Each subface contains three pointers to its neighboring subfaces, with +// edge versions. To save memory, both information are kept in a single +// pointer. To make this possible, all subfaces are aligned to eight-byte +// boundaries, so that the last three bits of each pointer are zeros. An +// edge version (in the range 0 to 5) is compressed into the last three +// bits of each pointer by 'sencode()'. 'sdecode()' decodes a pointer, +// extracting an edge version and a pointer to the beginning of a subface. + +inline void tetgenmesh::sdecode(shellface sptr, face& s) { + s.shver = (int) ((unsigned long) (sptr) & (unsigned long) 7l); + s.sh = (shellface *) ((unsigned long) (sptr) & ~ (unsigned long) 7l); } -/////////////////////////////////////////////////////////////////////////////// -// // -// pointtraverse() Traverse the points, skipping dead ones. // -// // -/////////////////////////////////////////////////////////////////////////////// +inline tetgenmesh::shellface tetgenmesh::sencode(face& s) { + return (shellface) ((unsigned long) s.sh | (unsigned long) s.shver); +} -tetgenmesh::point tetgenmesh::pointtraverse() -{ - point newpoint; +// spivot() finds the other subface (from this subface) that shares the +// same edge. - do { - newpoint = (point) points->traverse(); - if (newpoint == (point) NULL) { - return (point) NULL; - } - } while (pointtype(newpoint) == DEADVERTEX); // Skip dead ones. - return newpoint; +inline void tetgenmesh::spivot(face& s1, face& s2) { + shellface sptr = s1.sh[Orient(s1.shver)]; + sdecode(sptr, s2); } -/////////////////////////////////////////////////////////////////////////////// -// // -// maketetrahedron() Create a new tetrahedron. // -// // -/////////////////////////////////////////////////////////////////////////////// +inline void tetgenmesh::spivotself(face& s) { + shellface sptr = s.sh[Orient(s.shver)]; + sdecode(sptr, s); +} -void tetgenmesh::maketetrahedron(triface *newtet) -{ - newtet->tet = (tetrahedron *) tetrahedrons->alloc(); - // Initialize the four adjoining tetrahedra to be "outer space". - newtet->tet[0] = (tetrahedron) dummytet; - newtet->tet[1] = (tetrahedron) dummytet; - newtet->tet[2] = (tetrahedron) dummytet; - newtet->tet[3] = (tetrahedron) dummytet; - // Four NULL vertices. - newtet->tet[4] = (tetrahedron) NULL; - newtet->tet[5] = (tetrahedron) NULL; - newtet->tet[6] = (tetrahedron) NULL; - newtet->tet[7] = (tetrahedron) NULL; - // Initialize the four adjoining subfaces to be the omnipresent subface. - if (b->useshelles) { - newtet->tet[8 ] = NULL; - newtet->tet[9 ] = NULL; - } - for (int i = 0; i < in->numberoftetrahedronattributes; i++) { - setelemattribute(newtet->tet, i, 0.0); - } - if (b->varvolume) { - setvolumebound(newtet->tet, -1.0); - } - // Initialize the marker (for flags). - setelemmarker(newtet->tet, 0); - // Initialize the location and version to be Zero. - newtet->loc = 0; - newtet->ver = 0; +// sbond() bonds two subfaces together, i.e., after bonding, both faces +// are pointing to each other. + +inline void tetgenmesh::sbond(face& s1, face& s2) { + s1.sh[Orient(s1.shver)] = sencode(s2); + s2.sh[Orient(s2.shver)] = sencode(s1); } -/////////////////////////////////////////////////////////////////////////////// -// // -// makeshellface() Create a new shellface with version zero. Used for // -// both subfaces and seusegments. // -// // -/////////////////////////////////////////////////////////////////////////////// +// sbond1() only bonds s2 to s1, i.e., after bonding, s1 is pointing to s2, +// but s2 is not pointing to s1. -void tetgenmesh::makeshellface(memorypool *pool, face *newface) +inline void tetgenmesh::sbond1(face& s1, face& s2) { + s1.sh[Orient(s1.shver)] = sencode(s2); +} + +// Dissolve a subface bond (from one side). Note that the other subface +// will still think it's connected to this subface. + +inline void tetgenmesh::sdissolve(face& s) { + s.sh[Orient(s.shver)] = (shellface) dummysh; +} + +// These primitives determine or set the origin, destination, or apex +// of a subface with respect to the edge version. + +inline tetgenmesh::point tetgenmesh::sorg(face& s) { + return (point) s.sh[3 + vo[s.shver]]; +} + +inline tetgenmesh::point tetgenmesh::sdest(face& s) { + return (point) s.sh[3 + vd[s.shver]]; +} + +inline tetgenmesh::point tetgenmesh::sapex(face& s) { + return (point) s.sh[3 + va[s.shver]]; +} + +inline void tetgenmesh::setsorg(face& s, point pointptr) { + s.sh[3 + vo[s.shver]] = (shellface) pointptr; +} + +inline void tetgenmesh::setsdest(face& s, point pointptr) { + s.sh[3 + vd[s.shver]] = (shellface) pointptr; +} + +inline void tetgenmesh::setsapex(face& s, point pointptr) { + s.sh[3 + va[s.shver]] = (shellface) pointptr; +} + +// These primitives were drived from Mucke[2]'s triangle-edge data structure +// to change face-edge relation in a subface (sesym, senext and senext2). + +inline void tetgenmesh::sesym(face& s1, face& s2) { + s2.sh = s1.sh; + s2.shver = s1.shver + (EdgeRing(s1.shver) ? -1 : 1); +} + +inline void tetgenmesh::sesymself(face& s) { + s.shver += (EdgeRing(s.shver) ? -1 : 1); +} + +inline void tetgenmesh::senext(face& s1, face& s2) { + s2.sh = s1.sh; + s2.shver = ve[s1.shver]; +} + +inline void tetgenmesh::senextself(face& s) { + s.shver = ve[s.shver]; +} + +inline void tetgenmesh::senext2(face& s1, face& s2) { + s2.sh = s1.sh; + s2.shver = ve[ve[s1.shver]]; +} + +inline void tetgenmesh::senext2self(face& s) { + s.shver = ve[ve[s.shver]]; +} + +// If f0 and f1 are both in the same face ring, then f1 = f0.fnext(), + +inline void tetgenmesh::sfnext(face& s1, face& s2) { + getnextsface(&s1, &s2); +} + +inline void tetgenmesh::sfnextself(face& s) { + getnextsface(&s, NULL); +} + +// These primitives read or set a pointer of the badface structure. The +// pointer is stored sh[11]. + +inline tetgenmesh::badface* tetgenmesh::shell2badface(face& s) { + return (badface*) s.sh[11]; +} + +inline void tetgenmesh::setshell2badface(face& s, badface* value) { + s.sh[11] = (shellface) value; +} + +// Check or set a subface's maximum area bound. + +inline REAL tetgenmesh::areabound(face& s) { + return ((REAL *) (s.sh))[areaboundindex]; +} + +inline void tetgenmesh::setareabound(face& s, REAL value) { + ((REAL *) (s.sh))[areaboundindex] = value; +} + +// These two primitives read or set a shell marker. Shell markers are used +// to hold user boundary information. + +inline int tetgenmesh::shellmark(face& s) { + return ((int *) (s.sh))[shmarkindex]; +} + +inline void tetgenmesh::setshellmark(face& s, int value) { + ((int *) (s.sh))[shmarkindex] = value; +} + +// These two primitives set or read the type of the subface or subsegment. + +inline enum tetgenmesh::shestype tetgenmesh::shelltype(face& s) { + return (enum shestype) ((int *) (s.sh))[shmarkindex + 1]; +} + +inline void tetgenmesh::setshelltype(face& s, enum shestype value) { + ((int *) (s.sh))[shmarkindex + 1] = (int) value; +} + +// 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; +} + +// Primitives to infect or cure a subface with the virus. These rely on the +// assumption that all tetrahedra are aligned to eight-byte boundaries. + +inline void tetgenmesh::sinfect(face& s) { + s.sh[6] = (shellface) ((unsigned long) s.sh[6] | (unsigned long) 4l); +} + +inline void tetgenmesh::suninfect(face& s) { + s.sh[6] = (shellface)((unsigned long) s.sh[6] & ~(unsigned long) 4l); +} + +// Test a subface for viral infection. + +inline bool tetgenmesh::sinfected(face& s) { + return (((unsigned long) s.sh[6] & (unsigned long) 4l) != 0); +} + +// +// End of primitives for subfaces/subsegments +// + +// +// Begin of primitives for interacting between tetrahedra and subfaces +// + +// tspivot() finds a subface abutting on this tetrahdera. + +inline void tetgenmesh::tspivot(triface& t, face& s) { + shellface sptr = (shellface) t.tet[8 + t.loc]; + sdecode(sptr, s); +} + +// stpivot() finds a tetrahedron abutting a subface. + +inline void tetgenmesh::stpivot(face& s, triface& t) { + tetrahedron ptr = (tetrahedron) s.sh[6 + EdgeRing(s.shver)]; + decode(ptr, t); +} + +// tsbond() bond a tetrahedron to a subface. + +inline void tetgenmesh::tsbond(triface& t, face& s) { + t.tet[8 + t.loc] = (tetrahedron) sencode(s); + s.sh[6 + EdgeRing(s.shver)] = (shellface) encode(t); +} + +// tsdissolve() dissolve a bond (from the tetrahedron side). + +inline void tetgenmesh::tsdissolve(triface& t) { + t.tet[8 + t.loc] = (tetrahedron) dummysh; +} + +// stdissolve() dissolve a bond (from the subface side). + +inline void tetgenmesh::stdissolve(face& s) { + s.sh[6 + EdgeRing(s.shver)] = (shellface) dummytet; +} + +// +// End of primitives for interacting between tetrahedra and subfaces +// + +// +// Begin of primitives for interacting between subfaces and subsegs +// + +// sspivot() finds a subsegment abutting a subface. + +inline void tetgenmesh::sspivot(face& s, face& edge) { + shellface sptr = (shellface) s.sh[8 + Orient(s.shver)]; + sdecode(sptr, edge); +} + +// ssbond() bond a subface to a subsegment. + +inline void tetgenmesh::ssbond(face& s, face& edge) { + s.sh[8 + Orient(s.shver)] = sencode(edge); + edge.sh[0] = sencode(s); +} + +// ssdisolve() dissolve a bond (from the subface side) + +inline void tetgenmesh::ssdissolve(face& s) { + s.sh[8 + Orient(s.shver)] = (shellface) dummysh; +} + +// +// End of primitives for interacting between subfaces and subsegs +// + +// +// Begin of primitives for interacting between tet and subsegs. +// + +inline void tetgenmesh::tsspivot1(triface& t, face& seg) { - newface->sh = (shellface *) pool->alloc(); - //Initialize the three adjoining subfaces to be the omnipresent subface. - newface->sh[0] = (shellface) dummysh; - newface->sh[1] = (shellface) dummysh; - newface->sh[2] = (shellface) dummysh; - // Three NULL vertices. - newface->sh[3] = (shellface) NULL; - newface->sh[4] = (shellface) NULL; - newface->sh[5] = (shellface) NULL; - // Initialize the two adjoining tetrahedra to be "outer space". - newface->sh[6] = (shellface) dummytet; - newface->sh[7] = (shellface) dummytet; - // Initialize the three adjoining subsegments to be the omnipresent - // subsegments. - newface->sh [8] = (shellface) dummysh; - newface->sh [9] = (shellface) dummysh; - newface->sh[10] = (shellface) dummysh; - // Initialize the pointer to badface structure. - newface->sh[11] = (shellface) NULL; - if (b->quality && varconstraint) { - // Initialize the maximum area bound. - setareabound(*newface, 0.0); - } - // Clear the infection and marktest bits. - suninfect(*newface); - sunmarktest(*newface); - // Set the boundary marker to zero. - setshellmark(*newface, 0); - // Set the type. - setshelltype(*newface, NSHARP); - if (checkpbcs) { - // Set the pbcgroup be ivalid. - setshellpbcgroup(*newface, -1); - } - // Initialize the version to be Zero. - newface->shver = 0; + shellface sptr = (shellface) t.tet[8 + locver2edge[t.loc][t.ver]]; + sdecode(sptr, seg); } -/////////////////////////////////////////////////////////////////////////////// -// // -// makepoint() Create a new point. // -// // -/////////////////////////////////////////////////////////////////////////////// +// Only bond/dissolve at tet's side, but not vice versa. -void tetgenmesh::makepoint(point* pnewpoint) +inline void tetgenmesh::tssbond1(triface& t, face& seg) { - int ptmark, i; + t.tet[8 + locver2edge[t.loc][t.ver]] = (tetrahedron) sencode(seg); +} - *pnewpoint = (point) points->alloc(); - // Initialize three coordinates. - (*pnewpoint)[0] = 0.0; - (*pnewpoint)[1] = 0.0; - (*pnewpoint)[2] = 0.0; - // Initialize the list of user-defined attributes. - for (i = 0; i < in->numberofpointattributes; i++) { - (*pnewpoint)[3 + i] = 0.0; - } - // Initialize the metric tensor. - for (i = 0; i < sizeoftensor; i++) { - (*pnewpoint)[pointmtrindex + i] = 0.0; +inline void tetgenmesh::tssdissolve1(triface& t) +{ + t.tet[8 + locver2edge[t.loc][t.ver]] = (tetrahedron) dummysh; +} + +// +// End of primitives for interacting between tet and subsegs. +// + +// +// Begin of primitives for points +// + +inline int tetgenmesh::pointmark(point pt) { + return ((int *) (pt))[pointmarkindex]; +} + +inline void tetgenmesh::setpointmark(point pt, int value) { + ((int *) (pt))[pointmarkindex] = value; +} + +// These two primitives set and read the type of the point. + +inline enum tetgenmesh::verttype tetgenmesh::pointtype(point pt) { + return (enum verttype) ((int *) (pt))[pointmarkindex + 1]; +} + +inline void tetgenmesh::setpointtype(point pt, enum verttype value) { + ((int *) (pt))[pointmarkindex + 1] = (int) value; +} + +// These following primitives set and read a pointer to a tetrahedron +// a subface/subsegment, a point, or a tet of background mesh. + +inline tetgenmesh::tetrahedron tetgenmesh::point2tet(point pt) { + return ((tetrahedron *) (pt))[point2simindex]; +} + +inline void tetgenmesh::setpoint2tet(point pt, tetrahedron value) { + ((tetrahedron *) (pt))[point2simindex] = value; +} + +inline tetgenmesh::shellface tetgenmesh::point2sh(point pt) { + return (shellface) ((tetrahedron *) (pt))[point2simindex + 1]; +} + +inline void tetgenmesh::setpoint2sh(point pt, shellface value) { + ((tetrahedron *) (pt))[point2simindex + 1] = (tetrahedron) value; +} + +inline tetgenmesh::point tetgenmesh::point2ppt(point pt) { + return (point) ((tetrahedron *) (pt))[point2simindex + 2]; +} + +inline void tetgenmesh::setpoint2ppt(point pt, point value) { + ((tetrahedron *) (pt))[point2simindex + 2] = (tetrahedron) value; +} + +inline tetgenmesh::tetrahedron tetgenmesh::point2bgmtet(point pt) { + return ((tetrahedron *) (pt))[point2simindex + 3]; +} + +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; +} + +// +// End of primitives for points +// + +// +// Begin of advanced primitives +// + +// adjustedgering() adjusts the edge version so that it belongs to the +// indicated edge ring. The 'direction' only can be 0(CCW) or 1(CW). +// If the edge is not in the wanted edge ring, reverse it. + +inline void tetgenmesh::adjustedgering(triface& t, int direction) { + if (EdgeRing(t.ver) != direction) { + esymself(t); } - if (b->plc || b->refine) { - // Initialize the point-to-simplex filed. - setpoint2tet(*pnewpoint, NULL); - setpoint2sh(*pnewpoint, NULL); - setpoint2seg(*pnewpoint, NULL); - setpoint2ppt(*pnewpoint, NULL); - if (b->metric) { - setpoint2bgmtet(*pnewpoint, NULL); - } - if (checkpbcs) { - // Initialize the other pointer to its pbc point. - setpoint2pbcpt(*pnewpoint, NULL); - } +} + +inline void tetgenmesh::adjustedgering(face& s, int direction) { + if (EdgeRing(s.shver) != direction) { + sesymself(s); } - // Initialize the point marker (starting from in->firstnumber). - ptmark = (int) points->items - (in->firstnumber == 1 ? 0 : 1); - setpointmark(*pnewpoint, ptmark); - // Initialize the point type. - setpointtype(*pnewpoint, UNUSEDVERTEX); - // Clear the infection flag. (Thanks to Bill Jones who discovered this bug. - // 2009-12-11) - puninfect(*pnewpoint); } -//// //// -//// //// -//// mempool_cxx ////////////////////////////////////////////////////////////// +// isdead() returns TRUE if the tetrahedron or subface has been dealloced. + +inline bool tetgenmesh::isdead(triface* t) { + if (t->tet == (tetrahedron *) NULL) return true; + else return t->tet[4] == (tetrahedron) NULL; +} + +inline bool tetgenmesh::isdead(face* s) { + if (s->sh == (shellface *) NULL) return true; + else return s->sh[3] == (shellface) NULL; +} + +// isfacehaspoint() returns TRUE if the 'testpoint' is one of the vertices +// of the tetface 't' subface 's'. + +inline bool tetgenmesh::isfacehaspoint(triface* t, point testpoint) { + return ((org(*t) == testpoint) || (dest(*t) == testpoint) || + (apex(*t) == testpoint)); +} + +inline bool tetgenmesh::isfacehaspoint(face* s, point testpoint) { + return (s->sh[3] == (shellface) testpoint) || + (s->sh[4] == (shellface) testpoint) || + (s->sh[5] == (shellface) testpoint); +} + +// isfacehasedge() returns TRUE if the edge (given by its two endpoints) is +// one of the three edges of the subface 's'. -//// geom_cxx ///////////////////////////////////////////////////////////////// -//// //// -//// //// +inline bool tetgenmesh::isfacehasedge(face* s, point tend1, point tend2) { + return (isfacehaspoint(s, tend1) && isfacehaspoint(s, tend2)); +} -// PI is the ratio of a circle's circumference to its diameter. +// issymexist() returns TRUE if the adjoining tetrahedron is not 'duumytet'. -REAL tetgenmesh::PI = 3.14159265358979323846264338327950288419716939937510582; +inline bool tetgenmesh::issymexist(triface* t) { + tetrahedron *ptr = (tetrahedron *) + ((unsigned long)(t->tet[t->loc]) & ~(unsigned long)7l); + return ptr != dummytet; +} /////////////////////////////////////////////////////////////////////////////// // // -// Triangle-triangle intersection test // +// getnextsface() Finds the next subface in the face ring. // // // -// The triangle-triangle intersection test is implemented with exact arithm- // -// etic. It exactly tells whether or not two triangles in three dimensions // -// intersect. Before implementing this test myself, I tried two C codes // -// (implemented by Thomas Moeller and Philippe Guigue, respectively), which // -// are all public available. However both of them failed frequently. Another // -// unconvenience is both codes only tell whether or not the two triangles // -// intersect without distinguishing the cases whether they exactly intersect // -// in interior or they just share a vertex or share an edge. The two latter // -// cases are acceptable and should return not intersection in TetGen. // +// For saving space in the data structure of subface, there only exists one // +// face ring around a segment (see programming manual). This routine imple- // +// ments the double face ring as desired in Muecke's data structure. // // // /////////////////////////////////////////////////////////////////////////////// -// All the following routines require the input objects are not degenerate. -// i.e., a triangle must has three non-collinear corners; an edge must -// has two identical endpoints. Degenerate cases should have to detect -// first and then handled as special cases. +void tetgenmesh::getnextsface(face* s1, face* s2) +{ + face neighsh, spinsh; + face testseg; + + sspivot(*s1, testseg); + if (testseg.sh != dummysh) { + testseg.shver = 0; + if (sorg(testseg) == sorg(*s1)) { + spivot(*s1, neighsh); + } else { + spinsh = *s1; + do { + neighsh = spinsh; + spivotself(spinsh); + } while (spinsh.sh != s1->sh); + } + } else { + spivot(*s1, neighsh); + } + if (sorg(neighsh) != sorg(*s1)) { + sesymself(neighsh); + } + if (s2 != (face *) NULL) { + *s2 = neighsh; + } else { + *s1 = neighsh; + } +} /////////////////////////////////////////////////////////////////////////////// // // -// edge_vert_col_inter() Test whether an edge (ab) and a collinear vertex // -// (p) are intersecting or not. // +// tsspivot() Finds a subsegment abutting on a tetrahderon's edge. // // // -// Possible cases are p is coincident to a (p = a), or to b (p = b), or p is // -// inside ab (a < p < b), or outside ab (p < a or p > b). These cases can be // -// quickly determined by comparing the corresponding coords of a, b, and p // -// (which are not all equal). // +// The edge is represented in the primary edge of 'checkedge'. If there is a // +// subsegment bonded at this edge, it is returned in handle 'checkseg', the // +// edge direction of 'checkseg' is conformed to 'checkedge'. If there isn't, // +// set 'checkseg.sh = dummysh' to indicate it is not a subsegment. // // // -// The return value indicates one of the three cases: DISJOINT, SHAREVERTEX // -// (p = a or p = b), and INTERSECT (a < p < b). // +// To find whether an edge of a tetrahedron is a subsegment or not. First we // +// need find a subface around this edge to see if it contains a subsegment. // +// The reason is there is no direct connection between a tetrahedron and its // +// adjoining subsegments. // // // /////////////////////////////////////////////////////////////////////////////// -enum tetgenmesh::interresult tetgenmesh::edge_vert_col_inter(REAL* A, REAL* B, - REAL* P) +void tetgenmesh::tsspivot(triface* checkedge, face* checkseg) { - int i = 0; + triface spintet; + face parentsh; + point tapex; + int hitbdry; + + spintet = *checkedge; + tapex = apex(*checkedge); + hitbdry = 0; do { - if (A[i] < B[i]) { - if (P[i] < A[i]) { - return DISJOINT; - } else if (P[i] > A[i]) { - if (P[i] < B[i]) { - return INTERSECT; - } else if (P[i] > B[i]) { - return DISJOINT; - } else { - // assert(P[i] == B[i]); - return SHAREVERTEX; + tspivot(spintet, parentsh); + // Does spintet have a (non-fake) subface attached? + if ((parentsh.sh != dummysh) && (sapex(parentsh) != NULL)) { + // Find a subface! Find the edge in it. + findedge(&parentsh, org(*checkedge), dest(*checkedge)); + sspivot(parentsh, *checkseg); + if (checkseg->sh != dummysh) { + // Find a subsegment! Correct its edge direction before return. + if (sorg(*checkseg) != org(*checkedge)) { + sesymself(*checkseg); } - } else { - // assert(P[i] == A[i]); - return SHAREVERTEX; } - } else if (A[i] > B[i]) { - if (P[i] < B[i]) { - return DISJOINT; - } else if (P[i] > B[i]) { - if (P[i] < A[i]) { - return INTERSECT; - } else if (P[i] > A[i]) { - return DISJOINT; - } else { - // assert(P[i] == A[i]); - return SHAREVERTEX; + return; + } + if (!fnextself(spintet)) { + hitbdry++; + if (hitbdry < 2) { + esym(*checkedge, spintet); + if (!fnextself(spintet)) { + hitbdry++; } - } else { - // assert(P[i] == B[i]); - return SHAREVERTEX; } } - // i-th coordinates are equal, try i+1-th; - i++; - } while (i < 3); - // Should never be here. - return DISJOINT; + } while ((apex(spintet) != tapex) && (hitbdry < 2)); + // Not find. + checkseg->sh = dummysh; } /////////////////////////////////////////////////////////////////////////////// // // -// edge_edge_cop_inter() Test whether two coplanar edges (ab, and pq) are // -// intersecting or not. // -// // -// Possible cases are ab and pq are disjointed, or proper intersecting (int- // -// ersect at a point other than their endpoints), or both collinear and int- // -// ersecting, or sharing at a common endpoint, or are coincident. // -// // -// A reference point R is required, which is exactly not coplanar with these // -// two edges. Since the caller knows these two edges are coplanar, it must // -// be able to provide (or calculate) such a point. // +// sstpivot() Finds a tetrahedron abutting a subsegment. // // // -// The return value indicates one of the four cases: DISJOINT, SHAREVERTEX, // -// SHAREEDGE, and INTERSECT. // +// This is the inverse operation of 'tsspivot()'. One subsegment shared by // +// arbitrary number of tetrahedron, the returned tetrahedron is not unique. // +// The edge direction of the returned tetrahedron is conformed to the given // +// subsegment. // // // /////////////////////////////////////////////////////////////////////////////// -enum tetgenmesh::interresult tetgenmesh:: edge_edge_cop_inter(REAL* A, REAL* B, - REAL* P, REAL* Q, REAL* R) +void tetgenmesh::sstpivot(face* checkseg, triface* retedge) { - REAL s1, s2, s3, s4; - -#ifdef SELF_CHECK - assert(R != NULL); -#endif - s1 = orient3d(A, B, R, P); - s2 = orient3d(A, B, R, Q); - if (s1 * s2 > 0.0) { - // Both p and q are at the same side of ab. - return DISJOINT; - } - s3 = orient3d(P, Q, R, A); - s4 = orient3d(P, Q, R, B); - if (s3 * s4 > 0.0) { - // Both a and b are at the same side of pq. - return DISJOINT; - } - - // Possible degenerate cases are: - // (1) Only one of p and q is collinear with ab; - // (2) Both p and q are collinear with ab; - // (3) Only one of a and b is collinear with pq. - enum interresult abp, abq; - enum interresult pqa, pqb; - - if (s1 == 0.0) { - // p is collinear with ab. - abp = edge_vert_col_inter(A, B, P); - if (abp == INTERSECT) { - // p is inside ab. - return INTERSECT; - } - if (s2 == 0.0) { - // q is collinear with ab. Case (2). - abq = edge_vert_col_inter(A, B, Q); - if (abq == INTERSECT) { - // q is inside ab. - return INTERSECT; - } - if (abp == SHAREVERTEX && abq == SHAREVERTEX) { - // ab and pq are identical. - return SHAREEDGE; - } - pqa = edge_vert_col_inter(P, Q, A); - if (pqa == INTERSECT) { - // a is inside pq. - return INTERSECT; - } - pqb = edge_vert_col_inter(P, Q, B); - if (pqb == INTERSECT) { - // b is inside pq. - return INTERSECT; - } - if (abp == SHAREVERTEX || abq == SHAREVERTEX) { - // either p or q is coincident with a or b. -#ifdef SELF_CHECK - // ONLY one case is possible, otherwise, shoule be SHAREEDGE. - assert(abp ^ abq); -#endif - return SHAREVERTEX; - } - // The last case. They are disjointed. -#ifdef SELF_CHECK - assert((abp == DISJOINT) && (abp == abq && abq == pqa && pqa == pqb)); -#endif - return DISJOINT; - } else { - // p is collinear with ab. Case (1). -#ifdef SELF_CHECK - assert(abp == SHAREVERTEX || abp == DISJOINT); -#endif - return abp; - } - } - // p is NOT collinear with ab. - if (s2 == 0.0) { - // q is collinear with ab. Case (1). - abq = edge_vert_col_inter(A, B, Q); -#ifdef SELF_CHECK - assert(abq == SHAREVERTEX || abq == DISJOINT || abq == INTERSECT); -#endif - return abq; - } + face parentsh; - // We have found p and q are not collinear with ab. However, it is still - // possible that a or b is collinear with pq (ONLY one of a and b). - if (s3 == 0.0) { - // a is collinear with pq. Case (3). -#ifdef SELF_CHECK - assert(s4 != 0.0); -#endif - pqa = edge_vert_col_inter(P, Q, A); -#ifdef SELF_CHECK - // This case should have been detected in above. - assert(pqa != SHAREVERTEX); - assert(pqa == INTERSECT || pqa == DISJOINT); -#endif - return pqa; - } - if (s4 == 0.0) { - // b is collinear with pq. Case (3). + // Get the subface which holds the subsegment. + sdecode(checkseg->sh[0], parentsh); #ifdef SELF_CHECK - assert(s3 != 0.0); + assert(parentsh.sh != dummysh); #endif - pqb = edge_vert_col_inter(P, Q, B); + // Get a tetraheron to which the subface attches. + stpivot(parentsh, *retedge); + if (retedge->tet == dummytet) { + sesymself(parentsh); + stpivot(parentsh, *retedge); #ifdef SELF_CHECK - // This case should have been detected in above. - assert(pqb != SHAREVERTEX); - assert(pqb == INTERSECT || pqb == DISJOINT); + assert(retedge->tet != dummytet); #endif - return pqb; } - - // ab and pq are intersecting properly. - return INTERSECT; + // Correct the edge direction before return. + findedge(retedge, sorg(*checkseg), sdest(*checkseg)); } /////////////////////////////////////////////////////////////////////////////// // // -// Notations // +// findorg() Finds a point in the given handle (tetrahedron or subface). // // // -// Let ABC be the plane passes through a, b, and c; ABC+ be the halfspace // -// including the set of all points x, such that orient3d(a, b, c, x) > 0; // -// ABC- be the other halfspace, such that for each point x in ABC-, // -// orient3d(a, b, c, x) < 0. For the set of x which are on ABC, orient3d(a, // -// b, c, x) = 0. // +// If 'dorg' is a one of vertices of the given handle, set the origin of // +// this handle be that point and return TRUE. Otherwise, return FALSE and // +// 'tface' remains unchanged. // // // /////////////////////////////////////////////////////////////////////////////// +bool tetgenmesh::findorg(triface* tface, point dorg) +{ + if (org(*tface) == dorg) { + return true; + } else { + if (dest(*tface) == dorg) { + enextself(*tface); + return true; + } else { + if (apex(*tface) == dorg) { + enext2self(*tface); + return true; + } else { + if (oppo(*tface) == dorg) { + // Keep 'tface' referring to the same tet after fnext(). + adjustedgering(*tface, CCW); + fnextself(*tface); + enext2self(*tface); + return true; + } + } + } + } + return false; +} + +bool tetgenmesh::findorg(face* sface, point dorg) +{ + if (sorg(*sface) == dorg) { + return true; + } else { + if (sdest(*sface) == dorg) { + senextself(*sface); + return true; + } else { + if (sapex(*sface) == dorg) { + senext2self(*sface); + return true; + } + } + } + return false; +} + /////////////////////////////////////////////////////////////////////////////// // // -// tri_vert_copl_inter() Test whether a triangle (abc) and a coplanar // -// point (p) are intersecting or not. // -// // -// Possible cases are p is inside abc, or on an edge of, or coincident with // -// a vertex of, or outside abc. // -// // -// A reference point R is required. R is exactly not coplanar with abc and p.// -// Since the caller knows they are coplanar, it must be able to provide (or // -// calculate) such a point. // +// findedge() Find an edge in the given handle (tetrahedron or subface). // // // -// The return value indicates one of the four cases: DISJOINT, SHAREVERTEX, // -// and INTERSECT. // +// The edge is given in two points 'eorg' and 'edest'. It is assumed that // +// the edge must exist in the given handle (tetrahedron or subface). This // +// routine sets the right edge version for the input handle. // // // /////////////////////////////////////////////////////////////////////////////// -enum tetgenmesh::interresult tetgenmesh::tri_vert_cop_inter(REAL* A, REAL* B, - REAL* C, REAL* P, REAL* R) +void tetgenmesh::findedge(triface* tface, point eorg, point edest) { - REAL s1, s2, s3; - int sign; - -#ifdef SELF_CHECK - assert(R != (REAL *) NULL); -#endif - // Adjust the orientation of a, b, c and r, so that we can assume that - // r is strictly in ABC- (i.e., r is above ABC wrt. right-hand rule). - s1 = orient3d(A, B, C, R); -#ifdef SELF_CHECK - assert(s1 != 0.0); -#endif - sign = s1 < 0.0 ? 1 : -1; + int i; - // Test starts from here. - s1 = orient3d(A, B, R, P) * sign; - if (s1 < 0.0) { - // p is in ABR-. - return DISJOINT; - } - s2 = orient3d(B, C, R, P) * sign; - if (s2 < 0.0) { - // p is in BCR-. - return DISJOINT; - } - s3 = orient3d(C, A, R, P) * sign; - if (s3 < 0.0) { - // p is in CAR-. - return DISJOINT; - } - if (s1 == 0.0) { - // p is on ABR. - if (s2 == 0.0) { - // p is on BCR. -#ifdef SELF_CHECK - assert(s3 > 0.0); -#endif - // p is coincident with b. - return SHAREVERTEX; - } - if (s3 == 0.0) { - // p is on CAR. - // p is coincident with a. - return SHAREVERTEX; + for (i = 0; i < 3; i++) { + if (org(*tface) == eorg) { + if (dest(*tface) == edest) { + // Edge is found, return. + return; + } + } else { + if (org(*tface) == edest) { + if (dest(*tface) == eorg) { + // Edge is found, inverse the direction and return. + esymself(*tface); + return; + } + } } - // p is on edge ab. - return INTERSECT; + enextself(*tface); } - // p is in ABR+. - if (s2 == 0.0) { - // p is on BCR. - if (s3 == 0.0) { - // p is on CAR. - // p is coincident with c. - return SHAREVERTEX; + // It should never be here. + printf("Internalerror in findedge(): Unable to find an edge in tet.\n"); + internalerror(); +} + +void tetgenmesh::findedge(face* sface, point eorg, point edest) +{ + int i; + + for (i = 0; i < 3; i++) { + if (sorg(*sface) == eorg) { + if (sdest(*sface) == edest) { + // Edge is found, return. + return; + } + } else { + if (sorg(*sface) == edest) { + if (sdest(*sface) == eorg) { + // Edge is found, inverse the direction and return. + sesymself(*sface); + return; + } + } } - // p is on edge bc. - return INTERSECT; - } - if (s3 == 0.0) { - // p is on CAR. - // p is on edge ca. - return INTERSECT; + senextself(*sface); } - - // p is strictly inside abc. - return INTERSECT; + printf("Internalerror in findedge(): Unable to find an edge in subface.\n"); + internalerror(); } /////////////////////////////////////////////////////////////////////////////// // // -// tri_edge_cop_inter() Test whether a triangle (abc) and a coplanar edge // -// (pq) are intersecting or not. // -// // -// A reference point R is required. R is exactly not coplanar with abc and // -// pq. Since the caller knows they are coplanar, it must be able to provide // -// (or calculate) such a point. // +// findface() Find the face has the given origin, destination and apex. // // // -// The return value indicates one of the four cases: DISJOINT, SHAREVERTEX, // -// SHAREEDGE, and INTERSECT. // +// On input, 'fface' is a handle which may contain the three corners or may // +// not or may be dead. On return, it represents exactly the face with the // +// given origin, destination and apex. // // // /////////////////////////////////////////////////////////////////////////////// -enum tetgenmesh::interresult tetgenmesh::tri_edge_cop_inter(REAL* A, REAL* B, - REAL* C, REAL* P, REAL* Q, REAL* R) +void tetgenmesh::findface(triface *fface, point forg, point fdest, point fapex) { - enum interresult abpq, bcpq, capq; - enum interresult abcp, abcq; + triface spintet; + enum finddirectionresult collinear; + int hitbdry; - // Test if pq is intersecting one of edges of abc. - abpq = edge_edge_cop_inter(A, B, P, Q, R); - if (abpq == INTERSECT || abpq == SHAREEDGE) { - return abpq; - } - bcpq = edge_edge_cop_inter(B, C, P, Q, R); - if (bcpq == INTERSECT || bcpq == SHAREEDGE) { - return bcpq; - } - capq = edge_edge_cop_inter(C, A, P, Q, R); - if (capq == INTERSECT || capq == SHAREEDGE) { - return capq; - } - - // Test if p and q is inside abc. - abcp = tri_vert_cop_inter(A, B, C, P, R); - if (abcp == INTERSECT) { - return INTERSECT; - } - abcq = tri_vert_cop_inter(A, B, C, Q, R); - if (abcq == INTERSECT) { - return INTERSECT; + if (!isdead(fface)) { + // First check the easiest case, that 'fface' is just the right one. + if (org(*fface) == forg && dest(*fface) == fdest && + apex(*fface) == fapex) return; + } else { + // The input handle is dead, use the 'recenttet' if it is alive. + if (!isdead(&recenttet)) *fface = recenttet; + } + + if (!isdead(fface)) { + if (!findorg(fface, forg)) { + // 'forg' is not a corner of 'fface', locate it. + preciselocate(forg, fface, tetrahedrons->items); + } + // It is possible that forg is not found in a non-convex mesh. + if (org(*fface) == forg) { + collinear = finddirection(fface, fdest, tetrahedrons->items); + if (collinear == RIGHTCOLLINEAR) { + // fdest is just the destination. + } else if (collinear == LEFTCOLLINEAR) { + enext2self(*fface); + esymself(*fface); + } else if (collinear == TOPCOLLINEAR) { + fnextself(*fface); + enext2self(*fface); + esymself(*fface); + } + } + // It is possible taht fdest is not found in a non-convex mesh. + if ((org(*fface) == forg) && (dest(*fface) == fdest)) { + // Find the apex of 'fapex'. + spintet = *fface; + hitbdry = 0; + do { + if (apex(spintet) == fapex) { + // We have done. Be careful the edge direction of 'spintet', + // it may reversed because of hitting boundary once. + if (org(spintet) != org(*fface)) { + esymself(spintet); + } + *fface = spintet; + return; + } + if (!fnextself(spintet)) { + hitbdry ++; + if (hitbdry < 2) { + esym(*fface, spintet); + if (!fnextself(spintet)) { + hitbdry ++; + } + } + } + } while (hitbdry < 2 && apex(spintet) != apex(*fface)); + // It is possible that fapex is not found in a non-convex mesh. + } } - // Combine the test results of edge intersectings and triangle insides - // to detect whether abc and pq are sharing vertex or disjointed. - if (abpq == SHAREVERTEX) { - // p or q is coincident with a or b. -#ifdef SELF_CHECK - assert(abcp ^ abcq); -#endif - return SHAREVERTEX; - } - if (bcpq == SHAREVERTEX) { - // p or q is coincident with b or c. -#ifdef SELF_CHECK - assert(abcp ^ abcq); -#endif - return SHAREVERTEX; - } - if (capq == SHAREVERTEX) { - // p or q is coincident with c or a. -#ifdef SELF_CHECK - assert(abcp ^ abcq); -#endif - return SHAREVERTEX; + if (isdead(fface) || (org(*fface) != forg) || (dest(*fface) != fdest) || + (apex(*fface) != fapex)) { + // Too bad, the input handle is useless. We have to find a handle + // for 'fface' contains the 'forg' and 'fdest'. Here a brute force + // search is performed. + if (b->verbose > 1) { + printf("Warning in findface(): Perform a brute-force searching.\n"); + } + enum verttype forgty, fdestty, fapexty; + int share, i; + forgty = pointtype(forg); + fdestty = pointtype(fdest); + fapexty = pointtype(fapex); + setpointtype(forg, DEADVERTEX); + setpointtype(fdest, DEADVERTEX); + setpointtype(fapex, DEADVERTEX); + tetrahedrons->traversalinit(); + fface->tet = tetrahedrontraverse(); + while (fface->tet != (tetrahedron *) NULL) { + share = 0; + for (i = 0; i < 4; i++) { + if (pointtype((point) fface->tet[4 + i]) == DEADVERTEX) share ++; + } + if (share == 3) { + // Found! Set the correct face and desired corners. + if (pointtype((point) fface->tet[4]) != DEADVERTEX) { + fface->loc = 2; + } else if (pointtype((point) fface->tet[5]) != DEADVERTEX) { + fface->loc = 3; + } else if (pointtype((point) fface->tet[6]) != DEADVERTEX) { + fface->loc = 1; + } else { // pointtype((point) fface->tet[7]) != DEADVERTEX + fface->loc = 0; + } + findedge(fface, forg, fdest); + break; + } + fface->tet = tetrahedrontraverse(); + } + setpointtype(forg, forgty); + setpointtype(fdest, fdestty); + setpointtype(fapex, fapexty); + if (fface->tet == (tetrahedron *) NULL) { + // It is impossible to reach here. + printf("Internal error: Fail to find the indicated face.\n"); + internalerror(); + } } - - // They are disjointed. - return DISJOINT; } /////////////////////////////////////////////////////////////////////////////// // // -// tri_edge_inter_tail() Test whether a triangle (abc) and an edge (pq) // -// are intersecting or not. // +// getonextseg() Get the next SEGMENT counterclockwise with the same org. // // // -// s1 and s2 are results of pre-performed orientation tests. s1 = orient3d( // -// a, b, c, p); s2 = orient3d(a, b, c, q). To separate this routine from // -// tri_edge_inter() can save two orientation tests in tri_tri_inter(). // -// // -// The return value indicates one of the four cases: DISJOINT, SHAREVERTEX, // -// SHAREEDGE, and INTERSECT. // +// 's' is a subface. This routine reteuns the segment which is counterclock- // +// wise with the origin of s. // // // /////////////////////////////////////////////////////////////////////////////// -enum tetgenmesh::interresult tetgenmesh::tri_edge_inter_tail(REAL* A, REAL* B, - REAL* C, REAL* P, REAL* Q, REAL s1, REAL s2) +void tetgenmesh::getonextseg(face* s, face* lseg) { - REAL s3, s4, s5; - int sign; - - if (s1 * s2 > 0.0) { - // p, q are at the same halfspace of ABC, no intersection. - return DISJOINT; - } - - if (s1 * s2 < 0.0) { - // p, q are both not on ABC (and not sharing vertices, edges of abc). - // Adjust the orientation of a, b, c and p, so that we can assume that - // p is strictly in ABC-, and q is strictly in ABC+. - sign = s1 < 0.0 ? 1 : -1; - s3 = orient3d(A, B, P, Q) * sign; - if (s3 < 0.0) { - // q is at ABP-. - return DISJOINT; - } - s4 = orient3d(B, C, P, Q) * sign; - if (s4 < 0.0) { - // q is at BCP-. - return DISJOINT; - } - s5 = orient3d(C, A, P, Q) * sign; - if (s5 < 0.0) { - // q is at CAP-. - return DISJOINT; - } - if (s3 == 0.0) { - // q is on ABP. - if (s4 == 0.0) { - // q is on BCP (and q must in CAP+). -#ifdef SELF_CHECK - assert(s5 > 0.0); -#endif - // pq intersects abc at vertex b. - return SHAREVERTEX; - } - if (s5 == 0.0) { - // q is on CAP (and q must in BCP+). - // pq intersects abc at vertex a. - return SHAREVERTEX; - } - // q in both BCP+ and CAP+. - // pq crosses ab properly. - return INTERSECT; - } - // q is in ABP+; - if (s4 == 0.0) { - // q is on BCP. - if (s5 == 0.0) { - // q is on CAP. - // pq intersects abc at vertex c. - return SHAREVERTEX; - } - // pq crosses bc properly. - return INTERSECT; - } - // q is in BCP+; - if (s5 == 0.0) { - // q is on CAP. - // pq crosses ca properly. - return INTERSECT; - } - // q is in CAP+; - // pq crosses abc properly. - return INTERSECT; - } - - if (s1 != 0.0 || s2 != 0.0) { - // Either p or q is coplanar with abc. ONLY one of them is possible. - if (s1 == 0.0) { - // p is coplanar with abc, q can be used as reference point. -#ifdef SELF_CHECK - assert(s2 != 0.0); -#endif - return tri_vert_cop_inter(A, B, C, P, Q); - } else { - // q is coplanar with abc, p can be used as reference point. -#ifdef SELF_CHECK - assert(s2 == 0.0); -#endif - return tri_vert_cop_inter(A, B, C, Q, P); - } - } + face checksh, checkseg; + point forg; - // pq is coplanar with abc. Calculate a point which is exactly not - // coplanar with a, b, and c. - REAL R[3], N[3]; - REAL ax, ay, az, bx, by, bz; - - ax = A[0] - B[0]; - ay = A[1] - B[1]; - az = A[2] - B[2]; - bx = A[0] - C[0]; - by = A[1] - C[1]; - bz = A[2] - C[2]; - N[0] = ay * bz - by * az; - N[1] = az * bx - bz * ax; - N[2] = ax * by - bx * ay; - // The normal should not be a zero vector (otherwise, abc are collinear). + forg = sorg(*s); + checksh = *s; + do { + // Go to the edge at forg's left side. + senext2self(checksh); + // Check if there is a segment attaching this edge. + sspivot(checksh, checkseg); + if (checkseg.sh != dummysh) break; + // No segment! Go to the neighbor of this subface. + spivotself(checksh); #ifdef SELF_CHECK - assert((fabs(N[0]) + fabs(N[1]) + fabs(N[2])) > 0.0); + // It should always meet a segment before come back. + assert(checksh.sh != s->sh); #endif - // The reference point R is lifted from A to the normal direction with - // a distance d = average edge length of the triangle abc. - R[0] = N[0] + A[0]; - R[1] = N[1] + A[1]; - R[2] = N[2] + A[2]; - // Becareful the case: if the non-zero component(s) in N is smaller than - // the machine epsilon (i.e., 2^(-16) for double), R will exactly equal - // to A due to the round-off error. Do check if it is. - if (R[0] == A[0] && R[1] == A[1] && R[2] == A[2]) { - int i, j; - for (i = 0; i < 3; i++) { + if (sorg(checksh) != forg) { + sesymself(checksh); #ifdef SELF_CHECK - assert (R[i] == A[i]); + assert(sorg(checksh) == forg); #endif - j = 2; - do { - if (N[i] > 0.0) { - N[i] += (j * macheps); - } else { - N[i] -= (j * macheps); - } - R[i] = N[i] + A[i]; - j *= 2; - } while (R[i] == A[i]); } - } - - return tri_edge_cop_inter(A, B, C, P, Q, R); + } while (true); + if (sorg(checkseg) != forg) sesymself(checkseg); + *lseg = checkseg; } /////////////////////////////////////////////////////////////////////////////// // // -// tri_edge_inter() Test whether a triangle (abc) and an edge (pq) are // -// intersecting or not. // +// getseghasorg() Get the segment containing the given point. // // // -// The return value indicates one of the four cases: DISJOINT, SHAREVERTEX, // -// SHAREEDGE, and INTERSECT. // +// 'dorg' is an endpoint of a segment S. 'sseg' is a subsegment of S. This // +// routine search a subsegment (along sseg) of S containing dorg. On return, // +// 'sseg' contains 'dorg' as its origin. // // // /////////////////////////////////////////////////////////////////////////////// -enum tetgenmesh::interresult tetgenmesh::tri_edge_inter(REAL* A, REAL* B, - REAL* C, REAL* P, REAL* Q) +void tetgenmesh::getseghasorg(face* sseg, point dorg) { - REAL s1, s2; - - // Test the locations of p and q with respect to ABC. - s1 = orient3d(A, B, C, P); - s2 = orient3d(A, B, C, Q); + face nextseg; + point checkpt; - return tri_edge_inter_tail(A, B, C, P, Q, s1, s2); + nextseg = *sseg; + checkpt = sorg(nextseg); + while ((checkpt != dorg) && (pointtype(checkpt) == FREESEGVERTEX)) { + // Search dorg along the original direction of sseg. + senext2self(nextseg); + spivotself(nextseg); + nextseg.shver = 0; + if (sdest(nextseg) != checkpt) sesymself(nextseg); + checkpt = sorg(nextseg); + } + if (checkpt == dorg) { + *sseg = nextseg; + return; + } + nextseg = *sseg; + checkpt = sdest(nextseg); + while ((checkpt != dorg) && (pointtype(checkpt) == FREESEGVERTEX)) { + // Search dorg along the destinational direction of sseg. + senextself(nextseg); + spivotself(nextseg); + nextseg.shver = 0; + if (sorg(nextseg) != checkpt) sesymself(nextseg); + checkpt = sdest(nextseg); + } + if (checkpt == dorg) { + sesym(nextseg, *sseg); + return; + } + // Should never be here. + printf("Internalerror in getseghasorg(): Unable to find the subseg.\n"); + internalerror(); } /////////////////////////////////////////////////////////////////////////////// // // -// tri_tri_inter() Test whether two triangle (abc) and (opq) are // -// intersecting or not. // -// // -// The return value indicates one of the five cases: DISJOINT, SHAREVERTEX, // -// SHAREEDGE, SHAREFACE, and INTERSECT. // +// getsubsegfarorg() Get the origin of the parent segment of a subseg. // // // /////////////////////////////////////////////////////////////////////////////// -enum tetgenmesh::interresult tetgenmesh::tri_tri_inter(REAL* A, REAL* B, - REAL* C, REAL* O, REAL* P, REAL* Q) +tetgenmesh::point tetgenmesh::getsubsegfarorg(face* sseg) { - REAL s_o, s_p, s_q; - REAL s_a, s_b, s_c; + face prevseg; + point checkpt; - s_o = orient3d(A, B, C, O); - s_p = orient3d(A, B, C, P); - s_q = orient3d(A, B, C, Q); - if ((s_o * s_p > 0.0) && (s_o * s_q > 0.0)) { - // o, p, q are all in the same halfspace of ABC. - return DISJOINT; + checkpt = sorg(*sseg); + senext2(*sseg, prevseg); + spivotself(prevseg); + // Search dorg along the original direction of sseg. + while (prevseg.sh != dummysh) { + prevseg.shver = 0; + if (sdest(prevseg) != checkpt) sesymself(prevseg); + checkpt = sorg(prevseg); + senext2self(prevseg); + spivotself(prevseg); } + return checkpt; +} - s_a = orient3d(O, P, Q, A); - s_b = orient3d(O, P, Q, B); - s_c = orient3d(O, P, Q, C); - if ((s_a * s_b > 0.0) && (s_a * s_c > 0.0)) { - // a, b, c are all in the same halfspace of OPQ. - return DISJOINT; - } +/////////////////////////////////////////////////////////////////////////////// +// // +// getsubsegfardest() Get the dest. of the parent segment of a subseg. // +// // +/////////////////////////////////////////////////////////////////////////////// - enum interresult abcop, abcpq, abcqo; - int shareedge = 0; +tetgenmesh::point tetgenmesh::getsubsegfardest(face* sseg) +{ + face nextseg; + point checkpt; - abcop = tri_edge_inter_tail(A, B, C, O, P, s_o, s_p); - if (abcop == INTERSECT) { - return INTERSECT; - } else if (abcop == SHAREEDGE) { - shareedge++; - } - abcpq = tri_edge_inter_tail(A, B, C, P, Q, s_p, s_q); - if (abcpq == INTERSECT) { - return INTERSECT; - } else if (abcpq == SHAREEDGE) { - shareedge++; - } - abcqo = tri_edge_inter_tail(A, B, C, Q, O, s_q, s_o); - if (abcqo == INTERSECT) { - return INTERSECT; - } else if (abcqo == SHAREEDGE) { - shareedge++; - } - if (shareedge == 3) { - // opq are coincident with abc. - return SHAREFACE; - } -#ifdef SELF_CHECK - // It is only possible either no share edge or one. - assert(shareedge == 0 || shareedge == 1); -#endif - - // Continue to detect whether opq and abc are intersecting or not. - enum interresult opqab, opqbc, opqca; - - opqab = tri_edge_inter_tail(O, P, Q, A, B, s_a, s_b); - if (opqab == INTERSECT) { - return INTERSECT; - } - opqbc = tri_edge_inter_tail(O, P, Q, B, C, s_b, s_c); - if (opqbc == INTERSECT) { - return INTERSECT; - } - opqca = tri_edge_inter_tail(O, P, Q, C, A, s_c, s_a); - if (opqca == INTERSECT) { - return INTERSECT; - } - - // At this point, two triangles are not intersecting and not coincident. - // They may be share an edge, or share a vertex, or disjoint. - if (abcop == SHAREEDGE) { -#ifdef SELF_CHECK - assert(abcpq == SHAREVERTEX && abcqo == SHAREVERTEX); -#endif - // op is coincident with an edge of abc. - return SHAREEDGE; - } - if (abcpq == SHAREEDGE) { -#ifdef SELF_CHECK - assert(abcop == SHAREVERTEX && abcqo == SHAREVERTEX); -#endif - // pq is coincident with an edge of abc. - return SHAREEDGE; - } - if (abcqo == SHAREEDGE) { -#ifdef SELF_CHECK - assert(abcop == SHAREVERTEX && abcpq == SHAREVERTEX); -#endif - // qo is coincident with an edge of abc. - return SHAREEDGE; - } - - // They may share a vertex or disjoint. - if (abcop == SHAREVERTEX) { - // o or p is coincident with a vertex of abc. - if (abcpq == SHAREVERTEX) { - // p is the coincident vertex. -#ifdef SELF_CHECK - assert(abcqo != SHAREVERTEX); -#endif - } else { - // o is the coincident vertex. -#ifdef SELF_CHECK - assert(abcqo == SHAREVERTEX); -#endif - } - return SHAREVERTEX; - } - if (abcpq == SHAREVERTEX) { - // q is the coincident vertex. -#ifdef SELF_CHECK - assert(abcqo == SHAREVERTEX); -#endif - return SHAREVERTEX; + checkpt = sdest(*sseg); + senext(*sseg, nextseg); + spivotself(nextseg); + // Search dorg along the destinational direction of sseg. + while (nextseg.sh != dummysh) { + nextseg.shver = 0; + if (sorg(nextseg) != checkpt) sesymself(nextseg); + checkpt = sdest(nextseg); + senextself(nextseg); + spivotself(nextseg); } - - // They are disjoint. - return DISJOINT; + return checkpt; } /////////////////////////////////////////////////////////////////////////////// // // -// tri_edge_2d() Triangle-edge coplanar intersection test. // -// // -// This routine takes a triangle T (with vertices A, B, C) and an edge E (P, // -// Q) in a plane in 3D, and tests if they intersect each other. Return 1 if // -// they are intersected, i.e., T \cap E is not empty, otherwise, return 0. // -// // -// If the point 'R' is not NULL, it lies strictly above T [A, B, C]. // +// printtet() Print out the details of a tetrahedron on screen. // // // -// If T1 and T2 intersect each other (return 1), they may intersect in diff- // -// erent ways. If 'level' > 0, their intersection type will be reported in // -// combinations of 'types' and 'pos'. // +// It's also used when the highest level of verbosity (`-VVV') is specified. // // // /////////////////////////////////////////////////////////////////////////////// -int tetgenmesh::tri_edge_2d(point A, point B, point C, point P, point Q, - point R, int level, int *types, int *pos) +void tetgenmesh::printtet(triface* tface) { - point U[3], V[3]; // The permuted vectors of points. - int pu[3], pv[3]; // The original positions of points. - REAL sA, sB, sC; - REAL s1, s2, s3, s4; - int z1; - - if (R == NULL) { - REAL n[3], len; - // Calculate a lift point, saved in dummypoint. - facenormal2(A, B, C, n, 1); - len = sqrt(DOT(n, n)); - n[0] /= len; - n[1] /= len; - n[2] /= len; - len = DIST(A, B); - len += DIST(B, C); - len += DIST(C, A); - len /= 3.0; - R = dummypoint; - R[0] = A[0] + len * n[0]; - R[1] = A[1] + len * n[1]; - R[2] = A[2] + len * n[2]; - } - - // Test A's, B's, and C's orientations wrt plane PQR. - sA = orient3d(P, Q, R, A); - sB = orient3d(P, Q, R, B); - sC = orient3d(P, Q, R, C); - orient3dcount+=3; + triface tmpface, prtface; + point tmppt; + face tmpsh; + int facecount; - if (b->verbose > 2) { - printf(" Tri-edge-2d (%d %d %d)-(%d %d)-(%d) (%c%c%c)", pointmark(A), - pointmark(B), pointmark(C), pointmark(P), pointmark(Q), pointmark(R), - sA > 0 ? '+' : (sA < 0 ? '-' : '0'), sB>0 ? '+' : (sB<0 ? '-' : '0'), - sC>0 ? '+' : (sC<0 ? '-' : '0')); + printf("Tetra x%lx with loc(%i) and ver(%i):", + (unsigned long)(tface->tet), tface->loc, tface->ver); + if (infected(*tface)) { + printf(" (infected)"); } - // triedgcopcount++; + printf("\n"); - if (sA < 0) { - if (sB < 0) { - if (sC < 0) { // (---). - return 0; - } else { - if (sC > 0) { // (--+). - // All points are in the right positions. - SETVECTOR3(U, A, B, C); // I3 - SETVECTOR3(V, P, Q, R); // I2 - SETVECTOR3(pu, 0, 1, 2); - SETVECTOR3(pv, 0, 1, 2); - z1 = 0; - } else { // (--0). - SETVECTOR3(U, A, B, C); // I3 - SETVECTOR3(V, P, Q, R); // I2 - SETVECTOR3(pu, 0, 1, 2); - SETVECTOR3(pv, 0, 1, 2); - z1 = 1; - } - } - } else { - if (sB > 0) { - if (sC < 0) { // (-+-). - SETVECTOR3(U, C, A, B); // PT = ST - SETVECTOR3(V, P, Q, R); // I2 - SETVECTOR3(pu, 2, 0, 1); - SETVECTOR3(pv, 0, 1, 2); - z1 = 0; - } else { - if (sC > 0) { // (-++). - SETVECTOR3(U, B, C, A); // PT = ST x ST - SETVECTOR3(V, Q, P, R); // PL = SL - SETVECTOR3(pu, 1, 2, 0); - SETVECTOR3(pv, 1, 0, 2); - z1 = 0; - } else { // (-+0). - SETVECTOR3(U, C, A, B); // PT = ST - SETVECTOR3(V, P, Q, R); // I2 - SETVECTOR3(pu, 2, 0, 1); - SETVECTOR3(pv, 0, 1, 2); - z1 = 2; - } - } - } else { - if (sC < 0) { // (-0-). - SETVECTOR3(U, C, A, B); // PT = ST - SETVECTOR3(V, P, Q, R); // I2 - SETVECTOR3(pu, 2, 0, 1); - SETVECTOR3(pv, 0, 1, 2); - z1 = 1; - } else { - if (sC > 0) { // (-0+). - SETVECTOR3(U, B, C, A); // PT = ST x ST - SETVECTOR3(V, Q, P, R); // PL = SL - SETVECTOR3(pu, 1, 2, 0); - SETVECTOR3(pv, 1, 0, 2); - z1 = 2; - } else { // (-00). - SETVECTOR3(U, B, C, A); // PT = ST x ST - SETVECTOR3(V, Q, P, R); // PL = SL - SETVECTOR3(pu, 1, 2, 0); - SETVECTOR3(pv, 1, 0, 2); - z1 = 3; - } - } - } - } - } else { - if (sA > 0) { - if (sB < 0) { - if (sC < 0) { // (+--). - SETVECTOR3(U, B, C, A); // PT = ST x ST - SETVECTOR3(V, P, Q, R); // I2 - SETVECTOR3(pu, 1, 2, 0); - SETVECTOR3(pv, 0, 1, 2); - z1 = 0; - } else { - if (sC > 0) { // (+-+). - SETVECTOR3(U, C, A, B); // PT = ST - SETVECTOR3(V, Q, P, R); // PL = SL - SETVECTOR3(pu, 2, 0, 1); - SETVECTOR3(pv, 1, 0, 2); - z1 = 0; - } else { // (+-0). - SETVECTOR3(U, C, A, B); // PT = ST - SETVECTOR3(V, Q, P, R); // PL = SL - SETVECTOR3(pu, 2, 0, 1); - SETVECTOR3(pv, 1, 0, 2); - z1 = 2; - } - } - } else { - if (sB > 0) { - if (sC < 0) { // (++-). - SETVECTOR3(U, A, B, C); // I3 - SETVECTOR3(V, Q, P, R); // PL = SL - SETVECTOR3(pu, 0, 1, 2); - SETVECTOR3(pv, 1, 0, 2); - z1 = 0; - } else { - if (sC > 0) { // (+++). - return 0; - } else { // (++0). - SETVECTOR3(U, A, B, C); // I3 - SETVECTOR3(V, Q, P, R); // PL = SL - SETVECTOR3(pu, 0, 1, 2); - SETVECTOR3(pv, 1, 0, 2); - z1 = 1; - } - } - } else { // (+0#) - if (sC < 0) { // (+0-). - SETVECTOR3(U, B, C, A); // PT = ST x ST - SETVECTOR3(V, P, Q, R); // I2 - SETVECTOR3(pu, 1, 2, 0); - SETVECTOR3(pv, 0, 1, 2); - z1 = 2; - } else { - if (sC > 0) { // (+0+). - SETVECTOR3(U, C, A, B); // PT = ST - SETVECTOR3(V, Q, P, R); // PL = SL - SETVECTOR3(pu, 2, 0, 1); - SETVECTOR3(pv, 1, 0, 2); - z1 = 1; - } else { // (+00). - SETVECTOR3(U, B, C, A); // PT = ST x ST - SETVECTOR3(V, P, Q, R); // I2 - SETVECTOR3(pu, 1, 2, 0); - SETVECTOR3(pv, 0, 1, 2); - z1 = 3; - } - } - } - } - } else { - if (sB < 0) { - if (sC < 0) { // (0--). - SETVECTOR3(U, B, C, A); // PT = ST x ST - SETVECTOR3(V, P, Q, R); // I2 - SETVECTOR3(pu, 1, 2, 0); - SETVECTOR3(pv, 0, 1, 2); - z1 = 1; - } else { - if (sC > 0) { // (0-+). - SETVECTOR3(U, A, B, C); // I3 - SETVECTOR3(V, P, Q, R); // I2 - SETVECTOR3(pu, 0, 1, 2); - SETVECTOR3(pv, 0, 1, 2); - z1 = 2; - } else { // (0-0). - SETVECTOR3(U, C, A, B); // PT = ST - SETVECTOR3(V, Q, P, R); // PL = SL - SETVECTOR3(pu, 2, 0, 1); - SETVECTOR3(pv, 1, 0, 2); - z1 = 3; - } - } - } else { - if (sB > 0) { - if (sC < 0) { // (0+-). - SETVECTOR3(U, A, B, C); // I3 - SETVECTOR3(V, Q, P, R); // PL = SL - SETVECTOR3(pu, 0, 1, 2); - SETVECTOR3(pv, 1, 0, 2); - z1 = 2; - } else { - if (sC > 0) { // (0++). - SETVECTOR3(U, B, C, A); // PT = ST x ST - SETVECTOR3(V, Q, P, R); // PL = SL - SETVECTOR3(pu, 1, 2, 0); - SETVECTOR3(pv, 1, 0, 2); - z1 = 1; - } else { // (0+0). - SETVECTOR3(U, C, A, B); // PT = ST - SETVECTOR3(V, P, Q, R); // I2 - SETVECTOR3(pu, 2, 0, 1); - SETVECTOR3(pv, 0, 1, 2); - z1 = 3; - } - } - } else { // (00#) - if (sC < 0) { // (00-). - SETVECTOR3(U, A, B, C); // I3 - SETVECTOR3(V, Q, P, R); // PL = SL - SETVECTOR3(pu, 0, 1, 2); - SETVECTOR3(pv, 1, 0, 2); - z1 = 3; - } else { - if (sC > 0) { // (00+). - SETVECTOR3(U, A, B, C); // I3 - SETVECTOR3(V, P, Q, R); // I2 - SETVECTOR3(pu, 0, 1, 2); - SETVECTOR3(pv, 0, 1, 2); - z1 = 3; - } else { // (000) - // Not possible unless ABC is degenerate. - z1 = 4; - } - } - } + tmpface = *tface; + facecount = 0; + while(facecount < 4) { + tmpface.loc = facecount; + sym(tmpface, prtface); + if(prtface.tet == dummytet) { + printf(" [%i] Outer space.\n", facecount); + } else { + printf(" [%i] x%lx loc(%i).", facecount, + (unsigned long)(prtface.tet), prtface.loc); + if (infected(prtface)) { + printf(" (infected)"); } + printf("\n"); } + facecount ++; } - s1 = orient3d(U[0], U[2], R, V[1]); // A, C, R, Q - s2 = orient3d(U[1], U[2], R, V[0]); // B, C, R, P - orient3dcount+=2; - - if (b->verbose > 2) { - printf(" Tri-edge-2d (%d %d %d)-(%d %d %d) (%d) (%c%c)\n", - pointmark(U[0]), pointmark(U[1]), pointmark(U[2]), pointmark(V[0]), - pointmark(V[1]), pointmark(V[2]), z1, s1>0 ? '+' : (s1<0 ? '-' : '0'), - s2>0 ? '+' : (s2<0 ? '-' : '0')); - } - assert(z1 != 4); // SELF_CHECK - - if (s1 > 0) { - return 0; + tmppt = org(*tface); + if(tmppt == (point) NULL) { + printf(" Org [%i] NULL\n", locver2org[tface->loc][tface->ver]); + } else { + printf(" Org [%i] x%lx (%.12g,%.12g,%.12g) %d\n", + locver2org[tface->loc][tface->ver], (unsigned long)(tmppt), + tmppt[0], tmppt[1], tmppt[2], pointmark(tmppt)); } - if (s2 < 0) { - return 0; + tmppt = dest(*tface); + if(tmppt == (point) NULL) { + printf(" Dest[%i] NULL\n", locver2dest[tface->loc][tface->ver]); + } else { + printf(" Dest[%i] x%lx (%.12g,%.12g,%.12g) %d\n", + locver2dest[tface->loc][tface->ver], (unsigned long)(tmppt), + tmppt[0], tmppt[1], tmppt[2], pointmark(tmppt)); } - - if (level == 0) { - return 1; // They are intersected. + tmppt = apex(*tface); + if(tmppt == (point) NULL) { + printf(" Apex[%i] NULL\n", locver2apex[tface->loc][tface->ver]); + } else { + printf(" Apex[%i] x%lx (%.12g,%.12g,%.12g) %d\n", + locver2apex[tface->loc][tface->ver], (unsigned long)(tmppt), + tmppt[0], tmppt[1], tmppt[2], pointmark(tmppt)); } - - if (z1 == 1) { - if (s1 == 0) { // (0###) - // C = Q. - types[0] = (int) SHAREVERTEX; - pos[0] = pu[2]; // C - pos[1] = pv[1]; // Q - types[1] = (int) DISJOINT; - } else { - if (s2 == 0) { // (#0##) - // C = P. - types[0] = (int) SHAREVERTEX; - pos[0] = pu[2]; // C - pos[1] = pv[0]; // P - types[1] = (int) DISJOINT; - } else { // (-+##) - // C in [P, Q]. - types[0] = (int) INTERVERT; - pos[0] = pu[2]; // C - pos[1] = pv[0]; // [P, Q] - types[1] = (int) DISJOINT; - } - } - return 1; + tmppt = oppo(*tface); + if(tmppt == (point) NULL) { + printf(" Oppo[%i] NULL\n", loc2oppo[tface->loc]); + } else { + printf(" Oppo[%i] x%lx (%.12g,%.12g,%.12g) %d\n", + loc2oppo[tface->loc], (unsigned long)(tmppt), + tmppt[0], tmppt[1], tmppt[2], pointmark(tmppt)); } - s3 = orient3d(U[0], U[2], R, V[0]); // A, C, R, P - s4 = orient3d(U[1], U[2], R, V[1]); // B, C, R, Q - orient3dcount+=2; - - if (z1 == 0) { // (tritri-03) - if (s1 < 0) { - if (s3 > 0) { - assert(s2 > 0); // SELF_CHECK - if (s4 > 0) { - // [P, Q] overlaps [k, l] (-+++). - types[0] = (int) INTEREDGE; - pos[0] = pu[2]; // [C, A] - pos[1] = pv[0]; // [P, Q] - types[1] = (int) TOUCHFACE; - pos[2] = 3; // [A, B, C] - pos[3] = pv[1]; // Q - } else { - if (s4 == 0) { - // Q = l, [P, Q] contains [k, l] (-++0). - types[0] = (int) INTEREDGE; - pos[0] = pu[2]; // [C, A] - pos[1] = pv[0]; // [P, Q] - types[1] = (int) TOUCHEDGE; - pos[2] = pu[1]; // [B, C] - pos[3] = pv[1]; // Q - } else { // s4 < 0 - // [P, Q] contains [k, l] (-++-). - types[0] = (int) INTEREDGE; - pos[0] = pu[2]; // [C, A] - pos[1] = pv[0]; // [P, Q] - types[1] = (int) INTEREDGE; - pos[2] = pu[1]; // [B, C] - pos[3] = pv[0]; // [P, Q] - } - } - } else { - if (s3 == 0) { - assert(s2 > 0); // SELF_CHECK - if (s4 > 0) { - // P = k, [P, Q] in [k, l] (-+0+). - types[0] = (int) TOUCHEDGE; - pos[0] = pu[2]; // [C, A] - pos[1] = pv[0]; // P - types[1] = (int) TOUCHFACE; - pos[2] = 3; // [A, B, C] - pos[3] = pv[1]; // Q - } else { - if (s4 == 0) { - // [P, Q] = [k, l] (-+00). - types[0] = (int) TOUCHEDGE; - pos[0] = pu[2]; // [C, A] - pos[1] = pv[0]; // P - types[1] = (int) TOUCHEDGE; - pos[2] = pu[1]; // [B, C] - pos[3] = pv[1]; // Q - } else { - // P = k, [P, Q] contains [k, l] (-+0-). - types[0] = (int) TOUCHEDGE; - pos[0] = pu[2]; // [C, A] - pos[1] = pv[0]; // P - types[1] = (int) INTEREDGE; - pos[2] = pu[1]; // [B, C] - pos[3] = pv[0]; // [P, Q] - } - } - } else { // s3 < 0 - if (s2 > 0) { - if (s4 > 0) { - // [P, Q] in [k, l] (-+-+). - types[0] = (int) TOUCHFACE; - pos[0] = 3; // [A, B, C] - pos[1] = pv[0]; // P - types[1] = (int) TOUCHFACE; - pos[2] = 3; // [A, B, C] - pos[3] = pv[1]; // Q - } else { - if (s4 == 0) { - // Q = l, [P, Q] in [k, l] (-+-0). - types[0] = (int) TOUCHFACE; - pos[0] = 3; // [A, B, C] - pos[1] = pv[0]; // P - types[1] = (int) TOUCHEDGE; - pos[2] = pu[1]; // [B, C] - pos[3] = pv[1]; // Q - } else { // s4 < 0 - // [P, Q] overlaps [k, l] (-+--). - types[0] = (int) TOUCHFACE; - pos[0] = 3; // [A, B, C] - pos[1] = pv[0]; // P - types[1] = (int) INTEREDGE; - pos[2] = pu[1]; // [B, C] - pos[3] = pv[0]; // [P, Q] - } - } - } else { // s2 == 0 - // P = l (#0##). - types[0] = (int) TOUCHEDGE; - pos[0] = pu[1]; // [B, C] - pos[1] = pv[0]; // P - types[1] = (int) DISJOINT; - } - } - } - } else { // s1 == 0 - // Q = k (0####) - types[0] = (int) TOUCHEDGE; - pos[0] = pu[2]; // [C, A] - pos[1] = pv[1]; // Q - types[1] = (int) DISJOINT; - } - } else if (z1 == 2) { // (tritri-23) - if (s1 < 0) { - if (s3 > 0) { - assert(s2 > 0); // SELF_CHECK - if (s4 > 0) { - // [P, Q] overlaps [A, l] (-+++). - types[0] = (int) INTERVERT; - pos[0] = pu[0]; // A - pos[1] = pv[0]; // [P, Q] - types[1] = (int) TOUCHFACE; - pos[2] = 3; // [A, B, C] - pos[3] = pv[1]; // Q - } else { - if (s4 == 0) { - // Q = l, [P, Q] contains [A, l] (-++0). - types[0] = (int) INTERVERT; - pos[0] = pu[0]; // A - pos[1] = pv[0]; // [P, Q] - types[1] = (int) TOUCHEDGE; - pos[2] = pu[1]; // [B, C] - pos[3] = pv[1]; // Q - } else { // s4 < 0 - // [P, Q] contains [A, l] (-++-). - types[0] = (int) INTERVERT; - pos[0] = pu[0]; // A - pos[1] = pv[0]; // [P, Q] - types[1] = (int) INTEREDGE; - pos[2] = pu[1]; // [B, C] - pos[3] = pv[0]; // [P, Q] - } - } - } else { - if (s3 == 0) { - assert(s2 > 0); // SELF_CHECK - if (s4 > 0) { - // P = A, [P, Q] in [A, l] (-+0+). - types[0] = (int) SHAREVERTEX; - pos[0] = pu[0]; // A - pos[1] = pv[0]; // P - types[1] = (int) TOUCHFACE; - pos[2] = 3; // [A, B, C] - pos[3] = pv[1]; // Q - } else { - if (s4 == 0) { - // [P, Q] = [A, l] (-+00). - types[0] = (int) SHAREVERTEX; - pos[0] = pu[0]; // A - pos[1] = pv[0]; // P - types[1] = (int) TOUCHEDGE; - pos[2] = pu[1]; // [B, C] - pos[3] = pv[1]; // Q - } else { // s4 < 0 - // Q = l, [P, Q] in [A, l] (-+0-). - types[0] = (int) SHAREVERTEX; - pos[0] = pu[0]; // A - pos[1] = pv[0]; // P - types[1] = (int) INTEREDGE; - pos[2] = pu[1]; // [B, C] - pos[3] = pv[0]; // [P, Q] - } - } - } else { // s3 < 0 - if (s2 > 0) { - if (s4 > 0) { - // [P, Q] in [A, l] (-+-+). - types[0] = (int) TOUCHFACE; - pos[0] = 3; // [A, B, C] - pos[1] = pv[0]; // P - types[0] = (int) TOUCHFACE; - pos[0] = 3; // [A, B, C] - pos[1] = pv[1]; // Q - } else { - if (s4 == 0) { - // Q = l, [P, Q] in [A, l] (-+-0). - types[0] = (int) TOUCHFACE; - pos[0] = 3; // [A, B, C] - pos[1] = pv[0]; // P - types[0] = (int) TOUCHEDGE; - pos[0] = pu[1]; // [B, C] - pos[1] = pv[1]; // Q - } else { // s4 < 0 - // [P, Q] overlaps [A, l] (-+--). - types[0] = (int) TOUCHFACE; - pos[0] = 3; // [A, B, C] - pos[1] = pv[0]; // P - types[0] = (int) INTEREDGE; - pos[0] = pu[1]; // [B, C] - pos[1] = pv[0]; // [P, Q] - } - } - } else { // s2 == 0 - // P = l (#0##). - types[0] = (int) TOUCHEDGE; - pos[0] = pu[1]; // [B, C] - pos[1] = pv[0]; // P - types[1] = (int) DISJOINT; - } - } - } - } else { // s1 == 0 - // Q = A (0###). - types[0] = (int) SHAREVERTEX; - pos[0] = pu[0]; // A - pos[1] = pv[1]; // Q - types[1] = (int) DISJOINT; - } - } else if (z1 == 3) { // (tritri-33) - if (s1 < 0) { - if (s3 > 0) { - assert(s2 > 0); // SELF_CHECK - if (s4 > 0) { - // [P, Q] overlaps [A, B] (-+++). - types[0] = (int) INTERVERT; - pos[0] = pu[0]; // A - pos[1] = pv[0]; // [P, Q] - types[1] = (int) TOUCHEDGE; - pos[2] = pu[0]; // [A, B] - pos[3] = pv[1]; // Q - } else { - if (s4 == 0) { - // Q = B, [P, Q] contains [A, B] (-++0). - types[0] = (int) INTERVERT; - pos[0] = pu[0]; // A - pos[1] = pv[0]; // [P, Q] - types[1] = (int) SHAREVERTEX; - pos[2] = pu[1]; // B - pos[3] = pv[1]; // Q - } else { // s4 < 0 - // [P, Q] contains [A, B] (-++-). - types[0] = (int) INTERVERT; - pos[0] = pu[0]; // A - pos[1] = pv[0]; // [P, Q] - types[1] = (int) INTERVERT; - pos[2] = pu[1]; // B - pos[3] = pv[0]; // [P, Q] - } - } - } else { - if (s3 == 0) { - assert(s2 > 0); // SELF_CHECK - if (s4 > 0) { - // P = A, [P, Q] in [A, B] (-+0+). - types[0] = (int) SHAREVERTEX; - pos[0] = pu[0]; // A - pos[1] = pv[0]; // P - types[1] = (int) TOUCHEDGE; - pos[2] = pu[0]; // [A, B] - pos[3] = pv[1]; // Q - } else { - if (s4 == 0) { - // [P, Q] = [A, B] (-+00). - types[0] = (int) SHAREEDGE; - pos[0] = pu[0]; // [A, B] - pos[1] = pv[0]; // [P, Q] - types[1] = (int) DISJOINT; - } else { // s4 < 0 - // P= A, [P, Q] in [A, B] (-+0-). - types[0] = (int) SHAREVERTEX; - pos[0] = pu[0]; // A - pos[1] = pv[0]; // P - types[1] = (int) INTERVERT; - pos[2] = pu[1]; // B - pos[3] = pv[0]; // [P, Q] - } - } - } else { // s3 < 0 - if (s2 > 0) { - if (s4 > 0) { - // [P, Q] in [A, B] (-+-+). - types[0] = (int) TOUCHEDGE; - pos[0] = pu[0]; // [A, B] - pos[1] = pv[0]; // P - types[1] = (int) TOUCHEDGE; - pos[2] = pu[0]; // [A, B] - pos[3] = pv[1]; // Q - } else { - if (s4 == 0) { - // Q = B, [P, Q] in [A, B] (-+-0). - types[0] = (int) TOUCHEDGE; - pos[0] = pu[0]; // [A, B] - pos[1] = pv[0]; // P - types[1] = (int) SHAREVERTEX; - pos[2] = pu[1]; // B - pos[3] = pv[1]; // Q - } else { // s4 < 0 - // [P, Q] overlaps [A, B] (-+--). - types[0] = (int) TOUCHEDGE; - pos[0] = pu[0]; // [A, B] - pos[1] = pv[0]; // P - types[1] = (int) INTERVERT; - pos[2] = pu[1]; // B - pos[3] = pv[0]; // [P, Q] - } - } - } else { // s2 == 0 - // P = B (#0##). - types[0] = (int) SHAREVERTEX; - pos[0] = pu[1]; // B - pos[1] = pv[0]; // P - types[1] = (int) DISJOINT; - } + if (b->useshelles) { + tmpface = *tface; + facecount = 0; + while(facecount < 6) { + tmpface.loc = facecount; + tspivot(tmpface, tmpsh); + if(tmpsh.sh != dummysh) { + printf(" [%i] x%lx ID(%i) ", facecount, + (unsigned long)(tmpsh.sh), shellmark(tmpsh)); + if (sorg(tmpsh) == (point) NULL) { + printf("(fake)"); } + printf("\n"); } - } else { // s1 == 0 - // Q = A (0###). - types[0] = (int) SHAREVERTEX; - pos[0] = pu[0]; // A - pos[1] = pv[1]; // Q - types[1] = (int) DISJOINT; + facecount ++; } } - - return 1; } /////////////////////////////////////////////////////////////////////////////// // // -// tri_edge_test() Triangle-edge intersection test. // -// // -// This routine takes a triangle T (with vertices A, B, C) and an edge E (P, // -// Q) in 3D, and tests if they intersect each other. Return 1 if they are // -// intersected, i.e., T \cap E is not empty, otherwise, return 0. // -// // -// If the point 'R' is not NULL, it lies strictly above the plane defined by // -// A, B, C. It is used in test when T and E are coplanar. // +// printsh() Print out the details of a subface or subsegment on screen. // // // -// If T1 and T2 intersect each other (return 1), they may intersect in diff- // -// erent ways. If 'level' > 0, their intersection type will be reported in // -// combinations of 'types' and 'pos'. // +// It's also used when the highest level of verbosity (`-VVV') is specified. // // // /////////////////////////////////////////////////////////////////////////////// -int tetgenmesh::tri_edge_test(point A, point B, point C, point P, point Q, - point R, int level, int *types, int *pos) +void tetgenmesh::printsh(face* sface) { - point U[3], V[3]; //, Ptmp; - int pu[3], pv[3]; //, itmp; - REAL sP, sQ, s1, s2, s3; - int z1; - - // Test the locations of P and Q with respect to ABC. - sP = orient3d(A, B, C, P); - sQ = orient3d(A, B, C, Q); - orient3dcount+=2; - - if (b->verbose > 2) { - printf(" Tri-edge (%d %d %d)-(%d %d) (%c%c).\n", pointmark(A), - pointmark(B), pointmark(C), pointmark(P), pointmark(Q), - sP>0 ? '+' : (sP<0 ? '-' : '0'), sQ>0 ? '+' : (sQ<0 ? '-' : '0')); - } - // triedgcount++; + face prtsh; + triface prttet; + point printpoint; - if (sP < 0) { - if (sQ < 0) { // (--) disjoint - return 0; - } else { - if (sQ > 0) { // (-+) - SETVECTOR3(U, A, B, C); - SETVECTOR3(V, P, Q, R); - SETVECTOR3(pu, 0, 1, 2); - SETVECTOR3(pv, 0, 1, 2); - z1 = 0; - } else { // (-0) - SETVECTOR3(U, A, B, C); - SETVECTOR3(V, P, Q, R); - SETVECTOR3(pu, 0, 1, 2); - SETVECTOR3(pv, 0, 1, 2); - z1 = 1; - } - } + if (sapex(*sface) != NULL) { + printf("subface x%lx, ver %d, mark %d:", + (unsigned long)(sface->sh), sface->shver, shellmark(*sface)); } else { - if (sP > 0) { // (+-) - if (sQ < 0) { - SETVECTOR3(U, A, B, C); - SETVECTOR3(V, Q, P, R); // P and Q are flipped. - SETVECTOR3(pu, 0, 1, 2); - SETVECTOR3(pv, 1, 0, 2); - z1 = 0; - } else { - if (sQ > 0) { // (++) disjoint - return 0; - } else { // (+0) - SETVECTOR3(U, B, A, C); // A and B are flipped. - SETVECTOR3(V, P, Q, R); - SETVECTOR3(pu, 1, 0, 2); - SETVECTOR3(pv, 0, 1, 2); - z1 = 1; - } - } - } else { // sP == 0 - if (sQ < 0) { // (0-) - SETVECTOR3(U, A, B, C); - SETVECTOR3(V, Q, P, R); // P and Q are flipped. - SETVECTOR3(pu, 0, 1, 2); - SETVECTOR3(pv, 1, 0, 2); - z1 = 1; - } else { - if (sQ > 0) { // (0+) - SETVECTOR3(U, B, A, C); // A and B are flipped. - SETVECTOR3(V, Q, P, R); // P and Q are flipped. - SETVECTOR3(pu, 1, 0, 2); - SETVECTOR3(pv, 1, 0, 2); - z1 = 1; - } else { // (00) - // A, B, C, P, and Q are coplanar. - z1 = 2; - } - } + printf("Subsegment x%lx, ver %d, mark %d:", + (unsigned long)(sface->sh), sface->shver, shellmark(*sface)); + } + if (sinfected(*sface)) { + printf(" (infected)"); + } + if (shell2badface(*sface)) { + printf(" (queued)"); + } + if (sapex(*sface) != NULL) { + if (shelltype(*sface) == SHARP) { + printf(" (sharp)"); + } + } else { + if (shelltype(*sface) == SHARP) { + printf(" (sharp)"); } } - - if (z1 == 2) { - // The triangle and the edge are coplanar. - return tri_edge_2d(A, B, C, P, Q, R, level, types, pos); + if (checkpbcs) { + if (shellpbcgroup(*sface) >= 0) { + printf(" (pbc %d)", shellpbcgroup(*sface)); + } } + printf("\n"); - s1 = orient3d(U[0], U[1], V[0], V[1]); orient3dcount++; - if (s1 < 0) { - return 0; + sdecode(sface->sh[0], prtsh); + if (prtsh.sh == dummysh) { + printf(" [0] = No shell\n"); + } else { + printf(" [0] = x%lx %d\n", (unsigned long)(prtsh.sh), prtsh.shver); } - - s2 = orient3d(U[1], U[2], V[0], V[1]); orient3dcount++; - if (s2 < 0) { - return 0; + sdecode(sface->sh[1], prtsh); + if (prtsh.sh == dummysh) { + printf(" [1] = No shell\n"); + } else { + printf(" [1] = x%lx %d\n", (unsigned long)(prtsh.sh), prtsh.shver); } - - s3 = orient3d(U[2], U[0], V[0], V[1]); orient3dcount++; - if (s3 < 0) { - return 0; + sdecode(sface->sh[2], prtsh); + if (prtsh.sh == dummysh) { + printf(" [2] = No shell\n"); + } else { + printf(" [2] = x%lx %d\n", (unsigned long)(prtsh.sh), prtsh.shver); } - if (b->verbose > 2) { - printf(" Tri-edge (%d %d %d)-(%d %d) (%c%c%c).\n", pointmark(U[0]), - pointmark(U[1]), pointmark(U[2]), pointmark(V[0]), pointmark(V[1]), - s1>0 ? '+' : (s1<0 ? '-' : '0'), s2>0 ? '+' : (s2<0 ? '-' : '0'), - s3>0 ? '+' : (s3<0 ? '-' : '0')); - } - - if (level == 0) { - return 1; // The are intersected. - } - - types[1] = (int) DISJOINT; // No second intersection point. - - if (z1 == 0) { - if (s1 > 0) { - if (s2 > 0) { - if (s3 > 0) { // (+++) - // [P, Q] passes interior of [A, B, C]. - types[0] = (int) INTERFACE; - pos[0] = 3; // interior of [A, B, C] - pos[1] = 0; // [P, Q] - } else { // s3 == 0 (++0) - // [P, Q] intersects [C, A]. - types[0] = (int) INTEREDGE; - pos[0] = pu[2]; // [C, A] - pos[1] = 0; // [P, Q] - } - } else { // s2 == 0 - if (s3 > 0) { // (+0+) - // [P, Q] intersects [B, C]. - types[0] = (int) INTEREDGE; - pos[0] = pu[1]; // [B, C] - pos[1] = 0; // [P, Q] - } else { // s3 == 0 (+00) - // [P, Q] passes C. - types[0] = (int) INTERVERT; - pos[0] = pu[2]; // C - pos[1] = 0; // [P, Q] - } - } - } else { // s1 == 0 - if (s2 > 0) { - if (s3 > 0) { // (0++) - // [P, Q] intersects [A, B]. - types[0] = (int) INTEREDGE; - pos[0] = pu[0]; // [A, B] - pos[1] = 0; // [P, Q] - } else { // s3 == 0 (0+0) - // [P, Q] passes A. - types[0] = (int) INTERVERT; - pos[0] = pu[0]; // A - pos[1] = 0; // [P, Q] - } - } else { // s2 == 0 - if (s3 > 0) { // (00+) - // [P, Q] passes B. - types[0] = (int) INTERVERT; - pos[0] = pu[1]; // B - pos[1] = 0; // [P, Q] - } else { // s3 == 0 (000) - // Impossible. - assert(0); - } - } - } - } else { // z1 == 1 - if (s1 > 0) { - if (s2 > 0) { - if (s3 > 0) { // (+++) - // Q lies in [A, B, C]. - types[0] = (int) TOUCHFACE; - pos[0] = 0; // [A, B, C] - pos[1] = pv[1]; // Q - } else { // s3 == 0 (++0) - // Q lies on [C, A]. - types[0] = (int) TOUCHEDGE; - pos[0] = pu[2]; // [C, A] - pos[1] = pv[1]; // Q - } - } else { // s2 == 0 - if (s3 > 0) { // (+0+) - // Q lies on [B, C]. - types[0] = (int) TOUCHEDGE; - pos[0] = pu[1]; // [B, C] - pos[1] = pv[1]; // Q - } else { // s3 == 0 (+00) - // Q = C. - types[0] = (int) SHAREVERTEX; - pos[0] = pu[2]; // C - pos[1] = pv[1]; // Q - } - } - } else { // s1 == 0 - if (s2 > 0) { - if (s3 > 0) { // (0++) - // Q lies on [A, B]. - types[0] = (int) TOUCHEDGE; - pos[0] = pu[0]; // [A, B] - pos[1] = pv[1]; // Q - } else { // s3 == 0 (0+0) - // Q = A. - types[0] = (int) SHAREVERTEX; - pos[0] = pu[0]; // A - pos[1] = pv[1]; // Q - } - } else { // s2 == 0 - if (s3 > 0) { // (00+) - // Q = B. - types[0] = (int) SHAREVERTEX; - pos[0] = pu[1]; // B - pos[1] = pv[1]; // Q - } else { // s3 == 0 (000) - // Impossible. - assert(0); - } - } - } - } - - return 1; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// incircle3d() 3D in-circle test. // -// // -// Return a negative value if pd is inside the circumcircle of the triangle // -// pa, pb, and pc. // -// // -/////////////////////////////////////////////////////////////////////////////// - -REAL tetgenmesh::incircle3d(point pa, point pb, point pc, point pd) -{ - REAL area2[2], n1[3], n2[3], c[3]; - REAL sign, r, d; - - // Calculate the areas of the two triangles [a, b, c] and [b, a, d]. - facenormal2(pa, pb, pc, n1, 1); - area2[0] = DOT(n1, n1); - facenormal2(pb, pa, pd, n2, 1); - 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); - } else { - // Choose [b, a, d] as the base triangle. - if (area2[1] > 0) { - circumsphere(pb, pa, pd, NULL, c, &r); - d = DIST(c, pc); + printpoint = sorg(*sface); + if (printpoint == (point) NULL) + printf(" Org [%d] = NULL\n", vo[sface->shver]); + else + printf(" Org [%d] = x%lx (%.12g,%.12g,%.12g) %d\n", + vo[sface->shver], (unsigned long)(printpoint), printpoint[0], + printpoint[1], printpoint[2], pointmark(printpoint)); + printpoint = sdest(*sface); + if (printpoint == (point) NULL) + printf(" Dest[%d] = NULL\n", vd[sface->shver]); + else + printf(" Dest[%d] = x%lx (%.12g,%.12g,%.12g) %d\n", + vd[sface->shver], (unsigned long)(printpoint), printpoint[0], + printpoint[1], printpoint[2], pointmark(printpoint)); + + if (sapex(*sface) != NULL) { + printpoint = sapex(*sface); + if (printpoint == (point) NULL) + printf(" Apex[%d] = NULL\n", va[sface->shver]); + else + printf(" Apex[%d] = x%lx (%.12g,%.12g,%.12g) %d\n", + va[sface->shver], (unsigned long)(printpoint), printpoint[0], + printpoint[1], printpoint[2], pointmark(printpoint)); + + decode(sface->sh[6], prttet); + if (prttet.tet == dummytet) { + printf(" [6] = Outer space\n"); } else { - // The four points are collinear. This case only happens on the boundary. - return 0; // Return "not inside". + printf(" [6] = x%lx %d\n", + (unsigned long)(prttet.tet), prttet.loc); + } + decode(sface->sh[7], prttet); + if (prttet.tet == dummytet) { + printf(" [7] = Outer space\n"); + } else { + printf(" [7] = x%lx %d\n", + (unsigned long)(prttet.tet), prttet.loc); } - } - - sign = d - r; - if (fabs(sign) / r < b->epsilon) { - sign = 0; - } - return sign; + sdecode(sface->sh[8], prtsh); + if (prtsh.sh == dummysh) { + printf(" [8] = No subsegment\n"); + } else { + printf(" [8] = x%lx %d\n", + (unsigned long)(prtsh.sh), prtsh.shver); + } + sdecode(sface->sh[9], prtsh); + if (prtsh.sh == dummysh) { + printf(" [9] = No subsegment\n"); + } else { + printf(" [9] = x%lx %d\n", + (unsigned long)(prtsh.sh), prtsh.shver); + } + sdecode(sface->sh[10], prtsh); + if (prtsh.sh == dummysh) { + printf(" [10]= No subsegment\n"); + } else { + printf(" [10]= x%lx %d\n", + (unsigned long)(prtsh.sh), prtsh.shver); + } + } } +// +// End of advanced primitives +// + +// +// End of mesh manipulation primitives +// + +// +// Begin of mesh items searching routines +// + /////////////////////////////////////////////////////////////////////////////// // // -// 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 orientation of the sequence {pa, pb, pc, pd} is negative (NOT zero), // -// i.e., pd lies at the negative side of the plane defined by pa, pb, and pc.// +// makepoint2tetmap() Construct a mapping from points to tetrahedra. // // // -// Return a positive value (> 0) if pe lies outside, a negative value (< 0) // -// if pe lies inside the sphere, the returned value will not be zero. // +// Traverses all the tetrahedra, provides each corner of each tetrahedron // +// with a pointer to that tetrahedera. Some pointers will be overwritten by // +// other pointers because each point may be a corner of several tetrahedra, // +// but in the end every point will point to a tetrahedron that contains it. // // // /////////////////////////////////////////////////////////////////////////////// -REAL tetgenmesh::insphere_s(REAL* pa, REAL* pb, REAL* pc, REAL* pd, REAL* pe) +void tetgenmesh::makepoint2tetmap() { - REAL sign; - - inspherecount++; + triface tetloop; + point pointptr; - sign = insphere(pa, pb, pc, pd, pe); - if (sign != 0.0) { - return sign; + if (b->verbose > 0) { + printf(" Constructing mapping from points to tetrahedra.\n"); } - 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. + // Initialize the point2tet field of each point. + points->traversalinit(); + pointptr = pointtraverse(); + while (pointptr != (point) NULL) { + setpoint2tet(pointptr, (tetrahedron) NULL); + pointptr = pointtraverse(); + } - 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; + tetrahedrons->traversalinit(); + tetloop.tet = tetrahedrontraverse(); + while (tetloop.tet != (tetrahedron *) NULL) { + // Check all four points of the tetrahedron. + tetloop.loc = 0; + pointptr = org(tetloop); + setpoint2tet(pointptr, encode(tetloop)); + pointptr = dest(tetloop); + setpoint2tet(pointptr, encode(tetloop)); + pointptr = apex(tetloop); + setpoint2tet(pointptr, encode(tetloop)); + pointptr = oppo(tetloop); + setpoint2tet(pointptr, encode(tetloop)); + // Get the next tetrahedron in the list. + tetloop.tet = tetrahedrontraverse(); } - - 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; } /////////////////////////////////////////////////////////////////////////////// // // -// iscollinear() Check if three points are approximately collinear. // +// makeindex2pointmap() Create a map from index to vertices. // // // -// 'eps' is a relative error tolerance. The collinearity is determined by // -// the value q = cos(theta), where theta is the angle between two vectors // -// A->B and A->C. They're collinear if 1.0 - q <= epspp. // +// '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. // // // /////////////////////////////////////////////////////////////////////////////// -bool tetgenmesh::iscollinear(REAL* A, REAL* B, REAL* C, REAL eps) +void tetgenmesh::makeindex2pointmap(point*& idx2verlist) { - REAL abx, aby, abz; - REAL acx, acy, acz; - REAL Lv, Lw, dd; - REAL d, q; + point pointloop; + int idx; - // Limit of two closed points. - q = longest * eps; - q *= q; + if (b->verbose > 0) { + printf(" Constructing mapping from indices to points.\n"); + } - abx = A[0] - B[0]; - aby = A[1] - B[1]; - abz = A[2] - B[2]; - acx = A[0] - C[0]; - acy = A[1] - C[1]; - acz = A[2] - C[2]; - Lv = abx * abx + aby * aby + abz * abz; - // Is AB (nearly) indentical? - if (Lv < q) return true; - Lw = acx * acx + acy * acy + acz * acz; - // Is AC (nearly) indentical? - if (Lw < q) return true; - dd = abx * acx + aby * acy + abz * acz; - - d = (dd * dd) / (Lv * Lw); - if (d > 1.0) d = 1.0; // Rounding. - q = 1.0 - sqrt(d); // Notice 0 < q < 1.0. - - return q <= eps; + idx2verlist = new point[points->items]; + + points->traversalinit(); + pointloop = pointtraverse(); + idx = 0; + while (pointloop != (point) NULL) { + idx2verlist[idx] = pointloop; + idx++; + pointloop = pointtraverse(); + } } /////////////////////////////////////////////////////////////////////////////// // // -// iscoplanar() Check if four points are approximately coplanar. // +// makesegmentmap() Create a map from vertices (their indices) to // +// segments incident at the same vertices. // // // -// 'vol6' is six times of the signed volume of the tetrahedron formed by the // -// four points. 'eps' is the relative error tolerance. The coplanarity is // -// determined by the value: q = fabs(vol6) / L^3, where L is the average // -// edge length of the tet. They're coplanar if q <= eps. // +// Two arrays 'idx2seglist' and 'segsperverlist' together return the map. // +// They form a sparse matrix structure with size (n + 1) x (n + 1), n is the // +// number of segments. idx2seglist contains row information and // +// segsperverlist contains all (non-zero) elements. The i-th entry of // +// idx2seglist is the starting position of i-th row's (non-zero) elements in // +// segsperverlist. The number of elements of i-th row is calculated by the // +// (i+1)-th entry minus i-th entry of idx2seglist. // +// // +// NOTE: These two arrays will be created inside this routine, don't forget // +// to free them after using. // // // /////////////////////////////////////////////////////////////////////////////// -bool tetgenmesh:: -iscoplanar(REAL* k, REAL* l, REAL* m, REAL* n, REAL vol6, REAL eps) +void tetgenmesh::makesegmentmap(int*& idx2seglist, shellface**& segsperverlist) { - REAL L, q; - REAL x, y, z; + shellface *shloop; + int i, j, k; - if (vol6 == 0.0) return true; + if (b->verbose > 0) { + printf(" Constructing mapping from points to segments.\n"); + } - x = k[0] - l[0]; - y = k[1] - l[1]; - z = k[2] - l[2]; - L = sqrt(x * x + y * y + z * z); - x = l[0] - m[0]; - y = l[1] - m[1]; - z = l[2] - m[2]; - L += sqrt(x * x + y * y + z * z); - x = m[0] - k[0]; - y = m[1] - k[1]; - z = m[2] - k[2]; - L += sqrt(x * x + y * y + z * z); - x = k[0] - n[0]; - y = k[1] - n[1]; - z = k[2] - n[2]; - L += sqrt(x * x + y * y + z * z); - x = l[0] - n[0]; - y = l[1] - n[1]; - z = l[2] - n[2]; - L += sqrt(x * x + y * y + z * z); - x = m[0] - n[0]; - y = m[1] - n[1]; - z = m[2] - n[2]; - L += sqrt(x * x + y * y + z * z); -#ifdef SELF_CHECK - assert(L > 0.0); -#endif - L /= 6.0; - q = fabs(vol6) / (L * L * L); - - return q <= eps; -} + // Create and initialize 'idx2seglist'. + idx2seglist = new int[points->items + 1]; + for (i = 0; i < points->items + 1; i++) idx2seglist[i] = 0; -/////////////////////////////////////////////////////////////////////////////// -// // -// iscospheric() Check if five points are approximately coplanar. // -// // -// 'vol24' is the 24 times of the signed volume of the 4-dimensional simplex // -// formed by the five points. 'eps' is the relative tolerance. The cosphere // -// case is determined by the value: q = fabs(vol24) / L^4, where L is the // -// average edge length of the simplex. They're cosphere if q <= eps. // + // Loop the set of segments once, counter the number of segments sharing + // each vertex. + subsegs->traversalinit(); + shloop = shellfacetraverse(subsegs); + while (shloop != (shellface *) NULL) { + // Increment the number of sharing segments for each endpoint. + for (i = 0; i < 2; i++) { + j = pointmark((point) shloop[3 + i]) - in->firstnumber; + idx2seglist[j]++; + } + shloop = shellfacetraverse(subsegs); + } + + // Calculate the total length of array 'facesperverlist'. + j = idx2seglist[0]; + idx2seglist[0] = 0; // Array starts from 0 element. + for (i = 0; i < points->items; i++) { + k = idx2seglist[i + 1]; + idx2seglist[i + 1] = idx2seglist[i] + j; + j = k; + } + // The total length is in the last unit of idx2seglist. + segsperverlist = new shellface*[idx2seglist[i]]; + // Loop the set of segments again, set the info. of segments per vertex. + subsegs->traversalinit(); + shloop = shellfacetraverse(subsegs); + while (shloop != (shellface *) NULL) { + for (i = 0; i < 2; i++) { + j = pointmark((point) shloop[3 + i]) - in->firstnumber; + segsperverlist[idx2seglist[j]] = shloop; + idx2seglist[j]++; + } + shloop = shellfacetraverse(subsegs); + } + // Contents in 'idx2seglist' are shifted, now shift them back. + for (i = points->items - 1; i >= 0; i--) { + idx2seglist[i + 1] = idx2seglist[i]; + } + idx2seglist[0] = 0; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// makesubfacemap() Create a map from vertices (their indices) to // +// subfaces incident at the same vertices. // +// // +// Two arrays 'idx2facelist' and 'facesperverlist' together return the map. // +// They form a sparse matrix structure with size (n + 1) x (n + 1), n is the // +// number of subfaces. idx2facelist contains row information and // +// facesperverlist contains all (non-zero) elements. The i-th entry of // +// idx2facelist is the starting position of i-th row's(non-zero) elements in // +// facesperverlist. The number of elements of i-th row is calculated by the // +// (i+1)-th entry minus i-th entry of idx2facelist. // +// // +// NOTE: These two arrays will be created inside this routine, don't forget // +// to free them after using. // // // /////////////////////////////////////////////////////////////////////////////// -bool tetgenmesh:: -iscospheric(REAL* k, REAL* l, REAL* m, REAL* n, REAL* o, REAL vol24, REAL eps) +void tetgenmesh:: +makesubfacemap(int*& idx2facelist, shellface**& facesperverlist) { - REAL L, q; + shellface *shloop; + int i, j, k; - // A 4D simplex has 10 edges. - L = distance(k, l); - L += distance(l, m); - L += distance(m, k); - L += distance(k, n); - L += distance(l, n); - L += distance(m, n); - L += distance(k, o); - L += distance(l, o); - L += distance(m, o); - L += distance(n, o); -#ifdef SELF_CHECK - assert(L > 0.0); -#endif - L /= 10.0; - q = fabs(vol24) / (L * L * L * L); + if (b->verbose > 0) { + printf(" Constructing mapping from points to subfaces.\n"); + } - return q < eps; + // Create and initialize 'idx2facelist'. + idx2facelist = new int[points->items + 1]; + for (i = 0; i < points->items + 1; i++) idx2facelist[i] = 0; + + // Loop the set of subfaces once, counter the number of subfaces sharing + // each vertex. + subfaces->traversalinit(); + shloop = shellfacetraverse(subfaces); + while (shloop != (shellface *) NULL) { + // Increment the number of sharing segments for each endpoint. + for (i = 0; i < 3; i++) { + j = pointmark((point) shloop[3 + i]) - in->firstnumber; + idx2facelist[j]++; + } + shloop = shellfacetraverse(subfaces); + } + + // Calculate the total length of array 'facesperverlist'. + j = idx2facelist[0]; + idx2facelist[0] = 0; // Array starts from 0 element. + for (i = 0; i < points->items; i++) { + k = idx2facelist[i + 1]; + idx2facelist[i + 1] = idx2facelist[i] + j; + j = k; + } + // The total length is in the last unit of idx2facelist. + facesperverlist = new shellface*[idx2facelist[i]]; + // Loop the set of segments again, set the info. of segments per vertex. + subfaces->traversalinit(); + shloop = shellfacetraverse(subfaces); + while (shloop != (shellface *) NULL) { + for (i = 0; i < 3; i++) { + j = pointmark((point) shloop[3 + i]) - in->firstnumber; + facesperverlist[idx2facelist[j]] = shloop; + idx2facelist[j]++; + } + shloop = shellfacetraverse(subfaces); + } + // Contents in 'idx2facelist' are shifted, now shift them back. + for (i = points->items - 1; i >= 0; i--) { + idx2facelist[i + 1] = idx2facelist[i]; + } + idx2facelist[0] = 0; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// maketetrahedronmap() Create a map from vertices (their indices) to // +// tetrahedra incident at the same vertices. // +// // +// Two arrays 'idx2tetlist' and 'tetsperverlist' together return the map. // +// They form a sparse matrix structure with size (n + 1) x (n + 1), n is the // +// number of tetrahedra. idx2tetlist contains row information and // +// tetsperverlist contains all (non-zero) elements. The i-th entry of // +// idx2tetlist is the starting position of i-th row's (non-zero) elements in // +// tetsperverlist. The number of elements of i-th row is calculated by the // +// (i+1)-th entry minus i-th entry of idx2tetlist. // +// // +// NOTE: These two arrays will be created inside this routine, don't forget // +// to free them after using. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh:: +maketetrahedronmap(int*& idx2tetlist, tetrahedron**& tetsperverlist) +{ + tetrahedron *tetloop; + int i, j, k; + + if (b->verbose > 0) { + printf(" Constructing mapping from points to tetrahedra.\n"); + } + + // Create and initialize 'idx2tetlist'. + idx2tetlist = new int[points->items + 1]; + for (i = 0; i < points->items + 1; i++) idx2tetlist[i] = 0; + + // Loop the set of tetrahedra once, counter the number of tetrahedra + // sharing each vertex. + tetrahedrons->traversalinit(); + tetloop = tetrahedrontraverse(); + while (tetloop != (tetrahedron *) NULL) { + // Increment the number of sharing tetrahedra for each endpoint. + for (i = 0; i < 4; i++) { + j = pointmark((point) tetloop[4 + i]) - in->firstnumber; + idx2tetlist[j]++; + } + tetloop = tetrahedrontraverse(); + } + + // Calculate the total length of array 'tetsperverlist'. + j = idx2tetlist[0]; + idx2tetlist[0] = 0; // Array starts from 0 element. + for (i = 0; i < points->items; i++) { + k = idx2tetlist[i + 1]; + idx2tetlist[i + 1] = idx2tetlist[i] + j; + j = k; + } + // The total length is in the last unit of idx2tetlist. + tetsperverlist = new tetrahedron*[idx2tetlist[i]]; + // Loop the set of tetrahedra again, set the info. of tet. per vertex. + tetrahedrons->traversalinit(); + tetloop = tetrahedrontraverse(); + while (tetloop != (tetrahedron *) NULL) { + for (i = 0; i < 4; i++) { + j = pointmark((point) tetloop[4 + i]) - in->firstnumber; + tetsperverlist[idx2tetlist[j]] = tetloop; + idx2tetlist[j]++; + } + tetloop = tetrahedrontraverse(); + } + // Contents in 'idx2tetlist' are shifted, now shift them back. + for (i = points->items - 1; i >= 0; i--) { + idx2tetlist[i + 1] = idx2tetlist[i]; + } + idx2tetlist[0] = 0; +} + +// +// End of mesh items searching routines +// + +// +// Begin of linear algebra functions +// + +// 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]; + n[1] = -(v1[0] * v2[2] - v2[0] * v1[2]); + n[2] = v1[0] * v2[1] - v2[0] * v1[1]; +} + +// initm44() initializes a 4x4 matrix. +static void initm44(REAL a00, REAL a01, REAL a02, REAL a03, + REAL a10, REAL a11, REAL a12, REAL a13, + REAL a20, REAL a21, REAL a22, REAL a23, + REAL a30, REAL a31, REAL a32, REAL a33, + REAL M[4][4]) +{ + M[0][0] = a00; M[0][1] = a01; M[0][2] = a02; M[0][3] = a03; + M[1][0] = a10; M[1][1] = a11; M[1][2] = a12; M[1][3] = a13; + M[2][0] = a20; M[2][1] = a21; M[2][2] = a22; M[2][3] = a23; + M[3][0] = a30; M[3][1] = a31; M[3][2] = a32; M[3][3] = a33; +} + +// m4xm4() multiplies 2 4x4 matrics: m1 = m1 * m2. +static void m4xm4(REAL m1[4][4], REAL m2[4][4]) +{ + REAL tmp[4]; + int i, j; + + for (i = 0; i < 4; i++) { // i-th row + for (j = 0; j < 4; j++) { // j-th col + tmp[j] = m1[i][0] * m2[0][j] + m1[i][1] * m2[1][j] + + m1[i][2] * m2[2][j] + m1[i][3] * m2[3][j]; + } + for (j = 0; j < 4; j++) + m1[i][j] = tmp[j]; + } +} + +// m4xv4() multiplies a 4x4 matrix and 4x1 vector: v2 = m * v1 +static void m4xv4(REAL v2[4], REAL m[4][4], REAL v1[4]) +{ + v2[0] = m[0][0]*v1[0] + m[0][1]*v1[1] + m[0][2]*v1[2] + m[0][3]*v1[3]; + v2[1] = m[1][0]*v1[0] + m[1][1]*v1[1] + m[1][2]*v1[2] + m[1][3]*v1[3]; + v2[2] = m[2][0]*v1[0] + m[2][1]*v1[1] + m[2][2]*v1[2] + m[2][3]*v1[3]; + v2[3] = m[3][0]*v1[0] + m[3][1]*v1[1] + m[3][2]*v1[2] + m[3][3]*v1[3]; } /////////////////////////////////////////////////////////////////////////////// @@ -7349,2455 +6317,4073 @@ void tetgenmesh::lu_solve(REAL lu[4][4], int n, int* ps, REAL* b, int N) for (i = N; i < n + N; i++) b[i] = X[i]; } -// initm44() initializes a 4x4 matrix. -static void initm44(REAL a00, REAL a01, REAL a02, REAL a03, - REAL a10, REAL a11, REAL a12, REAL a13, - REAL a20, REAL a21, REAL a22, REAL a23, - REAL a30, REAL a31, REAL a32, REAL a33, - REAL M[4][4]) -{ - M[0][0] = a00; M[0][1] = a01; M[0][2] = a02; M[0][3] = a03; - M[1][0] = a10; M[1][1] = a11; M[1][2] = a12; M[1][3] = a13; - M[2][0] = a20; M[2][1] = a21; M[2][2] = a22; M[2][3] = a23; - M[3][0] = a30; M[3][1] = a31; M[3][2] = a32; M[3][3] = a33; -} +// +// End of linear algebra functions +// -// m4xv4() multiplies a 4x4 matrix and 4x1 vector: v2 = m * v1 -static void m4xv4(REAL v2[4], REAL m[4][4], REAL v1[4]) -{ - v2[0] = m[0][0]*v1[0] + m[0][1]*v1[1] + m[0][2]*v1[2] + m[0][3]*v1[3]; - v2[1] = m[1][0]*v1[0] + m[1][1]*v1[1] + m[1][2]*v1[2] + m[1][3]*v1[3]; - v2[2] = m[2][0]*v1[0] + m[2][1]*v1[1] + m[2][2]*v1[2] + m[2][3]*v1[3]; - v2[3] = m[3][0]*v1[0] + m[3][1]*v1[1] + m[3][2]*v1[2] + m[3][3]*v1[3]; -} +// +// Begin of geometric tests +// + +// All the following routines require the input objects are not degenerate. +// i.e., a triangle must has three non-collinear corners; an edge must +// has two identical endpoints. Degenerate cases should have to detect +// first and then handled as special cases. /////////////////////////////////////////////////////////////////////////////// // // -// shortdistance() Returns the shortest distance from point p to a line // -// defined by two points e1 and e2. // +// edge_vert_col_inter() Test whether an edge (ab) and a collinear vertex // +// (p) are intersecting or not. // // // -// 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. // +// Possible cases are p is coincident to a (p = a), or to b (p = b), or p is // +// inside ab (a < p < b), or outside ab (p < a or p > b). These cases can be // +// quickly determined by comparing the corresponding coords of a, b, and p // +// (which are not all equal). // // // -// 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. // +// The return value indicates one of the three cases: DISJOINT, SHAREVERTEX // +// (p = a or p = b), and INTERSECT (a < p < b). // // // /////////////////////////////////////////////////////////////////////////////// -REAL tetgenmesh::shortdistance(REAL* p, REAL* e1, REAL* e2) +enum tetgenmesh::interresult tetgenmesh::edge_vert_col_inter(REAL* A, REAL* B, + REAL* P) { - REAL v1[3], v2[3]; - REAL len, l_p; - - v1[0] = e2[0] - e1[0]; - v1[1] = e2[1] - e1[1]; - v1[2] = e2[2] - e1[2]; - v2[0] = p[0] - e1[0]; - v2[1] = p[1] - e1[1]; - 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; - l_p = dot(v1, v2); - - return sqrt(dot(v2, v2) - l_p * l_p); + int i = 0; + do { + if (A[i] < B[i]) { + if (P[i] < A[i]) { + return DISJOINT; + } else if (P[i] > A[i]) { + if (P[i] < B[i]) { + return INTERSECT; + } else if (P[i] > B[i]) { + return DISJOINT; + } else { + // assert(P[i] == B[i]); + return SHAREVERTEX; + } + } else { + // assert(P[i] == A[i]); + return SHAREVERTEX; + } + } else if (A[i] > B[i]) { + if (P[i] < B[i]) { + return DISJOINT; + } else if (P[i] > B[i]) { + if (P[i] < A[i]) { + return INTERSECT; + } else if (P[i] > A[i]) { + return DISJOINT; + } else { + // assert(P[i] == A[i]); + return SHAREVERTEX; + } + } else { + // assert(P[i] == B[i]); + return SHAREVERTEX; + } + } + // i-th coordinates are equal, try i+1-th; + i++; + } while (i < 3); + // Should never be here. + return DISJOINT; } /////////////////////////////////////////////////////////////////////////////// // // -// shortdistance() Returns the shortest distance from point p to a face. // +// edge_edge_cop_inter() Test whether two coplanar edges (ab, and pq) are // +// intersecting or not. // // // -/////////////////////////////////////////////////////////////////////////////// - -REAL tetgenmesh::shortdistance(REAL* p, REAL* e1, REAL* e2, REAL* e3) -{ - REAL prj[3]; - - projpt2face(p, e1, e2, e3, prj); - return distance(p, prj); -} - -/////////////////////////////////////////////////////////////////////////////// +// Possible cases are ab and pq are disjointed, or proper intersecting (int- // +// ersect at a point other than their endpoints), or both collinear and int- // +// ersecting, or sharing at a common endpoint, or are coincident. // // // -// interiorangle() Return the interior angle (0 - 2 * PI) between vectors // -// o->p1 and o->p2. // +// A reference point R is required, which is exactly not coplanar with these // +// two edges. Since the caller knows these two edges are coplanar, it must // +// be able to provide (or calculate) such a point. // // // -// 'n' is the normal of the plane containing face (o, p1, p2). The interior // -// angle is the total angle rotating from o->p1 around n to o->p2. Exchange // -// the position of p1 and p2 will get the complement angle of the other one. // -// i.e., interiorangle(o, p1, p2) = 2 * PI - interiorangle(o, p2, p1). Set // -// 'n' be NULL if you only want the interior angle between 0 - PI. // +// The return value indicates one of the four cases: DISJOINT, SHAREVERTEX, // +// SHAREEDGE, and INTERSECT. // // // /////////////////////////////////////////////////////////////////////////////// -REAL tetgenmesh::interiorangle(REAL* o, REAL* p1, REAL* p2, REAL* n) +enum tetgenmesh::interresult tetgenmesh:: edge_edge_cop_inter(REAL* A, REAL* B, + REAL* P, REAL* Q, REAL* R) { - REAL v1[3], v2[3], np[3]; - REAL theta, costheta, lenlen; - REAL ori, len1, len2; + REAL s1, s2, s3, s4; - // Get the interior angle (0 - PI) between o->p1, and o->p2. - v1[0] = p1[0] - o[0]; - v1[1] = p1[1] - o[1]; - v1[2] = p1[2] - o[2]; - v2[0] = p2[0] - o[0]; - v2[1] = p2[1] - o[1]; - v2[2] = p2[2] - o[2]; - len1 = sqrt(dot(v1, v1)); - len2 = sqrt(dot(v2, v2)); - lenlen = len1 * len2; #ifdef SELF_CHECK - assert(lenlen != 0.0); + assert(R != NULL); #endif - costheta = dot(v1, v2) / lenlen; - if (costheta > 1.0) { - costheta = 1.0; // Roundoff. - } else if (costheta < -1.0) { - costheta = -1.0; // Roundoff. + s1 = orient3d(A, B, R, P); + s2 = orient3d(A, B, R, Q); + if (s1 * s2 > 0.0) { + // Both p and q are at the same side of ab. + return DISJOINT; } - theta = acos(costheta); - if (n != NULL) { - // Get a point above the face (o, p1, p2); - np[0] = o[0] + n[0]; - np[1] = o[1] + n[1]; - np[2] = o[2] + n[2]; - // Adjust theta (0 - 2 * PI). - ori = orient3d(p1, o, np, p2); - if (ori > 0.0) { - theta = 2 * PI - theta; + s3 = orient3d(P, Q, R, A); + s4 = orient3d(P, Q, R, B); + if (s3 * s4 > 0.0) { + // Both a and b are at the same side of pq. + return DISJOINT; + } + + // Possible degenerate cases are: + // (1) Only one of p and q is collinear with ab; + // (2) Both p and q are collinear with ab; + // (3) Only one of a and b is collinear with pq. + enum interresult abp, abq; + enum interresult pqa, pqb; + + if (s1 == 0.0) { + // p is collinear with ab. + abp = edge_vert_col_inter(A, B, P); + if (abp == INTERSECT) { + // p is inside ab. + return INTERSECT; + } + if (s2 == 0.0) { + // q is collinear with ab. Case (2). + abq = edge_vert_col_inter(A, B, Q); + if (abq == INTERSECT) { + // q is inside ab. + return INTERSECT; + } + if (abp == SHAREVERTEX && abq == SHAREVERTEX) { + // ab and pq are identical. + return SHAREEDGE; + } + pqa = edge_vert_col_inter(P, Q, A); + if (pqa == INTERSECT) { + // a is inside pq. + return INTERSECT; + } + pqb = edge_vert_col_inter(P, Q, B); + if (pqb == INTERSECT) { + // b is inside pq. + return INTERSECT; + } + if (abp == SHAREVERTEX || abq == SHAREVERTEX) { + // either p or q is coincident with a or b. +#ifdef SELF_CHECK + // ONLY one case is possible, otherwise, shoule be SHAREEDGE. + assert(abp ^ abq); +#endif + return SHAREVERTEX; + } + // The last case. They are disjointed. +#ifdef SELF_CHECK + assert((abp == DISJOINT) && (abp == abq && abq == pqa && pqa == pqb)); +#endif + return DISJOINT; + } else { + // p is collinear with ab. Case (1). +#ifdef SELF_CHECK + assert(abp == SHAREVERTEX || abp == DISJOINT); +#endif + return abp; } } + // p is NOT collinear with ab. + if (s2 == 0.0) { + // q is collinear with ab. Case (1). + abq = edge_vert_col_inter(A, B, Q); +#ifdef SELF_CHECK + assert(abq == SHAREVERTEX || abq == DISJOINT || abq == INTERSECT); +#endif + return abq; + } - return theta; + // We have found p and q are not collinear with ab. However, it is still + // possible that a or b is collinear with pq (ONLY one of a and b). + if (s3 == 0.0) { + // a is collinear with pq. Case (3). +#ifdef SELF_CHECK + assert(s4 != 0.0); +#endif + pqa = edge_vert_col_inter(P, Q, A); +#ifdef SELF_CHECK + // This case should have been detected in above. + assert(pqa != SHAREVERTEX); + assert(pqa == INTERSECT || pqa == DISJOINT); +#endif + return pqa; + } + if (s4 == 0.0) { + // b is collinear with pq. Case (3). +#ifdef SELF_CHECK + assert(s3 != 0.0); +#endif + pqb = edge_vert_col_inter(P, Q, B); +#ifdef SELF_CHECK + // This case should have been detected in above. + assert(pqb != SHAREVERTEX); + assert(pqb == INTERSECT || pqb == DISJOINT); +#endif + return pqb; + } + + // ab and pq are intersecting properly. + return INTERSECT; } /////////////////////////////////////////////////////////////////////////////// // // -// projpt2edge() Return the projection point from a point to an edge. // +// Notations // +// // +// Let ABC be the plane passes through a, b, and c; ABC+ be the halfspace // +// including the set of all points x, such that orient3d(a, b, c, x) > 0; // +// ABC- be the other halfspace, such that for each point x in ABC-, // +// orient3d(a, b, c, x) < 0. For the set of x which are on ABC, orient3d(a, // +// b, c, x) = 0. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::projpt2edge(REAL* p, REAL* e1, REAL* e2, REAL* prj) -{ - REAL v1[3], v2[3]; - REAL len, l_p; - - v1[0] = e2[0] - e1[0]; - v1[1] = e2[1] - e1[1]; - v1[2] = e2[2] - e1[2]; - v2[0] = p[0] - e1[0]; - v2[1] = p[1] - e1[1]; - 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; - l_p = dot(v1, v2); - - prj[0] = e1[0] + l_p * v1[0]; - prj[1] = e1[1] + l_p * v1[1]; - prj[2] = e1[2] + l_p * v1[2]; -} - /////////////////////////////////////////////////////////////////////////////// // // -// projpt2face() Return the projection point from a point to a face. // +// tri_vert_copl_inter() Test whether a triangle (abc) and a coplanar // +// point (p) are intersecting or not. // // // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::projpt2face(REAL* p, REAL* f1, REAL* f2, REAL* f3, REAL* prj) -{ - REAL fnormal[3], v1[3]; - REAL len, dist; - - // Get the unit face normal. - // facenormal(f1, f2, f3, fnormal, &len); - facenormal2(f1, f2, f3, fnormal, 1); - len = sqrt(fnormal[0]*fnormal[0] + fnormal[1]*fnormal[1] + - fnormal[2]*fnormal[2]); - fnormal[0] /= len; - fnormal[1] /= len; - fnormal[2] /= len; - // Get the vector v1 = |p - f1|. - v1[0] = p[0] - f1[0]; - v1[1] = p[1] - f1[1]; - v1[2] = p[2] - f1[2]; - // Get the project distance. - dist = dot(fnormal, v1); - - // Get the project point. - prj[0] = p[0] - dist * fnormal[0]; - prj[1] = p[1] - dist * fnormal[1]; - prj[2] = p[2] - dist * fnormal[2]; -} - -/////////////////////////////////////////////////////////////////////////////// +// Possible cases are p is inside abc, or on an edge of, or coincident with // +// a vertex of, or outside abc. // // // -// facenormal() Calculate the normal of a face given by three points. // +// A reference point R is required. R is exactly not coplanar with abc and p.// +// Since the caller knows they are coplanar, it must be able to provide (or // +// calculate) such a point. // // // -// In general, the face normal can be calculate by the cross product of any // -// pair of the three edge vectors. However, if the three points are nearly // -// collinear, the rounding error may harm the result. To choose a good pair // -// of vectors is helpful to reduce the error. // +// The return value indicates one of the four cases: DISJOINT, SHAREVERTEX, // +// and INTERSECT. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::facenormal(REAL* pa, REAL* pb, REAL* pc, REAL* n, REAL* nlen) +enum tetgenmesh::interresult tetgenmesh::tri_vert_cop_inter(REAL* A, REAL* B, + REAL* C, REAL* P, REAL* R) { - REAL v1[3], v2[3]; + REAL s1, s2, s3; + int sign; - v1[0] = pb[0] - pa[0]; - v1[1] = pb[1] - pa[1]; - v1[2] = pb[2] - pa[2]; - v2[0] = pc[0] - pa[0]; - v2[1] = pc[1] - pa[1]; - v2[2] = pc[2] - pa[2]; +#ifdef SELF_CHECK + assert(R != (REAL *) NULL); +#endif + // Adjust the orientation of a, b, c and r, so that we can assume that + // r is strictly in ABC- (i.e., r is above ABC wrt. right-hand rule). + s1 = orient3d(A, B, C, R); +#ifdef SELF_CHECK + assert(s1 != 0.0); +#endif + sign = s1 < 0.0 ? 1 : -1; - cross(v1, v2, n); - if (nlen != (REAL *) NULL) { - *nlen = sqrt(dot(n, n)); + // Test starts from here. + s1 = orient3d(A, B, R, P) * sign; + if (s1 < 0.0) { + // p is in ABR-. + return DISJOINT; + } + s2 = orient3d(B, C, R, P) * sign; + if (s2 < 0.0) { + // p is in BCR-. + return DISJOINT; + } + s3 = orient3d(C, A, R, P) * sign; + if (s3 < 0.0) { + // p is in CAR-. + return DISJOINT; + } + if (s1 == 0.0) { + // p is on ABR. + if (s2 == 0.0) { + // p is on BCR. +#ifdef SELF_CHECK + assert(s3 > 0.0); +#endif + // p is coincident with b. + return SHAREVERTEX; + } + if (s3 == 0.0) { + // p is on CAR. + // p is coincident with a. + return SHAREVERTEX; + } + // p is on edge ab. + return INTERSECT; + } + // p is in ABR+. + if (s2 == 0.0) { + // p is on BCR. + if (s3 == 0.0) { + // p is on CAR. + // p is coincident with c. + return SHAREVERTEX; + } + // p is on edge bc. + return INTERSECT; + } + if (s3 == 0.0) { + // p is on CAR. + // p is on edge ca. + return INTERSECT; } + + // p is strictly inside abc. + return INTERSECT; } /////////////////////////////////////////////////////////////////////////////// // // -// facenormal() Calculate the normal of the face. // +// tri_edge_cop_inter() Test whether a triangle (abc) and a coplanar edge // +// (pq) are intersecting or not. // +// // +// A reference point R is required. R is exactly not coplanar with abc and // +// pq. Since the caller knows they are coplanar, it must be able to provide // +// (or calculate) such a point. // // // -// 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. // +// The return value indicates one of the four cases: DISJOINT, SHAREVERTEX, // +// SHAREEDGE, and INTERSECT. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::facenormal2(point pa, point pb, point pc, REAL *n, int pivot) +enum tetgenmesh::interresult tetgenmesh::tri_edge_cop_inter(REAL* A, REAL* B, + REAL* C, REAL* P, REAL* Q, REAL* R) { - REAL v1[3], v2[3], v3[3], *pv1, *pv2; - REAL L1, L2, L3; + enum interresult abpq, bcpq, capq; + enum interresult abcp, abcq; - 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). - } - } - } else { - pv1 = v1; pv2 = v2; // n = v1 x (-v2). + // Test if pq is intersecting one of edges of abc. + abpq = edge_edge_cop_inter(A, B, P, Q, R); + if (abpq == INTERSECT || abpq == SHAREEDGE) { + return abpq; + } + bcpq = edge_edge_cop_inter(B, C, P, Q, R); + if (bcpq == INTERSECT || bcpq == SHAREEDGE) { + return bcpq; + } + capq = edge_edge_cop_inter(C, A, P, Q, R); + if (capq == INTERSECT || capq == SHAREEDGE) { + return capq; + } + + // Test if p and q is inside abc. + abcp = tri_vert_cop_inter(A, B, C, P, R); + if (abcp == INTERSECT) { + return INTERSECT; + } + abcq = tri_vert_cop_inter(A, B, C, Q, R); + if (abcq == INTERSECT) { + return INTERSECT; + } + + // Combine the test results of edge intersectings and triangle insides + // to detect whether abc and pq are sharing vertex or disjointed. + if (abpq == SHAREVERTEX) { + // p or q is coincident with a or b. +#ifdef SELF_CHECK + assert(abcp ^ abcq); +#endif + return SHAREVERTEX; + } + if (bcpq == SHAREVERTEX) { + // p or q is coincident with b or c. +#ifdef SELF_CHECK + assert(abcp ^ abcq); +#endif + return SHAREVERTEX; + } + if (capq == SHAREVERTEX) { + // p or q is coincident with c or a. +#ifdef SELF_CHECK + assert(abcp ^ abcq); +#endif + return SHAREVERTEX; } - // Calculate the face normal. - CROSS(pv1, pv2, n); - // Inverse the direction; - n[0] = -n[0]; - n[1] = -n[1]; - n[2] = -n[2]; + // They are disjointed. + return DISJOINT; } /////////////////////////////////////////////////////////////////////////////// // // -// edgeorthonormal() Return the unit normal of an edge in a given plane. // +// tri_edge_inter_tail() Test whether a triangle (abc) and an edge (pq) // +// are intersecting or not. // // // -// The edge is from e1 to e2, the plane is defined by given an additional // -// point op, which is non-collinear with the edge. In addition, the side of // -// the edge in which op lies defines the positive position of the normal. // +// s1 and s2 are results of pre-performed orientation tests. s1 = orient3d( // +// a, b, c, p); s2 = orient3d(a, b, c, q). To separate this routine from // +// tri_edge_inter() can save two orientation tests in tri_tri_inter(). // // // -// Let v1 be the unit vector from e1 to e2, v2 be the unit edge vector from // -// e1 to op, fn be the unit face normal calculated by fn = v1 x v2. Then the // -// unit edge normal of e1e2 pointing to op is n = fn x v1. Note, we should // -// not change the position of fn and v1, otherwise, we get the edge normal // -// pointing to the other side of op. // +// The return value indicates one of the four cases: DISJOINT, SHAREVERTEX, // +// SHAREEDGE, and INTERSECT. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::edgeorthonormal(REAL* e1, REAL* e2, REAL* op, REAL* n) +enum tetgenmesh::interresult tetgenmesh::tri_edge_inter_tail(REAL* A, REAL* B, + REAL* C, REAL* P, REAL* Q, REAL s1, REAL s2) { - REAL v1[3], v2[3], fn[3]; - REAL len; - - // Get the edge vector v1. - v1[0] = e2[0] - e1[0]; - v1[1] = e2[1] - e1[1]; - v1[2] = e2[2] - e1[2]; - // Get the edge vector v2. - v2[0] = op[0] - e1[0]; - v2[1] = op[1] - e1[1]; - v2[2] = op[2] - e1[2]; - // Get the face normal fn = v1 x v2. - cross(v1, v2, fn); - // Get the edge normal n pointing to op. n = fn x v1. - cross(fn, v1, n); - // Normalize the vector. - len = sqrt(dot(n, n)); - n[0] /= len; - n[1] /= len; - n[2] /= len; -} + REAL s3, s4, s5; + int sign; -/////////////////////////////////////////////////////////////////////////////// -// // -// facedihedral() Return the dihedral angle (in radian) between two // -// adjoining faces. // -// // -// 'pa', 'pb' are the shared edge of these two faces, 'pc1', and 'pc2' are // -// apexes of these two faces. Return the angle (between 0 to 2*pi) between // -// the normal of face (pa, pb, pc1) and normal of face (pa, pb, pc2). // -// // -/////////////////////////////////////////////////////////////////////////////// + if (s1 * s2 > 0.0) { + // p, q are at the same halfspace of ABC, no intersection. + return DISJOINT; + } -REAL tetgenmesh::facedihedral(REAL* pa, REAL* pb, REAL* pc1, REAL* pc2) -{ - REAL n1[3], n2[3]; - REAL n1len, n2len; - REAL costheta, ori; - REAL theta; - - facenormal(pa, pb, pc1, n1, &n1len); - facenormal(pa, pb, pc2, n2, &n2len); - costheta = dot(n1, n2) / (n1len * n2len); - // Be careful rounding error! - if (costheta > 1.0) { - costheta = 1.0; - } else if (costheta < -1.0) { - costheta = -1.0; - } - theta = acos(costheta); - ori = orient3d(pa, pb, pc1, pc2); - if (ori > 0.0) { - theta = 2 * PI - theta; - } - - return theta; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// tetalldihedral() Get all (six) dihedral angles of a tet. // -// // -// The tet is given by its four corners a, b, c, and d. If 'cosdd' is not // -// NULL, it returns the cosines of the 6 dihedral angles, the corresponding // -// edges are: ab, bc, ca, ad, bd, and cd. If 'cosmaxd' (or 'cosmind') is not // -// NULL, it returns the cosine of the maximal (or minimal) dihedral angle. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::tetalldihedral(point pa, point pb, point pc, point pd, - REAL* cosdd, REAL* cosmaxd, REAL* cosmind) -{ - REAL N[4][3], vol, cosd, len; - int f1, f2, i, j; - - vol = 0; // Check if the tet is valid or not. - - // Get four normals of faces of the tet. - tetallnormal(pa, pb, pc, pd, N, &vol); - - if (vol == 0.0) { - // This tet is not valid. - if (cosdd != NULL) { - for (i = 0; i < 6; i++) { - cosdd[i] = -1.0; // 180 degree. + if (s1 * s2 < 0.0) { + // p, q are both not on ABC (and not sharing vertices, edges of abc). + // Adjust the orientation of a, b, c and p, so that we can assume that + // p is strictly in ABC-, and q is strictly in ABC+. + sign = s1 < 0.0 ? 1 : -1; + s3 = orient3d(A, B, P, Q) * sign; + if (s3 < 0.0) { + // q is at ABP-. + return DISJOINT; + } + s4 = orient3d(B, C, P, Q) * sign; + if (s4 < 0.0) { + // q is at BCP-. + return DISJOINT; + } + s5 = orient3d(C, A, P, Q) * sign; + if (s5 < 0.0) { + // q is at CAP-. + return DISJOINT; + } + if (s3 == 0.0) { + // q is on ABP. + if (s4 == 0.0) { + // q is on BCP (and q must in CAP+). +#ifdef SELF_CHECK + assert(s5 > 0.0); +#endif + // pq intersects abc at vertex b. + return SHAREVERTEX; } + if (s5 == 0.0) { + // q is on CAP (and q must in BCP+). + // pq intersects abc at vertex a. + return SHAREVERTEX; + } + // q in both BCP+ and CAP+. + // pq crosses ab properly. + return INTERSECT; } - // This tet has zero volume. - if (cosmaxd != NULL) { - *cosmaxd = -1.0; // 180 degree. + // q is in ABP+; + if (s4 == 0.0) { + // q is on BCP. + if (s5 == 0.0) { + // q is on CAP. + // pq intersects abc at vertex c. + return SHAREVERTEX; + } + // pq crosses bc properly. + return INTERSECT; } - if (cosmind != NULL) { - *cosmind = 1.0; // 0 degree. + // q is in BCP+; + if (s5 == 0.0) { + // q is on CAP. + // pq crosses ca properly. + return INTERSECT; } - return; + // q is in CAP+; + // pq crosses abc properly. + return INTERSECT; } - // Normalize the normals. - for (i = 0; i < 4; i++) { - len = sqrt(dot(N[i], N[i])); - if (len != 0.0) { - for (j = 0; j < 3; j++) N[i][j] /= len; + if (s1 != 0.0 || s2 != 0.0) { + // Either p or q is coplanar with abc. ONLY one of them is possible. + if (s1 == 0.0) { + // p is coplanar with abc, q can be used as reference point. +#ifdef SELF_CHECK + assert(s2 != 0.0); +#endif + return tri_vert_cop_inter(A, B, C, P, Q); + } else { + // q is coplanar with abc, p can be used as reference point. +#ifdef SELF_CHECK + assert(s2 == 0.0); +#endif + return tri_vert_cop_inter(A, B, C, Q, P); } } - for (i = 0; i < 6; i++) { - switch (i) { - case 0: f1 = 2; f2 = 3; break; // edge ab. - case 1: f1 = 0; f2 = 3; break; // edge bc. - case 2: f1 = 1; f2 = 3; break; // edge ca. - case 3: f1 = 1; f2 = 2; break; // edge ad. - case 4: f1 = 2; f2 = 0; break; // edge bd. - case 5: f1 = 0; f2 = 1; break; // edge cd. - } - cosd = -dot(N[f1], N[f2]); - if (cosdd) cosdd[i] = cosd; - if (i == 0) { - if (cosmaxd) *cosmaxd = cosd; - if (cosmind) *cosmind = cosd; - } else { - if (cosmaxd) *cosmaxd = cosd < *cosmaxd ? cosd : *cosmaxd; - if (cosmind) *cosmind = cosd > *cosmind ? cosd : *cosmind; + // pq is coplanar with abc. Calculate a point which is exactly not + // coplanar with a, b, and c. + REAL R[3], N[3]; + REAL ax, ay, az, bx, by, bz; + + ax = A[0] - B[0]; + ay = A[1] - B[1]; + az = A[2] - B[2]; + bx = A[0] - C[0]; + by = A[1] - C[1]; + bz = A[2] - C[2]; + N[0] = ay * bz - by * az; + N[1] = az * bx - bz * ax; + N[2] = ax * by - bx * ay; + // The normal should not be a zero vector (otherwise, abc are collinear). +#ifdef SELF_CHECK + assert((fabs(N[0]) + fabs(N[1]) + fabs(N[2])) > 0.0); +#endif + // The reference point R is lifted from A to the normal direction with + // a distance d = average edge length of the triangle abc. + R[0] = N[0] + A[0]; + R[1] = N[1] + A[1]; + R[2] = N[2] + A[2]; + // Becareful the case: if the non-zero component(s) in N is smaller than + // the machine epsilon (i.e., 2^(-16) for double), R will exactly equal + // to A due to the round-off error. Do check if it is. + if (R[0] == A[0] && R[1] == A[1] && R[2] == A[2]) { + int i, j; + for (i = 0; i < 3; i++) { +#ifdef SELF_CHECK + assert (R[i] == A[i]); +#endif + j = 2; + do { + if (N[i] > 0.0) { + N[i] += (j * macheps); + } else { + N[i] -= (j * macheps); + } + R[i] = N[i] + A[i]; + j *= 2; + } while (R[i] == A[i]); } } + + return tri_edge_cop_inter(A, B, C, P, Q, R); } /////////////////////////////////////////////////////////////////////////////// // // -// tetallnormal() Get the in-noramls of the four faces of a given tet. // +// tri_edge_inter() Test whether a triangle (abc) and an edge (pq) are // +// intersecting or not. // // // -// Let tet be abcd. N[4][3] returns the four normals, which are: N[0] cbd, // -// N[1] acd, N[2] bad, N[3] abc. These normals are unnormalized. // +// The return value indicates one of the four cases: DISJOINT, SHAREVERTEX, // +// SHAREEDGE, and INTERSECT. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::tetallnormal(point pa, point pb, point pc, point pd, - REAL N[4][3], REAL* volume) +enum tetgenmesh::interresult tetgenmesh::tri_edge_inter(REAL* A, REAL* B, + REAL* C, REAL* P, REAL* Q) { - REAL A[4][4], rhs[4], D; - int indx[4]; - int i, j; + REAL s1, s2; - // get the entries of A[3][3]. - for (i = 0; i < 3; i++) A[0][i] = pa[i] - pd[i]; // d->a vec - for (i = 0; i < 3; i++) A[1][i] = pb[i] - pd[i]; // d->b vec - for (i = 0; i < 3; i++) A[2][i] = pc[i] - pd[i]; // d->c vec - // Compute the inverse of matrix A, to get 3 normals of the 4 faces. - lu_decmp(A, 3, indx, &D, 0); // Decompose the matrix just once. - if (volume != NULL) { - // Get the volume of the tet. - *volume = fabs((A[indx[0]][0] * A[indx[1]][1] * A[indx[2]][2])) / 6.0; - } - for (j = 0; j < 3; j++) { - for (i = 0; i < 3; i++) rhs[i] = 0.0; - rhs[j] = 1.0; // Positive means the inside direction - lu_solve(A, 3, indx, rhs, 0); - for (i = 0; i < 3; i++) N[j][i] = rhs[i]; - } - // Get the fourth normal by summing up the first three. - for (i = 0; i < 3; i++) N[3][i] = - N[0][i] - N[1][i] - N[2][i]; + // Test the locations of p and q with respect to ABC. + s1 = orient3d(A, B, C, P); + s2 = orient3d(A, B, C, Q); + + return tri_edge_inter_tail(A, B, C, P, Q, s1, s2); } /////////////////////////////////////////////////////////////////////////////// // // -// tetaspectratio() Calculate the aspect ratio of the tetrahedron. // +// tri_tri_inter() Test whether two triangle (abc) and (opq) are // +// intersecting or not. // // // -// The aspect ratio of a tet is R/h, where R is the circumradius and h is // -// the shortest height of the tet. // +// The return value indicates one of the five cases: DISJOINT, SHAREVERTEX, // +// SHAREEDGE, SHAREFACE, and INTERSECT. // // // /////////////////////////////////////////////////////////////////////////////// -REAL tetgenmesh::tetaspectratio(point pa, point pb, point pc, point pd) +enum tetgenmesh::interresult tetgenmesh::tri_tri_inter(REAL* A, REAL* B, + REAL* C, REAL* O, REAL* P, REAL* Q) { - REAL vda[3], vdb[3], vdc[3]; - REAL N[4][3], A[4][4], rhs[4], D; - REAL H[4], volume, radius2, minheightinv; - int indx[4]; - int i, j; - - // Set the matrix A = [vda, vdb, vdc]^T. - for (i = 0; i < 3; i++) A[0][i] = vda[i] = pa[i] - pd[i]; - for (i = 0; i < 3; i++) A[1][i] = vdb[i] = pb[i] - pd[i]; - for (i = 0; i < 3; i++) A[2][i] = vdc[i] = pc[i] - pd[i]; - // Lu-decompose the matrix A. - lu_decmp(A, 3, indx, &D, 0); - // Get the volume of abcd. - volume = (A[indx[0]][0] * A[indx[1]][1] * A[indx[2]][2]) / 6.0; - // Check if it is zero. - if (volume == 0.0) return 1.0e+200; // A degenerate tet. - // if (volume < 0.0) volume = -volume; - // Check the radiu-edge ratio of the tet. - rhs[0] = 0.5 * dot(vda, vda); - rhs[1] = 0.5 * dot(vdb, vdb); - rhs[2] = 0.5 * dot(vdc, vdc); - lu_solve(A, 3, indx, rhs, 0); - // Get the circumcenter. - // for (i = 0; i < 3; i++) circumcent[i] = pd[i] + rhs[i]; - // Get the square of the circumradius. - radius2 = dot(rhs, rhs); + REAL s_o, s_p, s_q; + REAL s_a, s_b, s_c; - // Compute the 4 face normals (N[0], ..., N[3]). - for (j = 0; j < 3; j++) { - for (i = 0; i < 3; i++) rhs[i] = 0.0; - rhs[j] = 1.0; // Positive means the inside direction - lu_solve(A, 3, indx, rhs, 0); - for (i = 0; i < 3; i++) N[j][i] = rhs[i]; - } - // Get the fourth normal by summing up the first three. - for (i = 0; i < 3; i++) N[3][i] = - N[0][i] - N[1][i] - N[2][i]; - // Normalized the normals. - for (i = 0; i < 4; i++) { - // H[i] is the inverse of the height of its corresponding face. - H[i] = sqrt(dot(N[i], N[i])); - // if (H[i] > 0.0) { - // for (j = 0; j < 3; j++) N[i][j] /= H[i]; - // } + s_o = orient3d(A, B, C, O); + s_p = orient3d(A, B, C, P); + s_q = orient3d(A, B, C, Q); + if ((s_o * s_p > 0.0) && (s_o * s_q > 0.0)) { + // o, p, q are all in the same halfspace of ABC. + return DISJOINT; } - // Get the radius of the inscribed sphere. - // insradius = 1.0 / (H[0] + H[1] + H[2] + H[3]); - // Get the biggest H[i] (corresponding to the smallest height). - minheightinv = H[0]; - for (i = 1; i < 3; i++) { - if (H[i] > minheightinv) minheightinv = H[i]; + + s_a = orient3d(O, P, Q, A); + s_b = orient3d(O, P, Q, B); + s_c = orient3d(O, P, Q, C); + if ((s_a * s_b > 0.0) && (s_a * s_c > 0.0)) { + // a, b, c are all in the same halfspace of OPQ. + return DISJOINT; } - return sqrt(radius2) * minheightinv; -} + enum interresult abcop, abcpq, abcqo; + int shareedge = 0; -/////////////////////////////////////////////////////////////////////////////// -// // -// circumsphere() Calculate the smallest circumsphere (center and radius) // -// of the given three or four points. // -// // -// The circumsphere of four points (a tetrahedron) is unique if they are not // -// degenerate. If 'pd = NULL', the smallest circumsphere of three points is // -// the diametral sphere of the triangle if they are not degenerate. // -// // -// Return TRUE if the input points are not degenerate and the circumcenter // -// and circumradius are returned in 'cent' and 'radius' respectively if they // -// are not NULLs. Otherwise, return FALSE indicated the points are degenrate.// -// // -/////////////////////////////////////////////////////////////////////////////// + abcop = tri_edge_inter_tail(A, B, C, O, P, s_o, s_p); + if (abcop == INTERSECT) { + return INTERSECT; + } else if (abcop == SHAREEDGE) { + shareedge++; + } + abcpq = tri_edge_inter_tail(A, B, C, P, Q, s_p, s_q); + if (abcpq == INTERSECT) { + return INTERSECT; + } else if (abcpq == SHAREEDGE) { + shareedge++; + } + abcqo = tri_edge_inter_tail(A, B, C, Q, O, s_q, s_o); + if (abcqo == INTERSECT) { + return INTERSECT; + } else if (abcqo == SHAREEDGE) { + shareedge++; + } + if (shareedge == 3) { + // opq are coincident with abc. + return SHAREFACE; + } +#ifdef SELF_CHECK + // It is only possible either no share edge or one. + assert(shareedge == 0 || shareedge == 1); +#endif -bool tetgenmesh:: -circumsphere(REAL* pa, REAL* pb, REAL* pc, REAL* pd, REAL* cent, REAL* radius) -{ - REAL A[4][4], rhs[4], D; - int indx[4]; + // Continue to detect whether opq and abc are intersecting or not. + enum interresult opqab, opqbc, opqca; - // Compute the coefficient matrix A (3x3). - A[0][0] = pb[0] - pa[0]; - A[0][1] = pb[1] - pa[1]; - A[0][2] = pb[2] - pa[2]; - A[1][0] = pc[0] - pa[0]; - A[1][1] = pc[1] - pa[1]; - A[1][2] = pc[2] - pa[2]; - if (pd != NULL) { - A[2][0] = pd[0] - pa[0]; - A[2][1] = pd[1] - pa[1]; - A[2][2] = pd[2] - pa[2]; - } else { - cross(A[0], A[1], A[2]); + opqab = tri_edge_inter_tail(O, P, Q, A, B, s_a, s_b); + if (opqab == INTERSECT) { + return INTERSECT; + } + opqbc = tri_edge_inter_tail(O, P, Q, B, C, s_b, s_c); + if (opqbc == INTERSECT) { + return INTERSECT; + } + opqca = tri_edge_inter_tail(O, P, Q, C, A, s_c, s_a); + if (opqca == INTERSECT) { + return INTERSECT; } - // Compute the right hand side vector b (3x1). - rhs[0] = 0.5 * dot(A[0], A[0]); - rhs[1] = 0.5 * dot(A[1], A[1]); - if (pd != NULL) { - rhs[2] = 0.5 * dot(A[2], A[2]); - } else { - rhs[2] = 0.0; + // At this point, two triangles are not intersecting and not coincident. + // They may be share an edge, or share a vertex, or disjoint. + if (abcop == SHAREEDGE) { +#ifdef SELF_CHECK + assert(abcpq == SHAREVERTEX && abcqo == SHAREVERTEX); +#endif + // op is coincident with an edge of abc. + return SHAREEDGE; + } + if (abcpq == SHAREEDGE) { +#ifdef SELF_CHECK + assert(abcop == SHAREVERTEX && abcqo == SHAREVERTEX); +#endif + // pq is coincident with an edge of abc. + return SHAREEDGE; + } + if (abcqo == SHAREEDGE) { +#ifdef SELF_CHECK + assert(abcop == SHAREVERTEX && abcpq == SHAREVERTEX); +#endif + // qo is coincident with an edge of abc. + return SHAREEDGE; } - // 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)) { - if (radius != (REAL *) NULL) *radius = 0.0; - return false; - } - lu_solve(A, 3, indx, rhs, 0); - if (cent != (REAL *) NULL) { - cent[0] = pa[0] + rhs[0]; - cent[1] = pa[1] + rhs[1]; - cent[2] = pa[2] + rhs[2]; + // They may share a vertex or disjoint. + if (abcop == SHAREVERTEX) { + // o or p is coincident with a vertex of abc. + if (abcpq == SHAREVERTEX) { + // p is the coincident vertex. +#ifdef SELF_CHECK + assert(abcqo != SHAREVERTEX); +#endif + } else { + // o is the coincident vertex. +#ifdef SELF_CHECK + assert(abcqo == SHAREVERTEX); +#endif + } + return SHAREVERTEX; } - if (radius != (REAL *) NULL) { - *radius = sqrt(rhs[0] * rhs[0] + rhs[1] * rhs[1] + rhs[2] * rhs[2]); + if (abcpq == SHAREVERTEX) { + // q is the coincident vertex. +#ifdef SELF_CHECK + assert(abcqo == SHAREVERTEX); +#endif + return SHAREVERTEX; } - return true; + + // They are disjoint. + return DISJOINT; } /////////////////////////////////////////////////////////////////////////////// // // -// inscribedsphere() Compute the radius and center of the biggest // -// inscribed sphere of a given tetrahedron. // +// insphere_sos() Insphere test with symbolic perturbation. // // // -// The tetrahedron is given by its four points, it must not be degenerate. // -// The center and radius are returned in 'cent' and 'radius' respectively if // -// they are not NULLs. // +// The input points a, b, c, and d should be non-coplanar. They must be ord- // +// ered so that they have a positive orientation (as defined by orient3d()), // +// or the sign of the result will be reversed. // // // -// Geometrical fact. For any simplex in d dimension, // -// r/h1 + r/h2 + ... r/hn = 1 (n <= d + 1); // -// where r is the radius of inscribed ball, and h is the height of each side // -// of the simplex. The value of 'r/h' is just the barycenter coordinates of // -// each vertex of the simplex. Therefore, we can compute the radius and // -// center of the smallest inscribed ball as following equations: // -// r = 1.0 / (1/h1 + 1/h2 + ... + 1/hn); (1) // -// C = r/h1 * P1 + r/h2 * P2 + ... + r/hn * Pn; (2) // -// where C is the vector of center, P1, P2, .. Pn are vectors of vertices. // -// Here (2) contains n linear equations with n variables. (h, P) must be a // -// pair, h is the height from P to its opposite face. // +// Return a positive value if the point e lies inside the circumsphere of a, // +// b, c, and d; a negative value if it lies outside. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::inscribedsphere(REAL* pa, REAL* pb, REAL* pc, REAL* pd, - REAL* cent, REAL* radius) +REAL tetgenmesh::insphere_sos(REAL* pa, REAL* pb, REAL* pc, REAL* pd, REAL* pe, + int ia, int ib, int ic, int id, int ie) { - REAL N[4][3], H[4]; // Normals (colume vectors) and heights of each face. - REAL rd; - int i; + REAL det; - // Get the all normals of the tet. - tetallnormal(pa, pb, pc, pd, N, NULL); - for (i = 0; i < 4; i++) { - // H[i] is the inverse of height of its corresponding face. - H[i] = sqrt(dot(N[i], N[i])); + det = insphere(pa, pb, pc, pd, pe); + if (det != 0.0) { + return det; } - // Compute the radius use eq. (1). - rd = 1.0 / (H[0] + H[1] + H[2] + H[3]); - if (radius != (REAL*) NULL) *radius = rd; - if (cent != (REAL*) NULL) { - // Compute the center use eq. (2). - cent[0] = rd * (H[0] * pa[0] + H[1] * pb[0] + H[2] * pc[0] + H[3] * pd[0]); - cent[1] = rd * (H[0] * pa[1] + H[1] * pb[1] + H[2] * pc[1] + H[3] * pd[1]); - cent[2] = rd * (H[0] * pa[2] + H[1] * pb[2] + H[2] * pc[2] + H[3] * pd[2]); + + // det = 0.0, use symbolic perturbation. + REAL *p[5], *tmpp; + REAL sign, det_c, det_d; + int idx[5], perm, tmp; + int n, i, j; + + p[0] = pa; idx[0] = ia; + p[1] = pb; idx[1] = ib; + p[2] = pc; idx[2] = ic; + p[3] = pd; idx[3] = id; + p[4] = pe; idx[4] = ie; + + // Bubble sort the points by the increasing order of the indices. + n = 5; + perm = 0; // The number of total swaps. + for (i = 0; i < n - 1; i++) { + for (j = 0; j < n - 1 - i; j++) { + if (idx[j + 1] < idx[j]) { // compare the two neighbors. + tmp = idx[j]; // swap idx[j] and idx[j + 1] + idx[j] = idx[j + 1]; + idx[j + 1] = tmp; + tmpp = p[j]; // swap p[j] and p[j + 1] + p[j] = p[j + 1]; + p[j + 1] = tmpp; + perm++; + } + } + } + + sign = (perm % 2 == 0) ? 1.0 : -1.0; + det_c = orient3d(p[1], p[2], p[3], p[4]); // orient3d(b, c, d, e) + if (det_c != 0.0) { + return sign * det_c; } + det_d = orient3d(p[0], p[2], p[3], p[4]); // orient3d(a, c, d, e) + return -sign * det_d; } /////////////////////////////////////////////////////////////////////////////// // // -// rotatepoint() Create a point by rotating an existing point. // -// // -// Create a 3D point by rotating point 'p' with an angle 'rotangle' (in arc // -// degree) around a rotating axis given by a vector from point 'p1' to 'p2'. // -// The rotation is according with right-hand rule, i.e., use your right-hand // -// to grab the axis with your thumber pointing to its positive direction, // -// your fingers indicate the rotating direction. // +// iscollinear() Check if three points are approximately collinear. // // // -// The rotating steps are the following: // -// 1. Translate vector 'p1->p2' to origin, M1; // -// 2. Rotate vector around the Y-axis until it lies in the YZ plane, M2; // -// 3. Rotate vector around the X-axis until it lies on the Z axis, M3; // -// 4. Perform the rotation of 'p' around the z-axis, M4; // -// 5. Undo Step 3, M5; // -// 6. Undo Step 2, M6; // -// 7. Undo Step 1, M7; // -// Use matrix multiplication to combine the above sequences, we get: // -// p0' = T * p0, where T = M7 * M6 * M5 * M4 * M3 * M2 * M1 // +// 'eps' is a relative error tolerance. The collinearity is determined by // +// the value q = cos(theta), where theta is the angle between two vectors // +// A->B and A->C. They're collinear if 1.0 - q <= epspp. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::rotatepoint(REAL* p, REAL rotangle, REAL* p1, REAL* p2) +bool tetgenmesh::iscollinear(REAL* A, REAL* B, REAL* C, REAL eps) { - REAL T[4][4], pp0[4], p0t[4], p2t[4]; - REAL roty, rotx, alphaR, projlen; - REAL dx, dy, dz; + REAL abx, aby, abz; + REAL acx, acy, acz; + REAL Lv, Lw, dd; + REAL d, q; - initm44(1, 0, 0, -p1[0], - 0, 1, 0, -p1[1], - 0, 0, 1, -p1[2], - 0, 0, 0, 1, T); - pp0[0] = p[0]; pp0[1] = p[1]; pp0[2] = p[2]; pp0[3] = 1.0; - m4xv4(p0t, T, pp0); // Step 1 - pp0[0] = p2[0]; pp0[1] = p2[1]; pp0[2] = p2[2]; pp0[3] = 1.0; - m4xv4(p2t, T, pp0); // Step 1 - - // Get the rotation angle around y-axis; - dx = p2t[0]; - dz = p2t[2]; - projlen = sqrt(dx * dx + dz * dz); - if (projlen <= (b->epsilon * 1e-2) * longest) { - roty = 0; - } else { - roty = acos(dz / projlen); - if (dx < 0) { - roty = -roty; - } - } - - initm44(cos(-roty), 0, sin(-roty), 0, - 0, 1, 0, 0, - -sin(-roty), 0, cos(-roty), 0, - 0, 0, 0, 1, T); - pp0[0] = p0t[0]; pp0[1] = p0t[1]; pp0[2] = p0t[2]; pp0[3] = 1.0; - m4xv4(p0t, T, pp0); // Step 2 - pp0[0] = p2t[0]; pp0[1] = p2t[1]; pp0[2] = p2t[2]; pp0[3] = 1.0; - m4xv4(p2t, T, pp0); // Step 2 - - // Get the rotation angle around x-axis - dy = p2t[1]; - dz = p2t[2]; - projlen = sqrt(dy * dy + dz * dz); - if (projlen <= (b->epsilon * 1e-2) * longest) { - rotx = 0; - } else { - rotx = acos(dz / projlen); - if (dy < 0) { - rotx = -rotx; - } - } - - initm44(1, 0, 0, 0, - 0, cos(rotx), -sin(rotx), 0, - 0, sin(rotx), cos(rotx), 0, - 0, 0, 0, 1, T); - pp0[0] = p0t[0]; pp0[1] = p0t[1]; pp0[2] = p0t[2]; pp0[3] = 1.0; - m4xv4(p0t, T, pp0); // Step 3 - // pp0[0] = p2t[0]; pp0[1] = p2t[1]; pp0[2] = p2t[2]; pp0[3] = 1.0; - // m4xv4(p2t, T, pp0); // Step 3 - - alphaR = rotangle; - initm44(cos(alphaR), -sin(alphaR), 0, 0, - sin(alphaR), cos(alphaR), 0, 0, - 0, 0, 1, 0, - 0, 0, 0, 1, T); - pp0[0] = p0t[0]; pp0[1] = p0t[1]; pp0[2] = p0t[2]; pp0[3] = 1.0; - m4xv4(p0t, T, pp0); // Step 4 - - initm44(1, 0, 0, 0, - 0, cos(-rotx), -sin(-rotx), 0, - 0, sin(-rotx), cos(-rotx), 0, - 0, 0, 0, 1, T); - pp0[0] = p0t[0]; pp0[1] = p0t[1]; pp0[2] = p0t[2]; pp0[3] = 1.0; - m4xv4(p0t, T, pp0); // Step 5 - - initm44(cos(roty), 0, sin(roty), 0, - 0, 1, 0, 0, - -sin(roty), 0, cos(roty), 0, - 0, 0, 0, 1, T); - pp0[0] = p0t[0]; pp0[1] = p0t[1]; pp0[2] = p0t[2]; pp0[3] = 1.0; - m4xv4(p0t, T, pp0); // Step 6 - - initm44(1, 0, 0, p1[0], - 0, 1, 0, p1[1], - 0, 0, 1, p1[2], - 0, 0, 0, 1, T); - pp0[0] = p0t[0]; pp0[1] = p0t[1]; pp0[2] = p0t[2]; pp0[3] = 1.0; - m4xv4(p0t, T, pp0); // Step 7 + // Limit of two closed points. + q = longest * eps; + q *= q; - p[0] = p0t[0]; - p[1] = p0t[1]; - p[2] = p0t[2]; + abx = A[0] - B[0]; + aby = A[1] - B[1]; + abz = A[2] - B[2]; + acx = A[0] - C[0]; + acy = A[1] - C[1]; + acz = A[2] - C[2]; + Lv = abx * abx + aby * aby + abz * abz; + // Is AB (nearly) indentical? + if (Lv < q) return true; + Lw = acx * acx + acy * acy + acz * acz; + // Is AC (nearly) indentical? + if (Lw < q) return true; + dd = abx * acx + aby * acy + abz * acz; + + d = (dd * dd) / (Lv * Lw); + if (d > 1.0) d = 1.0; // Rounding. + q = 1.0 - sqrt(d); // Notice 0 < q < 1.0. + + return q <= eps; } /////////////////////////////////////////////////////////////////////////////// // // -// planelineint() Calculate the intersection of a line and a plane. // -// // -// The equation of a plane (points P are on the plane with normal N and P3 // -// on the plane) can be written as: N dot (P - P3) = 0. The equation of the // -// line (points P on the line passing through P1 and P2) can be written as: // -// P = P1 + u (P2 - P1). The intersection of these two occurs when: // -// N dot (P1 + u (P2 - P1)) = N dot P3. // -// Solving for u gives: // -// N dot (P3 - P1) // -// u = ------------------. // -// N dot (P2 - P1) // -// If the denominator is 0 then N (the normal to the plane) is perpendicular // -// to the line. Thus the line is either parallel to the plane and there are // -// no solutions or the line is on the plane in which case there are an infi- // -// nite number of solutions. // +// iscoplanar() Check if four points are approximately coplanar. // // // -// The plane is given by three points pa, pb, and pc, e1 and e2 defines the // -// line. If u is non-zero, The intersection point (if exists) returns in ip. // +// 'vol6' is six times of the signed volume of the tetrahedron formed by the // +// four points. 'eps' is the relative error tolerance. The coplanarity is // +// determined by the value: q = fabs(vol6) / L^3, where L is the average // +// edge length of the tet. They're coplanar if q <= eps. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::planelineint(REAL* pa, REAL* pb, REAL* pc, REAL* e1, REAL* e2, - REAL* ip, REAL* u) +bool tetgenmesh:: +iscoplanar(REAL* k, REAL* l, REAL* m, REAL* n, REAL vol6, REAL eps) { - REAL n[3], det, det1; + REAL L, q; + REAL x, y, z; - // Calculate N. - facenormal2(pa, pb, pc, n, 1); - // Calculate N dot (e2 - e1). - det = n[0] * (e2[0] - e1[0]) + n[1] * (e2[1] - e1[1]) - + n[2] * (e2[2] - e1[2]); - if (det != 0.0) { - // Calculate N dot (pa - e1) - det1 = n[0] * (pa[0] - e1[0]) + n[1] * (pa[1] - e1[1]) - + n[2] * (pa[2] - e1[2]); - *u = det1 / det; - ip[0] = e1[0] + *u * (e2[0] - e1[0]); - ip[1] = e1[1] + *u * (e2[1] - e1[1]); - ip[2] = e1[2] + *u * (e2[2] - e1[2]); - } else { - *u = 0.0; - } + if (vol6 == 0.0) return true; + + x = k[0] - l[0]; + y = k[1] - l[1]; + z = k[2] - l[2]; + L = sqrt(x * x + y * y + z * z); + x = l[0] - m[0]; + y = l[1] - m[1]; + z = l[2] - m[2]; + L += sqrt(x * x + y * y + z * z); + x = m[0] - k[0]; + y = m[1] - k[1]; + z = m[2] - k[2]; + L += sqrt(x * x + y * y + z * z); + x = k[0] - n[0]; + y = k[1] - n[1]; + z = k[2] - n[2]; + L += sqrt(x * x + y * y + z * z); + x = l[0] - n[0]; + y = l[1] - n[1]; + z = l[2] - n[2]; + L += sqrt(x * x + y * y + z * z); + x = m[0] - n[0]; + y = m[1] - n[1]; + z = m[2] - n[2]; + L += sqrt(x * x + y * y + z * z); +#ifdef SELF_CHECK + assert(L > 0.0); +#endif + L /= 6.0; + q = fabs(vol6) / (L * L * L); + + return q <= eps; } /////////////////////////////////////////////////////////////////////////////// // // -// randomnation() Generate a random number between 0 and 'choices' - 1. // +// iscospheric() Check if five points are approximately coplanar. // +// // +// 'vol24' is the 24 times of the signed volume of the 4-dimensional simplex // +// formed by the five points. 'eps' is the relative tolerance. The cosphere // +// case is determined by the value: q = fabs(vol24) / L^4, where L is the // +// average edge length of the simplex. They're cosphere if q <= eps. // // // /////////////////////////////////////////////////////////////////////////////// -unsigned long tetgenmesh::randomnation(unsigned int choices) +bool tetgenmesh:: +iscospheric(REAL* k, REAL* l, REAL* m, REAL* n, REAL* o, REAL vol24, REAL eps) { - unsigned long newrandom; - - if (choices >= 714025l) { - newrandom = (randomseed * 1366l + 150889l) % 714025l; - randomseed = (newrandom * 1366l + 150889l) % 714025l; - newrandom = newrandom * (choices / 714025l) + randomseed; - if (newrandom >= choices) { - return newrandom - choices; - } else { - return newrandom; - } - } else { - randomseed = (randomseed * 1366l + 150889l) % 714025l; - return randomseed % choices; - } -} + REAL L, q; -/////////////////////////////////////////////////////////////////////////////// -// // -// distance2() Returns the square "distance" of a tetrahedron to point p. // -// // -/////////////////////////////////////////////////////////////////////////////// + // A 4D simplex has 10 edges. + L = distance(k, l); + L += distance(l, m); + L += distance(m, k); + L += distance(k, n); + L += distance(l, n); + L += distance(m, n); + L += distance(k, o); + L += distance(l, o); + L += distance(m, o); + L += distance(n, o); +#ifdef SELF_CHECK + assert(L > 0.0); +#endif + L /= 10.0; + q = fabs(vol24) / (L * L * L * L); -REAL tetgenmesh::distance2(tetrahedron* tetptr, point p) -{ - point p1, p2, p3, p4; - REAL dx, dy, dz; + return q < eps; +} - p1 = (point) tetptr[4]; - p2 = (point) tetptr[5]; - p3 = (point) tetptr[6]; - p4 = (point) tetptr[7]; +// +// End of geometric tests +// - dx = p[0] - 0.25 * (p1[0] + p2[0] + p3[0] + p4[0]); - dy = p[1] - 0.25 * (p1[1] + p2[1] + p3[1] + p4[1]); - dz = p[2] - 0.25 * (p1[2] + p2[2] + p3[2] + p4[2]); +// +// Begin of Geometric quantities calculators +// - return dx * dx + dy * dy + dz * dz; +// 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]) + + (p2[1] - p1[1]) * (p2[1] - p1[1]) + + (p2[2] - p1[2]) * (p2[2] - p1[2])); } /////////////////////////////////////////////////////////////////////////////// // // -// preciselocate() Find a simplex containing a given point. // +// shortdistance() Returns the shortest distance from point p to a line // +// defined by two points e1 and e2. // // // -// 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. // +// 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. // // // -// On completion, 'searchtet' is a tetrahedron that contains 'searchpt'. The // -// returned value indicates one of the following cases: // -// - ONVERTEX, the search point lies on the origin of 'searchtet'. // -// - ONEDGE, the search point lies on an edge of '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. // -// // -// WARNING: This routine is designed for convex triangulations, and will not // -// generally work after the holes and concavities have been carved. // -// // -// If 'maxtetnumber' > 0, stop the searching process if the number of passed // -// tets is larger than it and return OUTSIDE. // +// 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. // // // /////////////////////////////////////////////////////////////////////////////// -enum tetgenmesh::locateresult tetgenmesh::preciselocate(point searchpt, - triface* searchtet, long maxtetnumber) +REAL tetgenmesh::shortdistance(REAL* p, REAL* e1, REAL* e2) { - triface backtracetet; - triface walkthroface; - point forg, fdest, fapex, toppo; - REAL ori1, ori2, ori3, ori4; - long tetnumber; - int side; + REAL v1[3], v2[3]; + REAL len, l_p; - if (isdead(searchtet)) searchtet->tet = dummytet; - if (searchtet->tet == dummytet) { - searchtet->loc = 0; - symself(*searchtet); - } - // 'searchtet' should be a valid tetrahedron now. -#ifdef SELF_CHECK - assert(searchtet->tet != dummytet); -#endif + v1[0] = e2[0] - e1[0]; + v1[1] = e2[1] - e1[1]; + v1[2] = e2[2] - e1[2]; + v2[0] = p[0] - e1[0]; + v2[1] = p[1] - e1[1]; + v2[2] = p[2] - e1[2]; - searchtet->ver = 0; // Keep in CCW edge ring. - // Find a face of 'searchtet' such that the 'searchpt' lies strictly - // above it. Such face should always exist. - for (searchtet->loc = 0; searchtet->loc < 4; searchtet->loc++) { - forg = org(*searchtet); - fdest = dest(*searchtet); - fapex = apex(*searchtet); - ori1 = orient3d(forg, fdest, fapex, searchpt); - if (ori1 < 0.0) break; - } + len = sqrt(dot(v1, v1)); #ifdef SELF_CHECK - assert(searchtet->loc < 4); + assert(len != 0.0); #endif + v1[0] /= len; + v1[1] /= len; + v1[2] /= len; + l_p = dot(v1, v2); - backtracetet = *searchtet; // Initialize backtracetet. + return sqrt(dot(v2, v2) - l_p * l_p); +} - // Define 'tetnumber' for exit the loop when it's running endless. - tetnumber = 0l; - while ((maxtetnumber > 0l) && (tetnumber <= maxtetnumber)) { - ptloc_count++; // Algorithimic count. - // Check if we are reaching the boundary of the triangulation. - if (searchtet->tet == dummytet) { - *searchtet = backtracetet; - return OUTSIDE; - } - // Initialize the face for returning the walk-through face. - walkthroface.tet = (tetrahedron *) NULL; - // Adjust the edge ring, so that 'ori1 < 0.0' holds. - searchtet->ver = 0; - // 'toppo' remains unchange for the following orientation tests. - toppo = oppo(*searchtet); - // Check the three sides of 'searchtet' to find the face through which - // we can walk next. - for (side = 0; side < 3; side++) { - forg = org(*searchtet); - fdest = dest(*searchtet); - ori2 = orient3d(forg, fdest, toppo, searchpt); - if (ori2 == 0.0) { - // They are coplanar, check if 'searchpt' lies inside, or on an edge, - // or coindice with a vertex of face (forg, fdest, toppo). - fapex = apex(*searchtet); - ori3 = orient3d(fdest, fapex, toppo, searchpt); - if (ori3 < 0.0) { - // Outside the face (fdest, fapex, toppo), walk through it. - enextself(*searchtet); - fnext(*searchtet, walkthroface); - break; - } - ori4 = orient3d(fapex, forg, toppo, searchpt); - if (ori4 < 0.0) { - // Outside the face (fapex, forg, toppo), walk through it. - enext2self(*searchtet); - fnext(*searchtet, walkthroface); - break; - } - // Remember, ori1 < 0.0, which means that 'searchpt' will not on edge - // (forg, fdest) or on vertex forg or fdest. - // The rest possible cases are: - // (1) 'searchpt' lies on edge (fdest, toppo); - // (2) 'searchpt' lies on edge (toppo, forg); - // (3) 'searchpt' coincident with toppo; - // (4) 'searchpt' lies inside face (forg, fdest, toppo). - fnextself(*searchtet); - if (ori3 == 0.0) { - if (ori4 == 0.0) { - // Case (4). - enext2self(*searchtet); - return ONVERTEX; - } else { - // Case (1). - enextself(*searchtet); - return ONEDGE; - } - } - if (ori4 == 0.0) { - // Case (2). - enext2self(*searchtet); - return ONEDGE; - } - // Case (4). - return ONFACE; - } else if (ori2 < 0.0) { - // Outside the face (forg, fdest, toppo), walk through it. - fnext(*searchtet, walkthroface); - break; - } - // Go to check next side. - enextself(*searchtet); - } - if (side == 3) { - // Found! Inside tetrahedron. - return INTETRAHEDRON; - } - // We walk through the face 'walkthroface' and continue the searching. - // Store the face handle in 'backtracetet' before we take the real walk. - // So we are able to restore the handle to 'searchtet' if we are - // reaching the outer boundary. - backtracetet = walkthroface; - sym(walkthroface, *searchtet); - tetnumber++; - } +/////////////////////////////////////////////////////////////////////////////// +// // +// shortdistance() Returns the shortest distance from point p to a face. // +// // +/////////////////////////////////////////////////////////////////////////////// - if (!b->quiet && b->verbose) { - printf("Warning: Point location stopped after searching %ld tets.\n", - maxtetnumber); - } - // terminatetetgen(2); - return OUTSIDE; +REAL tetgenmesh::shortdistance(REAL* p, REAL* e1, REAL* e2, REAL* e3) +{ + REAL prj[3]; + + projpt2face(p, e1, e2, e3, prj); + return distance(p, prj); } /////////////////////////////////////////////////////////////////////////////// // // -// randomsample() Randomly sample the tetrahedra for point loation. // +// interiorangle() Return the interior angle (0 - 2 * PI) between vectors // +// o->p1 and o->p2. // // // -// 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. // +// 'n' is the normal of the plane containing face (o, p1, p2). The interior // +// angle is the total angle rotating from o->p1 around n to o->p2. Exchange // +// the position of p1 and p2 will get the complement angle of the other one. // +// i.e., interiorangle(o, p1, p2) = 2 * PI - interiorangle(o, p2, p1). Set // +// 'n' be NULL if you only want the interior angle between 0 - PI. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::randomsample(point searchpt, triface *searchtet) +REAL tetgenmesh::interiorangle(REAL* o, REAL* p1, REAL* p2, REAL* n) { - tetrahedron *firsttet, *tetptr; - void **sampleblock; - long sampleblocks, samplesperblock, samplenum; - long tetblocks, i, j; - uintptr_t alignptr; - REAL searchdist, dist; - - // 'searchtet' should be a valid tetrahedron. - if (isdead(searchtet)) { - searchtet->tet = dummytet; - } - if (searchtet->tet == dummytet) { - // This is an 'Outer Space' handle, get a hull tetrahedron. - searchtet->loc = 0; - symself(*searchtet); - } + REAL v1[3], v2[3], np[3]; + REAL theta, costheta, lenlen; + REAL ori, len1, len2; - // Note 'searchtet' may be dead (chnaged in constrainedcavity2()). - if (!isdead(searchtet)) { - // Get the distance from the suggested starting tet to the point we seek. - searchdist = distance2(searchtet->tet, searchpt); - } else { - searchdist = longest * longest; + // Get the interior angle (0 - PI) between o->p1, and o->p2. + v1[0] = p1[0] - o[0]; + v1[1] = p1[1] - o[1]; + v1[2] = p1[2] - o[2]; + v2[0] = p2[0] - o[0]; + v2[1] = p2[1] - o[1]; + v2[2] = p2[2] - o[2]; + 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. + } else if (costheta < -1.0) { + costheta = -1.0; // Roundoff. } - - // If a recently encountered tetrahedron has been recorded and has not - // been deallocated, test it as a good starting point. - if (!isdead(&recenttet) && (recenttet.tet != searchtet->tet)) { - dist = distance2(recenttet.tet, searchpt); - if (dist < searchdist) { - *searchtet = recenttet; - searchdist = dist; + theta = acos(costheta); + if (n != NULL) { + // Get a point above the face (o, p1, p2); + np[0] = o[0] + n[0]; + np[1] = o[1] + n[1]; + np[2] = o[2] + n[2]; + // Adjust theta (0 - 2 * PI). + ori = orient3d(p1, o, np, p2); + if (ori > 0.0) { + theta = 2 * PI - theta; } } - // Select "good" candidate using k random samples, taking the closest one. - // The number of random samples taken is proportional to the fourth root - // of the number of tetrahedra in the mesh. The next bit of code assumes - // that the number of tetrahedra increases monotonically. - while (SAMPLEFACTOR * samples * samples * samples * samples < - tetrahedrons->items) { - samples++; - } - // Find how much blocks in current tet pool. - tetblocks = (tetrahedrons->maxitems + ELEPERBLOCK - 1) / ELEPERBLOCK; - // Find the average samles per block. Each block at least have 1 sample. - samplesperblock = 1 + (samples / tetblocks); - sampleblocks = samples / samplesperblock; - sampleblock = tetrahedrons->firstblock; - for (i = 0; i < sampleblocks; i++) { - alignptr = (uintptr_t) (sampleblock + 1); - firsttet = (tetrahedron *) - (alignptr + (uintptr_t) tetrahedrons->alignbytes - - (alignptr % (uintptr_t) tetrahedrons->alignbytes)); - for (j = 0; j < samplesperblock; j++) { - if (i == tetblocks - 1) { - // This is the last block. - samplenum = randomnation((int) - (tetrahedrons->maxitems - (i * ELEPERBLOCK))); - } else { - samplenum = randomnation(ELEPERBLOCK); - } - tetptr = (tetrahedron *) - (firsttet + (samplenum * tetrahedrons->itemwords)); - if (tetptr[4] != (tetrahedron) NULL) { - dist = distance2(tetptr, searchpt); - if (dist < searchdist) { - searchtet->tet = tetptr; - searchdist = dist; - } - } - } - sampleblock = (void **) *sampleblock; - } + return theta; } /////////////////////////////////////////////////////////////////////////////// // // -// locate() Find a simplex containing a given point. // +// projpt2edge() Return the projection point from a point to an edge. // // // /////////////////////////////////////////////////////////////////////////////// -enum tetgenmesh::locateresult tetgenmesh::locate(point searchpt, - triface *searchtet) +void tetgenmesh::projpt2edge(REAL* p, REAL* e1, REAL* e2, REAL* prj) { - // Randomly sample for a good starting tet. - randomsample(searchpt, searchtet); - // Call simple walk-through to locate the point. - return preciselocate(searchpt, searchtet, tetrahedrons->items); -} + REAL v1[3], v2[3]; + REAL len, l_p; + + v1[0] = e2[0] - e1[0]; + v1[1] = e2[1] - e1[1]; + v1[2] = e2[2] - e1[2]; + v2[0] = p[0] - e1[0]; + v2[1] = p[1] - e1[1]; + 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; + l_p = dot(v1, v2); + + prj[0] = e1[0] + l_p * v1[0]; + prj[1] = e1[1] + l_p * v1[1]; + prj[2] = e1[2] + l_p * v1[2]; +} /////////////////////////////////////////////////////////////////////////////// // // -// locate2() Find a simplex containing a given point. // -// // -// Another implementation of the Walk-through point location algorithm. // -// See the comments of preciselocate(). // +// projpt2face() Return the projection point from a point to a face. // // // /////////////////////////////////////////////////////////////////////////////// -enum tetgenmesh::locateresult tetgenmesh::locate2(point searchpt, - triface* searchtet, arraypool *histtetarray) +void tetgenmesh::projpt2face(REAL* p, REAL* f1, REAL* f2, REAL* f3, REAL* prj) { - triface neightet, backtracetet, *parytet; - point torg, tdest, tapex, toppo, ntoppo; - enum {ORGMOVE, DESTMOVE, APEXMOVE} nextmove; - REAL ori, oriorg, oridest, oriapex; - REAL searchdist, dist; - enum locateresult loc; - int i; - - if (searchtet->tet == dummytet) { - // A hull tet. Choose the neighbor of its base face. - searchtet->loc = 0; - symself(*searchtet); - } - - // Stay in the 0th edge ring. - searchtet->ver = 0; + REAL fnormal[3], v1[3]; + REAL len, dist; - // Let searchtet be the face such that 'searchpt' lies above to it. - for (searchtet->loc = 0; searchtet->loc < 4; searchtet->loc++) { - torg = org(*searchtet); - tdest = dest(*searchtet); - tapex = apex(*searchtet); - ori = orient3d(torg, tdest, tapex, searchpt); orient3dcount++; - if (ori < 0.0) break; - } - if (!(searchtet->loc < 4)) { - // Either 'searchtet' is a very flat tet, or the 'searchpt' lies in - // infinity, or both of them. Return OUTSIDE. - return OUTSIDE; - } + // Get the unit face normal. + facenormal(f1, f2, f3, fnormal, &len); +#ifdef SELF_CHECK + assert(len > 0.0); +#endif + fnormal[0] /= len; + fnormal[1] /= len; + fnormal[2] /= len; + // Get the vector v1 = |p - f1|. + v1[0] = p[0] - f1[0]; + v1[1] = p[1] - f1[1]; + v1[2] = p[2] - f1[2]; + // Get the project distance. + dist = dot(fnormal, v1); + + // Get the project point. + prj[0] = p[0] - dist * fnormal[0]; + prj[1] = p[1] - dist * fnormal[1]; + prj[2] = p[2] - dist * fnormal[2]; +} - if (histtetarray != NULL) { - // Remember all the tets we've visited. - assert(histtetarray->objects == 0l); - infect(*searchtet); - histtetarray->newindex((void **) &parytet); - *parytet = *searchtet; - } +/////////////////////////////////////////////////////////////////////////////// +// // +// facenormal() Calculate the normal of a face given by three points. // +// // +// In general, the face normal can be calculate by the cross product of any // +// pair of the three edge vectors. However, if the three points are nearly // +// collinear, the rounding error may harm the result. To choose a good pair // +// of vectors is helpful to reduce the error. // +// // +/////////////////////////////////////////////////////////////////////////////// - loc = OUTSIDE; // Set a default return value. +void tetgenmesh::facenormal(REAL* pa, REAL* pb, REAL* pc, REAL* n, REAL* nlen) +{ + REAL v1[3], v2[3]; - // Walk through tetrahedra to locate the point. - while (true) { + v1[0] = pb[0] - pa[0]; + v1[1] = pb[1] - pa[1]; + v1[2] = pb[2] - pa[2]; + v2[0] = pc[0] - pa[0]; + v2[1] = pc[1] - pa[1]; + v2[2] = pc[2] - pa[2]; - ptloc_count++; // Algorithimic count. + cross(v1, v2, n); + if (nlen != (REAL *) NULL) { + *nlen = sqrt(dot(n, n)); + } +} - toppo = oppo(*searchtet); - - // Check if the vertex is we seek. - if (toppo == searchpt) { - // Adjust the origin of searchtet to be searchpt. - fnextself(*searchtet); - esymself(*searchtet); - enext2self(*searchtet); - loc = ONVERTEX; // return ONVERTEX; - break; - } +/////////////////////////////////////////////////////////////////////////////// +// // +// edgeorthonormal() Return the unit normal of an edge in a given plane. // +// // +// The edge is from e1 to e2, the plane is defined by given an additional // +// point op, which is non-collinear with the edge. In addition, the side of // +// the edge in which op lies defines the positive position of the normal. // +// // +// Let v1 be the unit vector from e1 to e2, v2 be the unit edge vector from // +// e1 to op, fn be the unit face normal calculated by fn = v1 x v2. Then the // +// unit edge normal of e1e2 pointing to op is n = fn x v1. Note, we should // +// not change the position of fn and v1, otherwise, we get the edge normal // +// pointing to the other side of op. // +// // +/////////////////////////////////////////////////////////////////////////////// - // We enter from serarchtet's base face. There are three other faces in - // searchtet (all connecting to toppo), which one is the exit? - oriorg = orient3d(tdest, tapex, toppo, searchpt); - oridest = orient3d(tapex, torg, toppo, searchpt); - oriapex = orient3d(torg, tdest, toppo, searchpt); - orient3dcount+=3; - - // Now decide which face to move. It is possible there are more than one - // faces are viable moves. Use the opposite points of thier neighbors - // to discriminate, i.e., we choose the face whose opposite point has - // the shortest distance to searchpt. - if (oriorg < 0) { - if (oridest < 0) { - if (oriapex < 0) { - // Any of the three faces is a viable move. - nextmove = ORGMOVE; - enextfnext(*searchtet, neightet); - symself(neightet); - if (neightet.tet != dummytet) { - ntoppo = oppo(neightet); - searchdist = NORM2(searchpt[0] - ntoppo[0], - searchpt[1] - ntoppo[1], - searchpt[2] - ntoppo[2]); - } else { - searchdist = NORM2(xmax - xmin, ymax - ymin, zmax - zmin); - } - enext2fnext(*searchtet, neightet); - symself(neightet); - if (neightet.tet != dummytet) { - ntoppo = oppo(neightet); - dist = NORM2(searchpt[0] - ntoppo[0], searchpt[1] - ntoppo[1], - searchpt[2] - ntoppo[2]); - } else { - dist = searchdist; - } - if (dist < searchdist) { - nextmove = DESTMOVE; - searchdist = dist; - } - fnext(*searchtet, neightet); - symself(neightet); - if (neightet.tet != dummytet) { - ntoppo = oppo(neightet); - dist = NORM2(searchpt[0] - ntoppo[0], searchpt[1] - ntoppo[1], - searchpt[2] - ntoppo[2]); - } else { - dist = searchdist; - } - if (dist < searchdist) { - nextmove = APEXMOVE; - searchdist = dist; - } - } else { - // Two faces, opposite to origin and destination, are viable. - nextmove = ORGMOVE; - enextfnext(*searchtet, neightet); - symself(neightet); - if (neightet.tet != dummytet) { - ntoppo = oppo(neightet); - searchdist = NORM2(searchpt[0] - ntoppo[0], - searchpt[1] - ntoppo[1], - searchpt[2] - ntoppo[2]); - } else { - searchdist = NORM2(xmax - xmin, ymax - ymin, zmax - zmin); - } - enext2fnext(*searchtet, neightet); - symself(neightet); - if (neightet.tet != dummytet) { - ntoppo = oppo(neightet); - dist = NORM2(searchpt[0] - ntoppo[0], searchpt[1] - ntoppo[1], - searchpt[2] - ntoppo[2]); - } else { - dist = searchdist; - } - if (dist < searchdist) { - nextmove = DESTMOVE; - searchdist = dist; - } - } - } else { - if (oriapex < 0) { - // Two faces, opposite to origin and apex, are viable. - nextmove = ORGMOVE; - enextfnext(*searchtet, neightet); - symself(neightet); - if (neightet.tet != dummytet) { - ntoppo = oppo(neightet); - searchdist = NORM2(searchpt[0] - ntoppo[0], - searchpt[1] - ntoppo[1], - searchpt[2] - ntoppo[2]); - } else { - searchdist = NORM2(xmax - xmin, ymax - ymin, zmax - zmin); - } - fnext(*searchtet, neightet); - symself(neightet); - if (neightet.tet != dummytet) { - ntoppo = oppo(neightet); - dist = NORM2(searchpt[0] - ntoppo[0], searchpt[1] - ntoppo[1], - searchpt[2] - ntoppo[2]); - } else { - dist = searchdist; - } - if (dist < searchdist) { - nextmove = APEXMOVE; - searchdist = dist; - } - } else { - // Only the face opposite to origin is viable. - nextmove = ORGMOVE; - } - } - } else { - if (oridest < 0) { - if (oriapex < 0) { - // Two faces, opposite to destination and apex, are viable. - nextmove = DESTMOVE; - enext2fnext(*searchtet, neightet); - symself(neightet); - if (neightet.tet != dummytet) { - ntoppo = oppo(neightet); - searchdist = NORM2(searchpt[0] - ntoppo[0], - searchpt[1] - ntoppo[1], - searchpt[2] - ntoppo[2]); - } else { - searchdist = NORM2(xmax - xmin, ymax - ymin, zmax - zmin); - } - fnext(*searchtet, neightet); - symself(neightet); - if (neightet.tet != dummytet) { - ntoppo = oppo(neightet); - dist = NORM2(searchpt[0] - ntoppo[0], searchpt[1] - ntoppo[1], - searchpt[2] - ntoppo[2]); - } else { - dist = searchdist; - } - if (dist < searchdist) { - nextmove = APEXMOVE; - searchdist = dist; - } - } else { - // Only the face opposite to destination is viable. - nextmove = DESTMOVE; - } - } else { - if (oriapex < 0) { - // Only the face opposite to apex is viable. - nextmove = APEXMOVE; - } else { - // The point we seek must be on the boundary of or inside this - // tetrahedron. Check for boundary cases. - if (oriorg == 0) { - // Go to the face opposite to origin. - enextfnextself(*searchtet); - if (oridest == 0) { - enextself(*searchtet); // edge apex->oppo - if (oriapex == 0) { - enextself(*searchtet); // oppo is duplicated with p. - loc = ONVERTEX; // return ONVERTEX; - break; - } - loc = ONEDGE; // return ONEDGE; - break; - } - if (oriapex == 0) { - enext2self(*searchtet); - loc = ONEDGE; // return ONEDGE; - break; - } - loc = ONFACE; // return ONFACE; - break; - } - if (oridest == 0) { - // Go to the face opposite to destination. - enext2fnextself(*searchtet); - if (oriapex == 0) { - enextself(*searchtet); - loc = ONEDGE; // return ONEDGE; - break; - } - loc = ONFACE; // return ONFACE; - break; - } - if (oriapex == 0) { - // Go to the face opposite to apex - fnextself(*searchtet); - loc = ONFACE; // return ONFACE; - break; - } - loc = INTETRAHEDRON; // return INTETRAHEDRON; - break; - } - } - } - - // Move to the selected face. - if (nextmove == ORGMOVE) { - enextfnextself(*searchtet); - } else if (nextmove == DESTMOVE) { - enext2fnextself(*searchtet); - } else { - fnextself(*searchtet); - } - // Move to the adjacent tetrahedron (maybe a hull tetrahedron). - backtracetet = *searchtet; - symself(*searchtet); - if (searchtet->tet == dummytet) { - *searchtet = backtracetet; - loc = OUTSIDE; // return OUTSIDE; - break; - } +void tetgenmesh::edgeorthonormal(REAL* e1, REAL* e2, REAL* op, REAL* n) +{ + REAL v1[3], v2[3], fn[3]; + REAL len; - if (histtetarray != NULL) { - // Check if we have run into a loop. - if (infected(*searchtet)) { - // We have visited this tet. A potential loop is found. - loc = OUTSIDE; - break; - } else { - // Remember this tet. - infect(*searchtet); - histtetarray->newindex((void **) &parytet); - *parytet = *searchtet; - } - } + // Get the edge vector v1. + v1[0] = e2[0] - e1[0]; + v1[1] = e2[1] - e1[1]; + v1[2] = e2[2] - e1[2]; + // Get the edge vector v2. + v2[0] = op[0] - e1[0]; + v2[1] = op[1] - e1[1]; + v2[2] = op[2] - e1[2]; + // Get the face normal fn = v1 x v2. + cross(v1, v2, fn); + // Get the edge normal n pointing to op. n = fn x v1. + cross(fn, v1, n); + // Normalize the vector. + len = sqrt(dot(n, n)); + n[0] /= len; + n[1] /= len; + n[2] /= len; +} - // Retreat the three vertices of the base face. - searchtet->ver = 0; - torg = org(*searchtet); - tdest = dest(*searchtet); - tapex = apex(*searchtet); +/////////////////////////////////////////////////////////////////////////////// +// // +// facedihedral() Return the dihedral angle (in radian) between two // +// adjoining faces. // +// // +// 'pa', 'pb' are the shared edge of these two faces, 'pc1', and 'pc2' are // +// apexes of these two faces. Return the angle (between 0 to 2*pi) between // +// the normal of face (pa, pb, pc1) and normal of face (pa, pb, pc2). // +// // +/////////////////////////////////////////////////////////////////////////////// - } // while (true) +REAL tetgenmesh::facedihedral(REAL* pa, REAL* pb, REAL* pc1, REAL* pc2) +{ + REAL n1[3], n2[3]; + REAL n1len, n2len; + REAL costheta, ori; + REAL theta; - if (histtetarray != NULL) { - // Unmark the visited tets. - for (i = 0; i < (int) histtetarray->objects; i++) { - parytet = (triface *) fastlookup(histtetarray, i); - uninfect(*parytet); - } - histtetarray->restart(); + facenormal(pa, pb, pc1, n1, &n1len); + facenormal(pa, pb, pc2, n2, &n2len); + costheta = dot(n1, n2) / (n1len * n2len); + // Be careful rounding error! + if (costheta > 1.0) { + costheta = 1.0; + } else if (costheta < -1.0) { + costheta = -1.0; + } + theta = acos(costheta); + ori = orient3d(pa, pb, pc1, pc2); + if (ori > 0.0) { + theta = 2 * PI - theta; } - return loc; + return theta; } /////////////////////////////////////////////////////////////////////////////// // // -// adjustlocate() Adjust the precise location of a vertex. // -// // -// 'precise' is the value returned from preciselocate(). It indicates the // -// exact location of the point 'searchpt' with respect to the tetrahedron // -// 'searchtet'. 'epspp' is a given relative tolerance. // -// // -// This routine re-evaluates the orientations of searchpt with respect to // -// the four sides of searchtet. Detects the coplanarities by additinal tests // -// which are based on the given tolerance. If 'precise' is ONFACE or ONEDGE, // -// we can save one or two orientation tests. // +// tetalldihedral() Get all (six) dihedral angles of a tet. // // // -// The return value indicates the location of the 'searchpt' (INTETRAHEDRON, // -// or ONFACE, ...). 'searchtet' is adjusted to a tetrahedron corresponding // -// to that value. See the introduction part of preciselocate(). // +// The tet is given by its four corners a, b, c, and d. If 'cosdd' is not // +// NULL, it returns the cosines of the 6 dihedral angles, the corresponding // +// edges are: ab, bc, ca, ad, bd, and cd. If 'cosmaxd' (or 'cosmind') is not // +// NULL, it returns the cosine of the maximal (or minimal) dihedral angle. // // // /////////////////////////////////////////////////////////////////////////////// -enum tetgenmesh::locateresult tetgenmesh::adjustlocate(point searchpt, - triface* searchtet, enum locateresult precise, REAL epspp) +void tetgenmesh::tetalldihedral(point pa, point pb, point pc, point pd, + REAL* cosdd, REAL* cosmaxd, REAL* cosmind) { - point torg, tdest, tapex, toppo; - REAL s1, s2, s3, s4; + REAL N[4][3], cosd, len; + int f1, f2, i, j; - // For the given 'searchtet', the orientations tests are: - // s1: (tdest, torg, tapex, searchpt); - // s2: (torg, tdest, toppo, searchpt); - // s3: (tdest, tapex, toppo, searchpt); - // s4: (tapex, torg, toppo, searchpt); - adjustedgering(*searchtet, CCW); - torg = org(*searchtet); - tdest = dest(*searchtet); - tapex = apex(*searchtet); - toppo = oppo(*searchtet); - - switch (precise) { - case ONVERTEX: - // This case we don't need do any further test. - return ONVERTEX; - case ONEDGE: - // (torg, tdest); - s1 = 0.0; - s2 = 0.0; - break; - case ONFACE: - // (tdest, torg, tapex); - s1 = 0.0; - s2 = orient3d(torg, tdest, toppo, searchpt); - break; - default: // INTETRAHEDRON or OUTSIDE - s1 = orient3d(tdest, torg, tapex, searchpt); - s2 = orient3d(torg, tdest, toppo, searchpt); - } - - if (s1 != 0.0) { - if (iscoplanar(tdest, torg, tapex, searchpt, s1, epspp)) { - s1 = 0.0; - } - } - if (s1 < 0.0) { - return OUTSIDE; - } - - if (s2 != 0.0) { - if (iscoplanar(torg, tdest, toppo, searchpt, s2, epspp)) { - s2 = 0.0; - } - } - if (s2 < 0.0) { - fnextself(*searchtet); - return OUTSIDE; - } - - s3 = orient3d(tdest, tapex, toppo, searchpt); - if (s3 != 0.0) { - if (iscoplanar(tdest, tapex, toppo, searchpt, s3, epspp)) { - s3 = 0.0; - } - } - if (s3 < 0.0) { - enextfnextself(*searchtet); - return OUTSIDE; - } - - s4 = orient3d(tapex, torg, toppo, searchpt); - if (s4 != 0.0) { - if (iscoplanar(tapex, torg, toppo, searchpt, s4, epspp)) { - s4 = 0.0; + // Get four normals of faces of the tet. + tetallnormal(pa, pb, pc, pd, N, NULL); + // Normalize the normals. + for (i = 0; i < 4; i++) { + len = sqrt(dot(N[i], N[i])); + if (len != 0.0) { + for (j = 0; j < 3; j++) N[i][j] /= len; } } - if (s4 < 0.0) { - enext2fnextself(*searchtet); - return OUTSIDE; - } - // Determine degenerate cases. - if (s1 == 0.0) { - if (s2 == 0.0) { - if (s3 == 0.0) { - // On tdest. - enextself(*searchtet); - return ONVERTEX; - } - if (s4 == 0.0) { - // On torg. - return ONVERTEX; - } - // On edge (torg, tdest). - return ONEDGE; - } - if (s3 == 0.0) { - if (s4 == 0.0) { - // On tapex. - enext2self(*searchtet); - return ONVERTEX; - } - // On edge (tdest, tapex). - enextself(*searchtet); - return ONEDGE; - } - if (s4 == 0.0) { - // On edge (tapex, torg). - enext2self(*searchtet); - return ONEDGE; - } - // On face (torg, tdest, tapex). - return ONFACE; - } - if (s2 == 0.0) { - fnextself(*searchtet); - if (s3 == 0.0) { - if (s4 == 0.0) { - // On toppo. - enext2self(*searchtet); - return ONVERTEX; - } - // On edge (tdest, toppo). - enextself(*searchtet); - return ONEDGE; - } - if (s4 == 0.0) { - // On edge (toppo, torg). - enext2self(*searchtet); - return ONEDGE; + for (i = 0; i < 6; i++) { + switch (i) { + case 0: f1 = 2; f2 = 3; break; // edge ab. + case 1: f1 = 0; f2 = 3; break; // edge bc. + case 2: f1 = 1; f2 = 3; break; // edge ca. + case 3: f1 = 1; f2 = 2; break; // edge ad. + case 4: f1 = 2; f2 = 0; break; // edge bd. + case 5: f1 = 0; f2 = 1; break; // edge cd. } - // On face (torg, tdest, toppo). - return ONFACE; - } - if (s3 == 0.0) { - enextfnextself(*searchtet); - if (s4 == 0.0) { - // On edge (tapex, toppo). - enextself(*searchtet); - return ONEDGE; + cosd = -dot(N[f1], N[f2]); + if (cosdd) cosdd[i] = cosd; + if (i == 0) { + if (cosmaxd) *cosmaxd = cosd; + if (cosmind) *cosmind = cosd; + } else { + if (cosmaxd) *cosmaxd = cosd < *cosmaxd ? cosd : *cosmaxd; + if (cosmind) *cosmind = cosd > *cosmind ? cosd : *cosmind; } - // On face (tdest, tapex, toppo). - return ONFACE; - } - if (s4 == 0.0) { - enext2fnextself(*searchtet); - // On face (tapex, torg, toppo). - return ONFACE; } - - // Inside tetrahedron. - return INTETRAHEDRON; } /////////////////////////////////////////////////////////////////////////////// // // -// hullwalk() Find a tetrahedron on the hull to continue search. // +// tetallnormal() Get the in-noramls of the four faces of a given tet. // +// // +// Let tet be abcd. N[4][3] returns the four normals, which are: N[0] cbd, // +// N[1] acd, N[2] bad, N[3] abc. These normals are unnormalized. // // // /////////////////////////////////////////////////////////////////////////////// -enum tetgenmesh::locateresult tetgenmesh::hullwalk(point searchpt, - triface *hulltet) +void tetgenmesh::tetallnormal(point pa, point pb, point pc, point pd, + REAL N[4][3], REAL* volume) { - list* travtetlist; - triface travtet, neightet; - point pa, pb, pc, pp[3]; - enum locateresult loc; - REAL prjpt[3]; - REAL ori; + REAL A[4][4], rhs[4], D; + int indx[4]; int i, j; - travtetlist = new list(sizeof(triface), NULL, 256); - travtet = *hulltet; - infect(travtet); - travtetlist->append(&travtet); - - loc = OUTSIDE; - for (i = 0; i < travtetlist->len(); i++) { - travtet = * (triface *)(* travtetlist)[i]; - // Choose the CCW-edgering in face. - travtet.ver = 0; - // Look for a side where pt lies below it. - for (travtet.loc = 0; travtet.loc < 4; travtet.loc++) { - pa = org(travtet); - pb = dest(travtet); - pc = apex(travtet); - ori = orient3d(pa, pb, pc, searchpt); - if (ori > 0.0) break; - } - // Is pt above all (or coplanar with some of) the four sides? - if (travtet.loc == 4) { - hulltet->tet = travtet.tet; - loc = adjustlocate(searchpt, hulltet, INTETRAHEDRON, b->epsilon); - assert(loc != OUTSIDE); - } else { // ori > 0.0 - // pt is below (behind) this side. We want to walk through it. - sym(travtet, neightet); - if (neightet.tet == dummytet) { - // This is a hull side. Is p approximately on this side. - loc = adjustlocate(searchpt, &travtet, OUTSIDE, b->epsilon); - } - if (loc == OUTSIDE) { - // searchpt is outside the hull face. Project it on the face. - travtet.ver = 1; - pp[0] = org(travtet); - pp[1] = dest(travtet); - pp[2] = apex(travtet); - projpt2face(searchpt, pp[0], pp[1], pp[2], prjpt); - // check if project point inside the hull face. - for (j = 0; j < 3; j++) { - ori = orient3d(pp[j], pp[(j+1)%3], searchpt, prjpt); - if (ori < 0.0) break; // Stop if it lies ouside. - } - if (ori >= 0.0) { - // Yes, return this tet. - *hulltet = travtet; - } - // Let's collect all the neighbors for next searching. - for (travtet.loc = 0; travtet.loc < 4; travtet.loc++) { - sym(travtet, neightet); - if ((neightet.tet != dummytet) && !infected(neightet)) { - // Neighbor exists and not visited. - infect(neightet); - travtetlist->append(&neightet); - } - } // for (travtet.loc = 0; - } // if (loc == OUTSIDE) - } // if (travtet.loc == 4) - if (loc != OUTSIDE) break; - } // for (i = 0; i < travtetlist->len(); i++) - - // Uninfect traversed tets. - for (i = 0; i < travtetlist->len(); i++) { - travtet = * (triface *)(* travtetlist)[i]; - uninfect(travtet); + // get the entries of A[3][3]. + for (i = 0; i < 3; i++) A[0][i] = pa[i] - pd[i]; // d->a vec + for (i = 0; i < 3; i++) A[1][i] = pb[i] - pd[i]; // d->b vec + for (i = 0; i < 3; i++) A[2][i] = pc[i] - pd[i]; // d->c vec + // Compute the inverse of matrix A, to get 3 normals of the 4 faces. + lu_decmp(A, 3, indx, &D, 0); // Decompose the matrix just once. + if (volume != NULL) { + // Get the volume of the tet. + *volume = fabs((A[indx[0]][0] * A[indx[1]][1] * A[indx[2]][2])) / 6.0; } - - delete travtetlist; - return loc; + for (j = 0; j < 3; j++) { + for (i = 0; i < 3; i++) rhs[i] = 0.0; + rhs[j] = 1.0; // Positive means the inside direction + lu_solve(A, 3, indx, rhs, 0); + for (i = 0; i < 3; i++) N[j][i] = rhs[i]; + } + // Get the fourth normal by summing up the first three. + for (i = 0; i < 3; i++) N[3][i] = - N[0][i] - N[1][i] - N[2][i]; } /////////////////////////////////////////////////////////////////////////////// // // -// locatesub() Find a point in the surface mesh of a facet. // -// // -// Searching begins from the input 'searchsh', it should be a handle on the // -// convex hull of the facet triangulation. // +// tetaspectratio() Calculate the aspect ratio of the tetrahedron. // // // -// If 'stopatseg' is nonzero, the search will stop if it tries to walk // -// through a subsegment, and will return OUTSIDE. // -// // -// On completion, 'searchsh' is a subface that contains 'searchpt'. // -// - Returns ONVERTEX if the point lies on an existing vertex. 'searchsh' // -// is a handle whose origin is the existing vertex. // -// - Returns ONEDGE if the point lies on a mesh edge. 'searchsh' is a // -// handle whose primary edge is the edge on which the point lies. // -// - Returns ONFACE if the point lies strictly within a subface. // -// 'searchsh' is a handle on which the point lies. // -// - Returns OUTSIDE if the point lies outside the triangulation. // -// // -// WARNING: This routine is designed for convex triangulations, and will not // -// not generally work after the holes and concavities have been carved. // +// The aspect ratio of a tet is R/h, where R is the circumradius and h is // +// the shortest height of the tet. // // // /////////////////////////////////////////////////////////////////////////////// -enum tetgenmesh::locateresult tetgenmesh::locatesub(point searchpt, - face* searchsh, int stopatseg, REAL epspp) +REAL tetgenmesh::tetaspectratio(point pa, point pb, point pc, point pd) { - face backtracksh, spinsh, checkedge; - point forg, fdest, fapex; - REAL orgori, destori; - REAL ori, sign; - int moveleft, i; + REAL vda[3], vdb[3], vdc[3]; + REAL N[4][3], A[4][4], rhs[4], D; + REAL H[4], volume, radius2, minheightinv; + int indx[4]; + int i, j; - if (searchsh->sh == dummysh) { - searchsh->shver = 0; - spivotself(*searchsh); -#ifdef SELF_CHECK - assert(searchsh->sh != dummysh); -#endif - } - // Find the sign to simulate that abovepoint is 'above' the facet. - adjustedgering(*searchsh, CCW); - forg = sorg(*searchsh); - fdest = sdest(*searchsh); - fapex = sapex(*searchsh); - ori = orient3d(forg, fdest, fapex, abovepoint); - sign = ori > 0.0 ? -1 : 1; + // Set the matrix A = [vda, vdb, vdc]^T. + for (i = 0; i < 3; i++) A[0][i] = vda[i] = pa[i] - pd[i]; + for (i = 0; i < 3; i++) A[1][i] = vdb[i] = pb[i] - pd[i]; + for (i = 0; i < 3; i++) A[2][i] = vdc[i] = pc[i] - pd[i]; + // Lu-decompose the matrix A. + lu_decmp(A, 3, indx, &D, 0); + // Get the volume of abcd. + volume = (A[indx[0]][0] * A[indx[1]][1] * A[indx[2]][2]) / 6.0; + // Check if it is zero. + if (volume == 0.0) return 1.0e+200; // A degenerate tet. + // if (volume < 0.0) volume = -volume; + // Check the radiu-edge ratio of the tet. + rhs[0] = 0.5 * dot(vda, vda); + rhs[1] = 0.5 * dot(vdb, vdb); + rhs[2] = 0.5 * dot(vdc, vdc); + lu_solve(A, 3, indx, rhs, 0); + // Get the circumcenter. + // for (i = 0; i < 3; i++) circumcent[i] = pd[i] + rhs[i]; + // Get the square of the circumradius. + radius2 = dot(rhs, rhs); - // Orient 'searchsh' so that 'searchpt' is below it (i.e., searchpt has - // CCW orientation with respect to searchsh in plane). Such edge - // should always exist. Save it as (forg, fdest). - for (i = 0; i < 3; i++) { - forg = sorg(*searchsh); - fdest = sdest(*searchsh); - ori = orient3d(forg, fdest, abovepoint, searchpt) * sign; - if (ori > 0.0) break; - senextself(*searchsh); + // Compute the 4 face normals (N[0], ..., N[3]). + for (j = 0; j < 3; j++) { + for (i = 0; i < 3; i++) rhs[i] = 0.0; + rhs[j] = 1.0; // Positive means the inside direction + lu_solve(A, 3, indx, rhs, 0); + for (i = 0; i < 3; i++) N[j][i] = rhs[i]; } -#ifdef SELF_CHECK - assert(i < 3); -#endif - - while (1) { - fapex = sapex(*searchsh); - // Check whether the apex is the point we seek. - if (fapex[0] == searchpt[0] && fapex[1] == searchpt[1] && - fapex[2] == searchpt[2]) { - senext2self(*searchsh); - return ONVERTEX; - } - // Does the point lie on the other side of the line defined by the - // triangle edge opposite the triangle's destination? - destori = orient3d(forg, fapex, abovepoint, searchpt) * sign; - if (epspp > 0.0) { - if (iscoplanar(forg, fapex, abovepoint, searchpt, destori, epspp)) { - destori = 0.0; - } - } - // Does the point lie on the other side of the line defined by the - // triangle edge opposite the triangle's origin? - orgori = orient3d(fapex, fdest, abovepoint, searchpt) * sign; - if (epspp > 0.0) { - if (iscoplanar(fapex, fdest, abovepoint, searchpt, orgori, epspp)) { - orgori = 0.0; - } - } - if (destori > 0.0) { - moveleft = 1; - } else { - if (orgori > 0.0) { - moveleft = 0; - } else { - // The point must be on the boundary of or inside this triangle. - if (destori == 0.0) { - senext2self(*searchsh); - return ONEDGE; - } - if (orgori == 0.0) { - senextself(*searchsh); - return ONEDGE; - } - return ONFACE; - } - } - // Move to another triangle. Leave a trace `backtracksh' in case - // walking off a boundary of the triangulation. - if (moveleft) { - senext2(*searchsh, backtracksh); - fdest = fapex; - } else { - senext(*searchsh, backtracksh); - forg = fapex; - } - // Check if we meet a segment. - sspivot(backtracksh, checkedge); - if (checkedge.sh != dummysh) { - if (stopatseg) { - // The flag indicates we should not cross a segment. Stop. - *searchsh = backtracksh; - return OUTSIDE; - } - // Try to walk through a segment. We need to find a coplanar subface - // sharing this segment to get into. - spinsh = backtracksh; - do { - spivotself(spinsh); - if (spinsh.sh == backtracksh.sh) { - // Turn back, no coplanar subface is found. - break; - } - // Are they belong to the same facet. - if (shellmark(spinsh) == shellmark(backtracksh)) { - // Find a coplanar subface. Walk into it. - *searchsh = spinsh; - break; - } - // Are they (nearly) coplanar? - ori = orient3d(forg, fdest, sapex(backtracksh), sapex(spinsh)); - if (iscoplanar(forg, fdest, sapex(backtracksh), sapex(spinsh), ori, - b->epsilon)) { - // Find a coplanar subface. Walk into it. - *searchsh = spinsh; - break; - } - } while (spinsh.sh != backtracksh.sh); - } else { - spivot(backtracksh, *searchsh); - } - // Check for walking right out of the triangulation. - if ((searchsh->sh == dummysh) || (searchsh->sh == backtracksh.sh)) { - // Go back to the last triangle. - *searchsh = backtracksh; - return OUTSIDE; - } - // To keep the same orientation wrt abovepoint. - if (sorg(*searchsh) != forg) sesymself(*searchsh); -#ifdef SELF_CHECK - assert((sorg(*searchsh) == forg) && (sdest(*searchsh) == fdest)); -#endif + // Get the fourth normal by summing up the first three. + for (i = 0; i < 3; i++) N[3][i] = - N[0][i] - N[1][i] - N[2][i]; + // Normalized the normals. + for (i = 0; i < 4; i++) { + // H[i] is the inverse of the height of its corresponding face. + H[i] = sqrt(dot(N[i], N[i])); + // if (H[i] > 0.0) { + // for (j = 0; j < 3; j++) N[i][j] /= H[i]; + // } } + // Get the radius of the inscribed sphere. + // insradius = 1.0 / (H[0] + H[1] + H[2] + H[3]); + // Get the biggest H[i] (corresponding to the smallest height). + minheightinv = H[0]; + for (i = 1; i < 3; i++) { + if (H[i] > minheightinv) minheightinv = H[i]; + } + + return sqrt(radius2) * minheightinv; } /////////////////////////////////////////////////////////////////////////////// // // -// adjustlocatesub() Adjust the precise location of a vertex. // +// circumsphere() Calculate the smallest circumsphere (center and radius) // +// of the given three or four points. // // // -// 'precise' is the precise location (returned from locatesub()) of 'searcht'// -// with respect to 'searchsh'. 'epspp' is the given relative tolerance. // +// The circumsphere of four points (a tetrahedron) is unique if they are not // +// degenerate. If 'pd = NULL', the smallest circumsphere of three points is // +// the diametral sphere of the triangle if they are not degenerate. // // // -// This routine re-evaluates the orientations of 'searchpt' with respect to // -// the three edges of 'searchsh'. Detects the collinearities by additinal // -// tests based on the given tolerance. If 'precise' is ONEDGE, one can save // -// one orientation test for the current edge of 'searchsh'. // +// Return TRUE if the input points are not degenerate and the circumcenter // +// and circumradius are returned in 'cent' and 'radius' respectively if they // +// are not NULLs. Otherwise, return FALSE indicated the points are degenrate.// // // /////////////////////////////////////////////////////////////////////////////// -enum tetgenmesh::locateresult tetgenmesh:: -adjustlocatesub(point searchpt, face* searchsh, enum locateresult precise, - REAL epspp) +bool tetgenmesh:: +circumsphere(REAL* pa, REAL* pb, REAL* pc, REAL* pd, REAL* cent, REAL* radius) { - point pa, pb, pc; - bool s1, s2, s3; + REAL A[4][4], rhs[4], D; + int indx[4]; - pa = sorg(*searchsh); - pb = sdest(*searchsh); - pc = sapex(*searchsh); + // Compute the coefficient matrix A (3x3). + A[0][0] = pb[0] - pa[0]; + A[0][1] = pb[1] - pa[1]; + A[0][2] = pb[2] - pa[2]; + A[1][0] = pc[0] - pa[0]; + A[1][1] = pc[1] - pa[1]; + A[1][2] = pc[2] - pa[2]; + if (pd != NULL) { + A[2][0] = pd[0] - pa[0]; + A[2][1] = pd[1] - pa[1]; + A[2][2] = pd[2] - pa[2]; + } else { + cross(A[0], A[1], A[2]); + } - if (precise == ONEDGE) { - s1 = true; + // Compute the right hand side vector b (3x1). + rhs[0] = 0.5 * dot(A[0], A[0]); + rhs[1] = 0.5 * dot(A[1], A[1]); + if (pd != NULL) { + rhs[2] = 0.5 * dot(A[2], A[2]); } else { - s1 = iscollinear(pa, pb, searchpt, epspp); + rhs[2] = 0.0; } - s2 = iscollinear(pb, pc, searchpt, epspp); - s3 = iscollinear(pc, pa, searchpt, epspp); - if (s1) { - if (s2) { - // on vertex pb. -#ifdef SELF_CHECK - assert(!s3); -#endif - senextself(*searchsh); - return ONVERTEX; - } else if (s3) { - // on vertex pa. - return ONVERTEX; - } else { - // on edge pa->pb. - return ONEDGE; - } - } else if (s2) { - if (s3) { - // on vertex pc. - senext2self(*searchsh); - return ONVERTEX; - } else { - // on edge pb->pc. - senextself(*searchsh); - return ONEDGE; - } - } else if (s3) { - // on edge pc->pa. - senext2self(*searchsh); - return ONEDGE; - } else { - return precise; + + // 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)) { + if (radius != (REAL *) NULL) *radius = 0.0; + return false; + } + lu_solve(A, 3, indx, rhs, 0); + if (cent != (REAL *) NULL) { + cent[0] = pa[0] + rhs[0]; + cent[1] = pa[1] + rhs[1]; + cent[2] = pa[2] + rhs[2]; } + if (radius != (REAL *) NULL) { + *radius = sqrt(rhs[0] * rhs[0] + rhs[1] * rhs[1] + rhs[2] * rhs[2]); + } + return true; } /////////////////////////////////////////////////////////////////////////////// // // -// locateseg() Find a point in subsegments. // +// inscribedsphere() Compute the radius and center of the biggest // +// inscribed sphere of a given tetrahedron. // // // -// Searching begins from the input 'searchseg', it should be a subsegment of // -// the whole segment. // +// The tetrahedron is given by its four points, it must not be degenerate. // +// The center and radius are returned in 'cent' and 'radius' respectively if // +// they are not NULLs. // // // -// On completion, 'searchseg' is a subsegment that contains 'searchpt'. // -// - Returns ONVERTEX if the point lies on an existing vertex. 'searchseg' // -// is a handle whose origin is the existing vertex. // -// - Returns ONEDGE if the point lies inside 'searchseg'. // -// - Returns OUTSIDE if the point lies outside the segment. // +// Geometrical fact. For any simplex in d dimension, // +// r/h1 + r/h2 + ... r/hn = 1 (n <= d + 1); // +// where r is the radius of inscribed ball, and h is the height of each side // +// of the simplex. The value of 'r/h' is just the barycenter coordinates of // +// each vertex of the simplex. Therefore, we can compute the radius and // +// center of the smallest inscribed ball as following equations: // +// r = 1.0 / (1/h1 + 1/h2 + ... + 1/hn); (1) // +// C = r/h1 * P1 + r/h2 * P2 + ... + r/hn * Pn; (2) // +// where C is the vector of center, P1, P2, .. Pn are vectors of vertices. // +// Here (2) contains n linear equations with n variables. (h, P) must be a // +// pair, h is the height from P to its opposite face. // // // /////////////////////////////////////////////////////////////////////////////// -enum tetgenmesh::locateresult tetgenmesh:: -locateseg(point searchpt, face* searchseg) +void tetgenmesh::inscribedsphere(REAL* pa, REAL* pb, REAL* pc, REAL* pd, + REAL* cent, REAL* radius) { - face backtraceseg; - point pa, pb; - REAL dx, dy, dz; - int moveleft; - int i; + REAL N[4][3], H[4]; // Normals (colume vectors) and heights of each face. + REAL rd; + int i; - moveleft = 0; - while (1) { - searchseg->shver = 0; - pa = sorg(*searchseg); - pb = sdest(*searchseg); - // Find the biggest difference in x, y, and z coordinates of a and b. - dx = fabs(pb[0] - pa[0]); - dy = fabs(pb[1] - pa[1]); - dz = fabs(pb[2] - pa[2]); - if (dx > dy) { - if (dx > dz) { - i = 0; - } else { - i = 2; - } - } else { - if (dy > dz) { - i = 1; - } else { - i = 2; - } - } - if (pa[i] < pb[i]) { - if (searchpt[i] < pa[i]) { - moveleft = 1; - } else if (searchpt[i] > pa[i]) { - if (searchpt[i] < pb[i]) { - return ONEDGE; - } else if (searchpt[i] > pb[i]) { - moveleft = 0; - } else { -#ifdef SELF_CHECK - assert(searchpt[i] == pb[i]); -#endif - sesymself(*searchseg); - return ONVERTEX; - } - } else { -#ifdef SELF_CHECK - assert(searchpt[i] == pa[i]); -#endif - return ONVERTEX; - } - } else if (pa[i] > pb[i]) { - if (searchpt[i] < pb[i]) { - moveleft = 0; - } else if (searchpt[i] > pb[i]) { - if (searchpt[i] < pa[i]) { - return ONEDGE; - } else if (searchpt[i] > pa[i]) { - moveleft = 1; - } else { -#ifdef SELF_CHECK - assert(searchpt[i] == pa[i]); -#endif - return ONVERTEX; - } - } else { -#ifdef SELF_CHECK - assert(searchpt[i] == pb[i]); -#endif - sesymself(*searchseg); - return ONVERTEX; - } - } - backtraceseg = *searchseg; - if (moveleft) { - senext2self(*searchseg); - } else { - senextself(*searchseg); - } - spivotself(*searchseg); - if (searchseg->sh == dummysh) { - *searchseg = backtraceseg; - break; - } + // Get the all normals of the tet. + tetallnormal(pa, pb, pc, pd, N, NULL); + for (i = 0; i < 4; i++) { + // H[i] is the inverse of height of its corresponding face. + H[i] = sqrt(dot(N[i], N[i])); + } + // Compute the radius use eq. (1). + rd = 1.0 / (H[0] + H[1] + H[2] + H[3]); + if (radius != (REAL*) NULL) *radius = rd; + if (cent != (REAL*) NULL) { + // Compute the center use eq. (2). + cent[0] = rd * (H[0] * pa[0] + H[1] * pb[0] + H[2] * pc[0] + H[3] * pd[0]); + cent[1] = rd * (H[0] * pa[1] + H[1] * pb[1] + H[2] * pc[1] + H[3] * pd[1]); + cent[2] = rd * (H[0] * pa[2] + H[1] * pb[2] + H[2] * pc[2] + H[3] * pd[2]); } - - return OUTSIDE; } /////////////////////////////////////////////////////////////////////////////// // // -// adjustlocateseg() Adjust the precise location of a vertex on segment. // +// rotatepoint() Create a point by rotating an existing point. // // // -// 'searchpt' is either inside or ouside the segment 'searchseg'. It will be // -// adjusted to on vertex if it is very close to an endpoint of 'searchseg'. // -// 'epspp' is the given relative tolerance. // +// Create a 3D point by rotating point 'p' with an angle 'rotangle' (in arc // +// degree) around a rotating axis given by a vector from point 'p1' to 'p2'. // +// The rotation is according with right-hand rule, i.e., use your right-hand // +// to grab the axis with your thumber pointing to its positive direction, // +// your fingers indicate the rotating direction. // +// // +// The rotating steps are the following: // +// 1. Translate vector 'p1->p2' to origin, M1; // +// 2. Rotate vector around the Y-axis until it lies in the YZ plane, M2; // +// 3. Rotate vector around the X-axis until it lies on the Z axis, M3; // +// 4. Perform the rotation of 'p' around the z-axis, M4; // +// 5. Undo Step 3, M5; // +// 6. Undo Step 2, M6; // +// 7. Undo Step 1, M7; // +// Use matrix multiplication to combine the above sequences, we get: // +// p0' = T * p0, where T = M7 * M6 * M5 * M4 * M3 * M2 * M1 // // // /////////////////////////////////////////////////////////////////////////////// -enum tetgenmesh::locateresult tetgenmesh:: -adjustlocateseg(point searchpt, face* searchseg, enum locateresult precise, - REAL epspp) +void tetgenmesh::rotatepoint(REAL* p, REAL rotangle, REAL* p1, REAL* p2) { - point pa, pb; - REAL L, d, r; + REAL T[4][4], pp0[4], p0t[4], p2t[4]; + REAL roty, rotx, alphaR, projlen; + REAL dx, dy, dz; - pa = sorg(*searchseg); - pb = sdest(*searchseg); - L = distance(pa, pb); + initm44(1, 0, 0, -p1[0], + 0, 1, 0, -p1[1], + 0, 0, 1, -p1[2], + 0, 0, 0, 1, T); + pp0[0] = p[0]; pp0[1] = p[1]; pp0[2] = p[2]; pp0[3] = 1.0; + m4xv4(p0t, T, pp0); // Step 1 + pp0[0] = p2[0]; pp0[1] = p2[1]; pp0[2] = p2[2]; pp0[3] = 1.0; + m4xv4(p2t, T, pp0); // Step 1 - // Is searchpt approximate to pa? - d = distance(pa, searchpt); - r = d / L; - if (r <= epspp) { - return ONVERTEX; - } - // Is searchpt approximate to pb? - d = distance(pb, searchpt); - r = d / L; - if (r <= epspp) { - sesymself(*searchseg); - return ONVERTEX; + // Get the rotation angle around y-axis; + dx = p2t[0]; + dz = p2t[2]; + projlen = sqrt(dx * dx + dz * dz); + if (projlen <= (b->epsilon * 1e-2) * longest) { + roty = 0; + } else { + roty = acos(dz / projlen); + if (dx < 0) { + roty = -roty; + } } - return precise; -} - -//// //// -//// //// -//// geom_cxx ///////////////////////////////////////////////////////////////// + initm44(cos(-roty), 0, sin(-roty), 0, + 0, 1, 0, 0, + -sin(-roty), 0, cos(-roty), 0, + 0, 0, 0, 1, T); + pp0[0] = p0t[0]; pp0[1] = p0t[1]; pp0[2] = p0t[2]; pp0[3] = 1.0; + m4xv4(p0t, T, pp0); // Step 2 + pp0[0] = p2t[0]; pp0[1] = p2t[1]; pp0[2] = p2t[2]; pp0[3] = 1.0; + m4xv4(p2t, T, pp0); // Step 2 -//// flip_cxx ///////////////////////////////////////////////////////////////// -//// //// -//// //// + // Get the rotation angle around x-axis + dy = p2t[1]; + dz = p2t[2]; + projlen = sqrt(dy * dy + dz * dz); + if (projlen <= (b->epsilon * 1e-2) * longest) { + rotx = 0; + } else { + rotx = acos(dz / projlen); + if (dy < 0) { + rotx = -rotx; + } + } + + initm44(1, 0, 0, 0, + 0, cos(rotx), -sin(rotx), 0, + 0, sin(rotx), cos(rotx), 0, + 0, 0, 0, 1, T); + pp0[0] = p0t[0]; pp0[1] = p0t[1]; pp0[2] = p0t[2]; pp0[3] = 1.0; + m4xv4(p0t, T, pp0); // Step 3 + // pp0[0] = p2t[0]; pp0[1] = p2t[1]; pp0[2] = p2t[2]; pp0[3] = 1.0; + // m4xv4(p2t, T, pp0); // Step 3 -/////////////////////////////////////////////////////////////////////////////// -// // -// enqueueflipface(), enqueueflipedge() Queue a face (or an edge). // -// // -// The face (or edge) may be non-locally Delaunay. It is queued for process- // -// ing in flip() (or flipsub()). The vertices of the face (edge) are stored // -// seperatly to ensure the face (or edge) is still the same one when we save // -// it since other flips will cause this face (or edge) be changed or dead. // -// // -/////////////////////////////////////////////////////////////////////////////// + alphaR = rotangle; + initm44(cos(alphaR), -sin(alphaR), 0, 0, + sin(alphaR), cos(alphaR), 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1, T); + pp0[0] = p0t[0]; pp0[1] = p0t[1]; pp0[2] = p0t[2]; pp0[3] = 1.0; + m4xv4(p0t, T, pp0); // Step 4 -void tetgenmesh::enqueueflipface(triface& checkface, queue* flipqueue) -{ - badface *queface; - triface symface; + initm44(1, 0, 0, 0, + 0, cos(-rotx), -sin(-rotx), 0, + 0, sin(-rotx), cos(-rotx), 0, + 0, 0, 0, 1, T); + pp0[0] = p0t[0]; pp0[1] = p0t[1]; pp0[2] = p0t[2]; pp0[3] = 1.0; + m4xv4(p0t, T, pp0); // Step 5 - sym(checkface, symface); - if (symface.tet != dummytet) { - queface = (badface *) flipqueue->push((void *) NULL); - queface->tt = checkface; - queface->foppo = oppo(symface); - } + initm44(cos(roty), 0, sin(roty), 0, + 0, 1, 0, 0, + -sin(roty), 0, cos(roty), 0, + 0, 0, 0, 1, T); + pp0[0] = p0t[0]; pp0[1] = p0t[1]; pp0[2] = p0t[2]; pp0[3] = 1.0; + m4xv4(p0t, T, pp0); // Step 6 + + initm44(1, 0, 0, p1[0], + 0, 1, 0, p1[1], + 0, 0, 1, p1[2], + 0, 0, 0, 1, T); + pp0[0] = p0t[0]; pp0[1] = p0t[1]; pp0[2] = p0t[2]; pp0[3] = 1.0; + m4xv4(p0t, T, pp0); // Step 7 + + p[0] = p0t[0]; + p[1] = p0t[1]; + p[2] = p0t[2]; } -void tetgenmesh::enqueueflipedge(face& checkedge, queue* flipqueue) +/////////////////////////////////////////////////////////////////////////////// +// // +// spherelineint() 3D line sphere (or circle) intersection. // +// // +// The line is given by two points p1, and p2, the sphere is centered at c // +// with radius r. This function returns a pointer array p which first index // +// indicates the number of intersection point, followed by coordinate pairs. // +// // +// The following code are adapted from: http://astronomy.swin.edu.au/pbourke // +// /geometry/sphereline. Paul Bourke pbourke@swin.edu.au // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::spherelineint(REAL* p1, REAL* p2, REAL* C, REAL R, REAL p[7]) { - badface *queface; + REAL x1, y1, z1; // P1 coordinates (point of line) + REAL x2, y2, z2; // P2 coordinates (point of line) + REAL x3, y3, z3, r; // P3 coordinates and radius (sphere) + REAL a, b, c, mu, i ; - queface = (badface *) flipqueue->push((void *) NULL); - queface->ss = checkedge; - queface->forg = sorg(checkedge); - queface->fdest = sdest(checkedge); + x1 = p1[0]; y1 = p1[1]; z1 = p1[2]; + x2 = p2[0]; y2 = p2[1]; z2 = p2[2]; + x3 = C[0]; y3 = C[1]; z3 = C[2]; + r = R; + + a = (x2 - x1) * (x2 - x1) + + (y2 - y1) * (y2 - y1) + + (z2 - z1) * (z2 - z1); + b = 2 * ( (x2 - x1) * (x1 - x3) + + (y2 - y1) * (y1 - y3) + + (z2 - z1) * (z1 - z3) ) ; + c = (x3 * x3) + (y3 * y3) + (z3 * z3) + + (x1 * x1) + (y1 * y1) + (z1 * z1) + - 2 * (x3 * x1 + y3 * y1 + z3 * z1) - (r * r) ; + i = b * b - 4 * a * c ; + + if (i < 0.0) { + // no intersection + p[0] = 0.0; + } else if (i == 0.0) { + // one intersection + p[0] = 1.0; + mu = -b / (2 * a) ; + p[1] = x1 + mu * (x2 - x1); + p[2] = y1 + mu * (y2 - y1); + p[3] = z1 + mu * (z2 - z1); + } else { + // two intersections + p[0] = 2.0; + // first intersection + mu = (-b + sqrt((b * b) - 4 * a * c)) / (2 * a); + p[1] = x1 + mu * (x2 - x1); + p[2] = y1 + mu * (y2 - y1); + p[3] = z1 + mu * (z2 - z1); + // second intersection + mu = (-b - sqrt((b * b) - 4 * a * c)) / (2 * a); + p[4] = x1 + mu * (x2 - x1); + p[5] = y1 + mu * (y2 - y1); + p[6] = z1 + mu * (z2 - z1); + } } /////////////////////////////////////////////////////////////////////////////// // // -// flip23() Perform a 2-to-3 flip. // +// linelineint() Calculate the shortest line between two lines in 3D. // // // -// On input, 'flipface' represents the face will be flipped. Let it is abc, // -// the two tetrahedra sharing abc are abcd, bace. abc is not a subface. // +// Two 3D lines generally don't intersect at a point, they may be parallel ( // +// no intersections), or coincident (infinite intersections) but most often // +// only their projections onto a plane intersect. If they don't exactly int- // +// ersect at a point they can be connected by a line segment, the shortest // +// segment is unique and is often considered to be their intersection in 3D. // // // -// A 2-to-3 flip is to change two tetrahedra abcd, bace to three tetrahedra // -// edab, edbc, and edca. As a result, face abc has been removed and three // -// new faces eda, edb and edc have been created. // +// The following code are adapted from: http://astronomy.swin.edu.au/pbourke // +// /geometry/lineline3d. Paul Bourke pbourke@swin.edu.au // // // -// On completion, 'flipface' returns edab. If 'flipqueue' is not NULL, all // -// possibly non-Delaunay faces are added into it. // +// Calculate the line segment PaPb that is the shortest route between two // +// lines P1P2 and P3P4. This function returns a pointer array p which first // +// index indicates there exists solution or not, 0 means no solution, 1 meas // +// has solution followed by two coordinate pairs. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::flip23(triface* flipface, queue* flipqueue) +void tetgenmesh::linelineint(REAL *p1,REAL *p2, REAL *p3, REAL *p4, REAL p[7]) { - triface abcd, bace; // Old configuration. - triface oldabd, oldbcd, oldcad; - triface abdcasing, bcdcasing, cadcasing; - triface oldbae, oldcbe, oldace; - triface baecasing, cbecasing, acecasing; - triface worktet; - face abdsh, bcdsh, cadsh; // The six subfaces on the CH. - face baesh, cbesh, acesh; - face abseg, bcseg, caseg; // The nine segs on the CH. - face adseg, bdseg, cdseg; - face aeseg, beseg, ceseg; - triface edab, edbc, edca; // New configuration. - point pa, pb, pc, pd, pe; - REAL attrib, volume; - int i; + REAL p13[3], p43[3], p21[3]; + REAL d1343, d4321, d1321, d4343, d2121; + REAL numer, denom; + REAL mua, mub; - abcd = *flipface; - adjustedgering(abcd, CCW); // abcd represents edge ab. - pa = org(abcd); - pb = dest(abcd); - pc = apex(abcd); - pd = oppo(abcd); - // sym(abcd, bace); - // findedge(&bace, dest(abcd), org(abcd)); // bace represents edge ba. - sym(abcd, bace); - bace.ver = 0; // CCW. - for (i = 0; (i < 3) && (org(bace) != pb); i++) { - enextself(bace); + p13[0] = p1[0] - p3[0]; + p13[1] = p1[1] - p3[1]; + p13[2] = p1[2] - p3[2]; + p43[0] = p4[0] - p3[0]; + p43[1] = p4[1] - p3[1]; + p43[2] = p4[2] - p3[2]; + if (p43[0] == 0.0 && p43[1] == 0.0 && p43[2] == 0.0) { + p[0] = 0.0; + return; } - pe = oppo(bace); - if (b->verbose > 1) { - printf(" Do T23 on face (%d, %d, %d) %d, %d.\n", pointmark(pa), - pointmark(pb), pointmark(pc), pointmark(pd), pointmark(pe)); + p21[0] = p2[0] - p1[0]; + p21[1] = p2[1] - p1[1]; + p21[2] = p2[2] - p1[2]; + if (p21[0] == 0.0 && p21[1] == 0.0 && p21[2] == 0.0) { + p[0] = 0.0; + return; } - flip23s++; - // Storing the old configuration outside the convex hull. - fnext(abcd, oldabd); - enextfnext(abcd, oldbcd); - enext2fnext(abcd, oldcad); - fnext(bace, oldbae); - enext2fnext(bace, oldcbe); - enextfnext(bace, oldace); - sym(oldabd, abdcasing); - sym(oldbcd, bcdcasing); - sym(oldcad, cadcasing); - sym(oldbae, baecasing); - sym(oldcbe, cbecasing); - sym(oldace, acecasing); - if (checksubfaces) { - tspivot(oldabd, abdsh); - tspivot(oldbcd, bcdsh); - tspivot(oldcad, cadsh); - tspivot(oldbae, baesh); - tspivot(oldcbe, cbesh); - tspivot(oldace, acesh); - } - if (checksubsegs) { - tsspivot1(abcd, abseg); - enext(abcd, worktet); - tsspivot1(worktet, bcseg); - enext2(abcd, worktet); - tsspivot1(worktet, caseg); - enext2(oldabd, worktet); - tsspivot1(worktet, adseg); - enext2(oldbcd, worktet); - tsspivot1(worktet, bdseg); - enext2(oldcad, worktet); - tsspivot1(worktet, cdseg); - enext(oldbae, worktet); - tsspivot1(worktet, aeseg); - enext(oldcbe, worktet); - tsspivot1(worktet, beseg); - enext(oldace, worktet); - tsspivot1(worktet, ceseg); - } + d1343 = p13[0] * p43[0] + p13[1] * p43[1] + p13[2] * p43[2]; + d4321 = p43[0] * p21[0] + p43[1] * p21[1] + p43[2] * p21[2]; + d1321 = p13[0] * p21[0] + p13[1] * p21[1] + p13[2] * p21[2]; + d4343 = p43[0] * p43[0] + p43[1] * p43[1] + p43[2] * p43[2]; + d2121 = p21[0] * p21[0] + p21[1] * p21[1] + p21[2] * p21[2]; - // Creating the new configuration inside the convex hull. - edab.tet = abcd.tet; // Update abcd to be edab. - setorg (edab, pe); - setdest(edab, pd); - setapex(edab, pa); - setoppo(edab, pb); - edbc.tet = bace.tet; // Update bace to be edbc. - setorg (edbc, pe); - setdest(edbc, pd); - setapex(edbc, pb); - setoppo(edbc, pc); - maketetrahedron(&edca); // Create edca. - setorg (edca, pe); - setdest(edca, pd); - setapex(edca, pc); - setoppo(edca, pa); - // Set the element attributes of the new tetrahedron 'edca'. - for (i = 0; i < in->numberoftetrahedronattributes; i++) { - attrib = elemattribute(abcd.tet, i); - setelemattribute(edca.tet, i, attrib); - } - // Set the volume constraint of the new tetrahedron 'edca' if the -ra - // switches are not used together. In -ra case, the various volume - // constraints can be spreaded very far. - if (b->varvolume && !b->refine) { - volume = volumebound(abcd.tet); - setvolumebound(edca.tet, volume); + denom = d2121 * d4343 - d4321 * d4321; + if (denom == 0.0) { + p[0] = 0.0; + return; } + numer = d1343 * d4321 - d1321 * d4343; + mua = numer / denom; + mub = (d1343 + d4321 * mua) / d4343; - // Clear old bonds in edab(was abcd) and edbc(was bace). - for (i = 0; i < 4; i ++) { - edab.tet[i] = (tetrahedron) dummytet; - } - for (i = 0; i < 4; i ++) { - edbc.tet[i] = (tetrahedron) dummytet; - } - // Bond the faces inside the convex hull. - edab.loc = 0; - edca.loc = 1; - bond(edab, edca); - edab.loc = 1; - edbc.loc = 0; - bond(edab, edbc); - edbc.loc = 1; - edca.loc = 0; - bond(edbc, edca); - // Bond the faces on the convex hull. - edab.loc = 2; - bond(edab, abdcasing); - edab.loc = 3; - bond(edab, baecasing); - edbc.loc = 2; - bond(edbc, bcdcasing); - edbc.loc = 3; - bond(edbc, cbecasing); - edca.loc = 2; - bond(edca, cadcasing); - edca.loc = 3; - bond(edca, acecasing); - // There may exist subfaces that need to be bonded to new configuarton. - if (checksubfaces) { - // Clear old flags in edab(was abcd) and edbc(was bace). - for (i = 0; i < 4; i ++) { - edab.loc = i; - tsdissolve(edab); - edbc.loc = i; - tsdissolve(edbc); - } - if (abdsh.sh != dummysh) { - edab.loc = 2; - tsbond(edab, abdsh); - } - if (baesh.sh != dummysh) { - edab.loc = 3; - tsbond(edab, baesh); - } - if (bcdsh.sh != dummysh) { - edbc.loc = 2; - tsbond(edbc, bcdsh); - } - if (cbesh.sh != dummysh) { - edbc.loc = 3; - tsbond(edbc, cbesh); - } - if (cadsh.sh != dummysh) { - edca.loc = 2; - tsbond(edca, cadsh); - } - if (acesh.sh != dummysh) { - edca.loc = 3; - tsbond(edca, acesh); - } - } - if (checksubsegs) { - for (i = 0; i < 6; i++) { - edab.loc = edge2locver[i][0]; - edab.ver = edge2locver[i][1]; - tssdissolve1(edab); - } - for (i = 0; i < 6; i++) { - edbc.loc = edge2locver[i][0]; - edbc.ver = edge2locver[i][1]; - tssdissolve1(edbc); - } - edab.loc = edab.ver = 0; - edbc.loc = edab.ver = 0; - edca.loc = edab.ver = 0; - // Operate in tet edab (5 edges). - enext(edab, worktet); - tssbond1(worktet, adseg); - enext2(edab, worktet); - tssbond1(worktet, aeseg); - fnext(edab, worktet); - enextself(worktet); - tssbond1(worktet, bdseg); - enextself(worktet); - tssbond1(worktet, beseg); - enextfnext(edab, worktet); - enextself(worktet); - tssbond1(worktet, abseg); - // Operate in tet edbc (5 edges) - enext(edbc, worktet); - tssbond1(worktet, bdseg); - enext2(edbc, worktet); - tssbond1(worktet, beseg); - fnext(edbc, worktet); - enextself(worktet); - tssbond1(worktet, cdseg); - enextself(worktet); - tssbond1(worktet, ceseg); - enextfnext(edbc, worktet); - enextself(worktet); - tssbond1(worktet, bcseg); - // Operate in tet edca (5 edges) - enext(edca, worktet); - tssbond1(worktet, cdseg); - enext2(edca, worktet); - tssbond1(worktet, ceseg); - fnext(edca, worktet); - enextself(worktet); - tssbond1(worktet, adseg); - enextself(worktet); - tssbond1(worktet, aeseg); - enextfnext(edca, worktet); - enextself(worktet); - tssbond1(worktet, caseg); + p[0] = 1.0; + p[1] = p1[0] + mua * p21[0]; + p[2] = p1[1] + mua * p21[1]; + p[3] = p1[2] + mua * p21[2]; + p[4] = p3[0] + mub * p43[0]; + p[5] = p3[1] + mub * p43[1]; + p[6] = p3[2] + mub * p43[2]; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// planelineint() Calculate the intersection of a line and a plane. // +// // +// The equation of a plane (points P are on the plane with normal N and P3 // +// on the plane) can be written as: N dot (P - P3) = 0. The equation of the // +// line (points P on the line passing through P1 and P2) can be written as: // +// P = P1 + u (P2 - P1). The intersection of these two occurs when: // +// N dot (P1 + u (P2 - P1)) = N dot P3. // +// Solving for u gives: // +// N dot (P3 - P1) // +// u = ------------------. // +// N dot (P2 - P1) // +// If the denominator is 0 then N (the normal to the plane) is perpendicular // +// to the line. Thus the line is either parallel to the plane and there are // +// no solutions or the line is on the plane in which case there are an infi- // +// nite number of solutions. // +// // +// The plane is given by three points pa, pb, and pc, e1 and e2 defines the // +// line. If u is non-zero, The intersection point (if exists) returns in ip. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::planelineint(REAL* pa, REAL* pb, REAL* pc, REAL* e1, REAL* e2, + REAL* ip, REAL* u) +{ + REAL n[3], det, det1; + + // Calculate N. + facenormal(pa, pb, pc, n, NULL); + // Calculate N dot (e2 - e1). + det = n[0] * (e2[0] - e1[0]) + n[1] * (e2[1] - e1[1]) + + n[2] * (e2[2] - e1[2]); + if (det != 0.0) { + // Calculate N dot (pa - e1) + det1 = n[0] * (pa[0] - e1[0]) + n[1] * (pa[1] - e1[1]) + + n[2] * (pa[2] - e1[2]); + *u = det1 / det; + ip[0] = e1[0] + *u * (e2[0] - e1[0]); + ip[1] = e1[1] + *u * (e2[1] - e1[1]); + ip[2] = e1[2] + *u * (e2[2] - e1[2]); + } else { + *u = 0.0; } +} - edab.loc = 0; - edbc.loc = 0; - edca.loc = 0; - if (b->verbose > 3) { - printf(" Updating edab "); - printtet(&edab); - printf(" Updating edbc "); - printtet(&edbc); - printf(" Creating edca "); - printtet(&edca); - } +// +// End of Geometric quantities calculators +// - // Update point-to-tet map. - setpoint2tet(pa, encode(edab)); - setpoint2tet(pb, encode(edab)); - setpoint2tet(pc, encode(edbc)); - setpoint2tet(pd, encode(edab)); - setpoint2tet(pe, encode(edab)); +// +// Begin of memory management routines +// - if (flipqueue != (queue *) NULL) { - enextfnext(edab, abdcasing); - enqueueflipface(abdcasing, flipqueue); - enext2fnext(edab, baecasing); - enqueueflipface(baecasing, flipqueue); - enextfnext(edbc, bcdcasing); - enqueueflipface(bcdcasing, flipqueue); - enext2fnext(edbc, cbecasing); - enqueueflipface(cbecasing, flipqueue); - enextfnext(edca, cadcasing); - enqueueflipface(cadcasing, flipqueue); - enext2fnext(edca, acecasing); - enqueueflipface(acecasing, flipqueue); - } +/////////////////////////////////////////////////////////////////////////////// +// // +// dummyinit() Initialize the tetrahedron that fills "outer space" and // +// the omnipresent subface. // +// // +// The tetrahedron that fills "outer space" called 'dummytet', is pointed to // +// by every tetrahedron and subface on a boundary (be it outer or inner) of // +// the tetrahedralization. Also, 'dummytet' points to one of the tetrahedron // +// on the convex hull(until the holes and concavities are carved), making it // +// possible to find a starting tetrahedron for point location. // +// // +// The omnipresent subface,'dummysh', is pointed to by every tetrahedron or // +// subface that doesn't have a full complement of real subface to point to. // +// // +/////////////////////////////////////////////////////////////////////////////// - // Save a live handle in 'recenttet'. - recenttet = edbc; - // Set the return handle be edab. - *flipface = edab; +void tetgenmesh::dummyinit(int tetwords, int shwords) +{ + unsigned long alignptr; + + // Set up 'dummytet', the 'tetrahedron' that occupies "outer space". + dummytetbase = (tetrahedron *) new char[tetwords * sizeof(tetrahedron) + + tetrahedrons->alignbytes]; + // Align 'dummytet' on a 'tetrahedrons->alignbytes'-byte boundary. + alignptr = (unsigned long) dummytetbase; + dummytet = (tetrahedron *) + (alignptr + (unsigned long) tetrahedrons->alignbytes + - (alignptr % (unsigned long) tetrahedrons->alignbytes)); + // Initialize the four adjoining tetrahedra to be "outer space". These + // will eventually be changed by various bonding operations, but their + // values don't really matter, as long as they can legally be + // dereferenced. + dummytet[0] = (tetrahedron) dummytet; + dummytet[1] = (tetrahedron) dummytet; + dummytet[2] = (tetrahedron) dummytet; + dummytet[3] = (tetrahedron) dummytet; + // Four null vertex points. + dummytet[4] = (tetrahedron) NULL; + dummytet[5] = (tetrahedron) NULL; + dummytet[6] = (tetrahedron) NULL; + dummytet[7] = (tetrahedron) NULL; + + if (b->useshelles) { + // Set up 'dummysh', the omnipresent "subface" pointed to by any + // tetrahedron side or subface end that isn't attached to a real + // subface. + dummyshbase = (shellface *) new char[shwords * sizeof(shellface) + + subfaces->alignbytes]; + // Align 'dummysh' on a 'subfaces->alignbytes'-byte boundary. + alignptr = (unsigned long) dummyshbase; + dummysh = (shellface *) + (alignptr + (unsigned long) subfaces->alignbytes + - (alignptr % (unsigned long) subfaces->alignbytes)); + // Initialize the three adjoining subfaces to be the omnipresent + // subface. These will eventually be changed by various bonding + // operations, but their values don't really matter, as long as they + // can legally be dereferenced. + dummysh[0] = (shellface) dummysh; + dummysh[1] = (shellface) dummysh; + dummysh[2] = (shellface) dummysh; + // Three null vertex points. + dummysh[3] = (shellface) NULL; + dummysh[4] = (shellface) NULL; + dummysh[5] = (shellface) NULL; + // Initialize the two adjoining tetrahedra to be "outer space". + dummysh[6] = (shellface) dummytet; + dummysh[7] = (shellface) dummytet; + // Initialize the three adjoining subsegments to be "out boundary". + dummysh[8] = (shellface) dummysh; + dummysh[9] = (shellface) dummysh; + dummysh[10] = (shellface) dummysh; + // Initialize the pointer to badface structure. + dummysh[11] = (shellface) NULL; + // Initialize the four adjoining subfaces of 'dummytet' to be the + // omnipresent subface. + dummytet[8 ] = (tetrahedron) dummysh; + dummytet[9 ] = (tetrahedron) dummysh; + dummytet[10] = (tetrahedron) dummysh; + dummytet[11] = (tetrahedron) dummysh; + } } /////////////////////////////////////////////////////////////////////////////// // // -// flip32() Perform a 3-to-2 flip. // -// // -// On input, 'flipface' represents the face will be flipped. Let it is eda, // -// where edge ed is locally non-convex. Three tetrahedra sharing ed are edab,// -// edbc, and edca. ed is not a subsegment. // +// initializepools() Calculate the sizes of the point, tetrahedron, and // +// subface. Initialize their memory pools. // // // -// A 3-to-2 flip is to change the three tetrahedra edab, edbc, and edca into // -// another two tetrahedra abcd and bace. As a result, the edge ed has been // -// removed and the face abc has been created. // +// This routine also computes the indices 'pointmarkindex', 'point2simindex',// +// and 'point2pbcptindex' used to find values within each point; computes // +// indices 'highorderindex', 'elemattribindex', and 'volumeboundindex' used // +// to find values within each tetrahedron. // // // -// On completion, 'flipface' returns abcd. If 'flipqueue' is not NULL, all // -// possibly non-Delaunay faces are added into it. // +// There are two types of boundary elements, which are subfaces and subsegs, // +// they are stored in seperate pools. However, the data structures of them // +// are the same. A subsegment can be regarded as a degenerate subface, i.e.,// +// one of its three corners is not used. We set the apex of it be 'NULL' to // +// distinguish it's a subsegment. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::flip32(triface* flipface, queue* flipqueue) +void tetgenmesh::initializepools() { - triface edab, edbc, edca; // Old configuration. - triface oldabd, oldbcd, oldcad; - triface abdcasing, bcdcasing, cadcasing; - triface oldbae, oldcbe, oldace; - triface baecasing, cbecasing, acecasing; - triface worktet; - face abdsh, bcdsh, cadsh; - face baesh, cbesh, acesh; - face abseg, bcseg, caseg; // The nine segs on the CH. - face adseg, bdseg, cdseg; - face aeseg, beseg, ceseg; - triface abcd, bace; // New configuration. - point pa, pb, pc, pd, pe; - int i; + enum wordtype wtype; + int pointsize, elesize, shsize; - edab = *flipface; - adjustedgering(edab, CCW); - pa = apex(edab); - pb = oppo(edab); - pd = dest(edab); - pe = org(edab); - fnext(edab, edbc); - symself(edbc); - edbc.ver = 0; - for (i = 0; (i < 3) && (org(edbc) != pe); i++) { - enextself(edbc); - } - pc = oppo(edbc); - fnext(edbc, edca); - symself(edca); - edca.ver = 0; - for (i = 0; (i < 3) && (org(edca) != pe); i++) { - enextself(edca); + // Default checkpbc = 0; + if ((b->plc || b->refine) && (in->pbcgrouplist != NULL)) { + checkpbcs = 1; } - - if (b->verbose > 1) { - printf(" Do T32 on edge (%d, %d) %d, %d, %d.\n", pointmark(pe), - pointmark(pd), pointmark(pa), pointmark(pb), pointmark(pc)); + // Default varconstraint = 0; + if (in->segmentconstraintlist || in->facetconstraintlist) { + varconstraint = 1; } - flip32s++; - // Storing the old configuration outside the convex hull. - enextfnext(edab, oldabd); - enext2fnext(edab, oldbae); - enextfnext(edbc, oldbcd); - enext2fnext(edbc, oldcbe); - enextfnext(edca, oldcad); - enext2fnext(edca, oldace); - sym(oldabd, abdcasing); - sym(oldbcd, bcdcasing); - sym(oldcad, cadcasing); - sym(oldbae, baecasing); + // The index within each point at which its metric tensor is found. It is + // saved directly after the list of point attributes. + pointmtrindex = 3 + in->numberofpointattributes; + // Decide the size (1, 3, or 6) of the metric tensor. + if (b->metric) { + // For '-m' option. A tensor field is provided (*.mtr or *.b.mtr file). + if (bgm != (tetgenmesh *) NULL) { + // A background mesh is allocated. It may not exist though. + sizeoftensor = (bgm->in != (tetgenio *) NULL) ? + bgm->in->numberofpointmtrs : in->numberofpointmtrs; + } else { + // No given background mesh - Itself is a background mesh. + sizeoftensor = in->numberofpointmtrs; + } + // Make sure sizeoftensor is at least 1. + sizeoftensor = (sizeoftensor > 0) ? sizeoftensor : 1; + } else { + // For '-q' option. Make sure to have space for saving a scalar value. + sizeoftensor = b->quality ? 1 : 0; + } + // The index within each point at which an element pointer is found, where + // the index is measured in pointers. Ensure the index is aligned to a + // sizeof(tetrahedron)-byte address. + point2simindex = ((pointmtrindex + sizeoftensor) * sizeof(REAL) + + sizeof(tetrahedron) - 1) / sizeof(tetrahedron); + if (b->plc || b->refine) { + // Increase the point size by three pointers, which are: + // - a pointer to a tet, read by point2tet(); + // - a pointer to a subface/subsegment , read by point2sh(); + // - a pointer to a parent point, read by point2ppt()). + if (b->metric) { + // Increase one pointer to a tet of the background mesh. + 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 { + pointsize = point2simindex * sizeof(tetrahedron); + } + // The index within each point at which the boundary marker is found, + // Ensure the point marker is aligned to a sizeof(int)-byte address. + pointmarkindex = (pointsize + sizeof(int) - 1) / sizeof(int); + // Now point size is the ints (inidcated by pointmarkindex) plus: + // - an integer for boundary marker; + // - an integer for vertex type; + pointsize = (pointmarkindex + 2) * sizeof(int); + // Decide the wordtype used in vertex pool. + wtype = (sizeof(REAL) >= sizeof(tetrahedron)) ? FLOATINGPOINT : POINTER; + // Initialize the pool of vertices. + points = new memorypool(pointsize, VERPERBLOCK, wtype, 0); + + // The number of bytes occupied by a tetrahedron. There are four pointers + // to other tetrahedra, four pointers to corners, and possibly four + // pointers to subfaces. + elesize = (8 + b->useshelles * 6) * sizeof(tetrahedron); + // If Voronoi diagram is wanted, make sure we have additional space. + if (b->voroout && (b->useshelles == 0)) { + elesize = (8 + 4) * sizeof(tetrahedron); + } + // 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); + // 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) { + elesize = volumeboundindex * sizeof(REAL); + } + // If element neighbor graph is requested (-n switch), an additional + // integer is allocated for each element. + elemmarkerindex = (elesize + sizeof(int) - 1) / sizeof(int); + if (b->neighout || b->voroout) { + elesize = (elemmarkerindex + 1) * sizeof(int); + } + // If -o2 switch is used, an additional pointer pointed to the list of + // higher order nodes is allocated for each element. + highorderindex = (elesize + sizeof(tetrahedron) - 1) / sizeof(tetrahedron); + if (b->order == 2) { + elesize = (highorderindex + 1) * sizeof(tetrahedron); + } + // Having determined the memory size of an element, initialize the pool. + tetrahedrons = new memorypool(elesize, ELEPERBLOCK, POINTER, 8); + + if (b->useshelles) { + // The number of bytes occupied by a subface. The list of pointers + // stored in a subface are: three to other subfaces, three to corners, + // three to subsegments, two to tetrahedra, and one to a badface. + shsize = 12 * sizeof(shellface); + // The index within each subface at which the maximum area bound is + // found, where the index is measured in REALs. + areaboundindex = (shsize + sizeof(REAL) - 1) / sizeof(REAL); + // If -q switch is in use, increase the number of bytes occupied by + // a subface for saving maximum area bound. + if (b->quality && varconstraint) { + shsize = (areaboundindex + 1) * sizeof(REAL); + } else { + shsize = areaboundindex * sizeof(REAL); + } + // The index within subface at which the facet marker is found. Ensure + // the marker is aligned to a sizeof(int)-byte address. + 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(int); + // 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, SUBPERBLOCK, POINTER, 8); + // Initialize the pool of subsegments. The subsegment's record is same + // with subface. + subsegs = new memorypool(shsize, SUBPERBLOCK, POINTER, 8); + // Initialize the "outer space" tetrahedron and omnipresent subface. + dummyinit(tetrahedrons->itemwords, subfaces->itemwords); + } else { + // Initialize the "outer space" tetrahedron. + dummyinit(tetrahedrons->itemwords, 0); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// tetrahedrondealloc() Deallocate space for a tet., marking it dead. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::tetrahedrondealloc(tetrahedron *dyingtetrahedron) +{ + // Set tetrahedron's vertices to NULL. This makes it possible to detect + // dead tetrahedra when traversing the list of all tetrahedra. + dyingtetrahedron[4] = (tetrahedron) NULL; + dyingtetrahedron[5] = (tetrahedron) NULL; + dyingtetrahedron[6] = (tetrahedron) NULL; + dyingtetrahedron[7] = (tetrahedron) NULL; + tetrahedrons->dealloc((void *) dyingtetrahedron); +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// tetrahedrontraverse() Traverse the tetrahedra, skipping dead ones. // +// // +/////////////////////////////////////////////////////////////////////////////// + +tetgenmesh::tetrahedron* tetgenmesh::tetrahedrontraverse() +{ + tetrahedron *newtetrahedron; + + do { + newtetrahedron = (tetrahedron *) tetrahedrons->traverse(); + if (newtetrahedron == (tetrahedron *) NULL) { + return (tetrahedron *) NULL; + } + } while (newtetrahedron[7] == (tetrahedron) NULL); // Skip dead ones. + return newtetrahedron; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// shellfacedealloc() Deallocate space for a shellface, marking it dead. // +// Used both for dealloc a subface and subsegment. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::shellfacedealloc(memorypool *pool, shellface *dyingsh) +{ + // Set shellface's vertices to NULL. This makes it possible to detect dead + // shellfaces when traversing the list of all shellfaces. + dyingsh[3] = (shellface) NULL; + dyingsh[4] = (shellface) NULL; + dyingsh[5] = (shellface) NULL; + pool->dealloc((void *) dyingsh); +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// shellfacetraverse() Traverse the subfaces, skipping dead ones. Used // +// for both subfaces and subsegments pool traverse. // +// // +/////////////////////////////////////////////////////////////////////////////// + +tetgenmesh::shellface* tetgenmesh::shellfacetraverse(memorypool *pool) +{ + shellface *newshellface; + + do { + newshellface = (shellface *) pool->traverse(); + if (newshellface == (shellface *) NULL) { + return (shellface *) NULL; + } + } while (newshellface[3] == (shellface) NULL); // Skip dead ones. + 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; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// pointdealloc() Deallocate space for a point, marking it dead. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::pointdealloc(point dyingpoint) +{ + // Mark the point as dead. This makes it possible to detect dead points + // when traversing the list of all points. + setpointtype(dyingpoint, DEADVERTEX); + points->dealloc((void *) dyingpoint); +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// pointtraverse() Traverse the points, skipping dead ones. // +// // +/////////////////////////////////////////////////////////////////////////////// + +tetgenmesh::point tetgenmesh::pointtraverse() +{ + point newpoint; + + do { + newpoint = (point) points->traverse(); + if (newpoint == (point) NULL) { + return (point) NULL; + } + } while (pointtype(newpoint) == DEADVERTEX); // Skip dead ones. + return newpoint; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// maketetrahedron() Create a new tetrahedron. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::maketetrahedron(triface *newtet) +{ + newtet->tet = (tetrahedron *) tetrahedrons->alloc(); + // Initialize the four adjoining tetrahedra to be "outer space". + newtet->tet[0] = (tetrahedron) dummytet; + newtet->tet[1] = (tetrahedron) dummytet; + newtet->tet[2] = (tetrahedron) dummytet; + newtet->tet[3] = (tetrahedron) dummytet; + // Four NULL vertices. + newtet->tet[4] = (tetrahedron) NULL; + newtet->tet[5] = (tetrahedron) NULL; + newtet->tet[6] = (tetrahedron) NULL; + newtet->tet[7] = (tetrahedron) NULL; + // Initialize the four adjoining subfaces to be the omnipresent subface. + if (b->useshelles) { + newtet->tet[8 ] = (tetrahedron) dummysh; + newtet->tet[9 ] = (tetrahedron) dummysh; + newtet->tet[10] = (tetrahedron) dummysh; + newtet->tet[11] = (tetrahedron) dummysh; + newtet->tet[12] = (tetrahedron) dummysh; + newtet->tet[13] = (tetrahedron) dummysh; + } + for (int i = 0; i < in->numberoftetrahedronattributes; i++) { + setelemattribute(newtet->tet, i, 0.0); + } + if (b->varvolume) { + setvolumebound(newtet->tet, -1.0); + } + // Initialize the location and version to be Zero. + newtet->loc = 0; + newtet->ver = 0; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// makeshellface() Create a new shellface with version zero. Used for // +// both subfaces and seusegments. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::makeshellface(memorypool *pool, face *newface) +{ + newface->sh = (shellface *) pool->alloc(); + //Initialize the three adjoining subfaces to be the omnipresent subface. + newface->sh[0] = (shellface) dummysh; + newface->sh[1] = (shellface) dummysh; + newface->sh[2] = (shellface) dummysh; + // Three NULL vertices. + newface->sh[3] = (shellface) NULL; + newface->sh[4] = (shellface) NULL; + newface->sh[5] = (shellface) NULL; + // Initialize the two adjoining tetrahedra to be "outer space". + newface->sh[6] = (shellface) dummytet; + newface->sh[7] = (shellface) dummytet; + // Initialize the three adjoining subsegments to be the omnipresent + // subsegments. + newface->sh [8] = (shellface) dummysh; + newface->sh [9] = (shellface) dummysh; + newface->sh[10] = (shellface) dummysh; + // Initialize the pointer to badface structure. + newface->sh[11] = (shellface) NULL; + if (b->quality && varconstraint) { + // Initialize the maximum area bound. + setareabound(*newface, 0.0); + } + // Set the boundary marker to zero. + setshellmark(*newface, 0); + // Set the type. + setshelltype(*newface, NSHARP); + if (checkpbcs) { + // Set the pbcgroup be ivalid. + setshellpbcgroup(*newface, -1); + } + // Initialize the version to be Zero. + newface->shver = 0; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// makepoint() Create a new point. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::makepoint(point* pnewpoint) +{ + int ptmark, i; + + *pnewpoint = (point) points->alloc(); + // Initialize three coordinates. + (*pnewpoint)[0] = 0.0; + (*pnewpoint)[1] = 0.0; + (*pnewpoint)[2] = 0.0; + // Initialize the list of user-defined attributes. + for (i = 0; i < in->numberofpointattributes; i++) { + (*pnewpoint)[3 + i] = 0.0; + } + // Initialize the metric tensor. + for (i = 0; i < sizeoftensor; i++) { + (*pnewpoint)[pointmtrindex + i] = 0.0; + } + if (b->plc || b->refine) { + // Initialize the point-to-simplex filed. + setpoint2tet(*pnewpoint, NULL); + setpoint2sh(*pnewpoint, NULL); + setpoint2ppt(*pnewpoint, NULL); + if (b->metric) { + setpoint2bgmtet(*pnewpoint, NULL); + } + if (checkpbcs) { + // Initialize the other pointer to its pbc point. + setpoint2pbcpt(*pnewpoint, NULL); + } + } + // Initialize the point marker (starting from in->firstnumber). + ptmark = (int) points->items - (in->firstnumber == 1 ? 0 : 1); + setpointmark(*pnewpoint, ptmark); + // Initialize the point type. + setpointtype(*pnewpoint, UNUSEDVERTEX); +} + +// +// End of memory management routines +// + +// +// Begin of point location routines +// + +/////////////////////////////////////////////////////////////////////////////// +// // +// randomnation() Generate a random number between 0 and 'choices' - 1. // +// // +/////////////////////////////////////////////////////////////////////////////// + +unsigned long tetgenmesh::randomnation(unsigned int choices) +{ + unsigned long newrandom; + + if (choices >= 714025l) { + newrandom = (randomseed * 1366l + 150889l) % 714025l; + randomseed = (newrandom * 1366l + 150889l) % 714025l; + newrandom = newrandom * (choices / 714025l) + randomseed; + if (newrandom >= choices) { + return newrandom - choices; + } else { + return newrandom; + } + } else { + randomseed = (randomseed * 1366l + 150889l) % 714025l; + return randomseed % choices; + } + // Old function. + // randomseed = (randomseed * 1366l + 150889l) % 714025l; + // return randomseed / (714025l / choices + 1); +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// distance2() Returns the square "distance" of a tetrahedron to point p. // +// // +/////////////////////////////////////////////////////////////////////////////// + +REAL tetgenmesh::distance2(tetrahedron* tetptr, point p) +{ + point p1, p2, p3, p4; + REAL dx, dy, dz; + + p1 = (point) tetptr[4]; + p2 = (point) tetptr[5]; + p3 = (point) tetptr[6]; + p4 = (point) tetptr[7]; + + dx = p[0] - 0.25 * (p1[0] + p2[0] + p3[0] + p4[0]); + dy = p[1] - 0.25 * (p1[1] + p2[1] + p3[1] + p4[1]); + dz = p[2] - 0.25 * (p1[2] + p2[2] + p3[2] + p4[2]); + + return dx * dx + dy * dy + dz * dz; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// preciselocate() Find a simplex 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. // +// // +// On completion, 'searchtet' is a tetrahedron that contains 'searchpt'. The // +// returned value indicates one of the following cases: // +// - Returns ONVERTEX if the point lies on an existing vertex. 'searchtet' // +// is a handle whose origin is the existing vertex. // +// - Returns ONEDGE if the point lies on a mesh edge. 'searchtet' is a // +// handle whose primary edge is the edge on which the point lies. // +// - Returns ONFACE if the point lies strictly within a face. 'searchtet' // +// is a handle whose primary face is the face on which the point lies. // +// - Returns INTETRAHEDRON if the point lies strictly in a tetrahededron. // +// 'searchtet' is a handle on the tetrahedron that contains the point. // +// - Returns OUTSIDE if the point lies outside the mesh. 'searchtet' is a // +// handle whose location is the face the point is to 'above' of. // +// // +// WARNING: This routine is designed for convex triangulations, and will not // +// generally work after the holes and concavities have been carved. // +// // +// If 'maxtetnumber' > 0, stop the searching process if the number of passed // +// tets is larger than it and return OUTSIDE. // +// // +/////////////////////////////////////////////////////////////////////////////// + +enum tetgenmesh::locateresult tetgenmesh::preciselocate(point searchpt, + triface* searchtet, long maxtetnumber) +{ + triface backtracetet; + triface walkthroface; + point forg, fdest, fapex, toppo; + REAL ori1, ori2, ori3, ori4; + long tetnumber; + int side; + + if (isdead(searchtet)) searchtet->tet = dummytet; + if (searchtet->tet == dummytet) { + searchtet->loc = 0; + symself(*searchtet); + } + // 'searchtet' should be a valid tetrahedron now. +#ifdef SELF_CHECK + // assert(!isdead(searchtet) && (searchtet->tet != dummytet)); +#endif + if (isdead(searchtet)) { + printf("Warning: Point location failed.\n"); + return OUTSIDE; + } + + searchtet->ver = 0; // Keep in CCW edge ring. + // Find a face of 'searchtet' such that the 'searchpt' lies strictly + // above it. Such face should always exist. + for (searchtet->loc = 0; searchtet->loc < 4; searchtet->loc++) { + forg = org(*searchtet); + fdest = dest(*searchtet); + fapex = apex(*searchtet); + ori1 = orient3d(forg, fdest, fapex, searchpt); + if (ori1 < 0.0) break; + } +#ifdef SELF_CHECK + assert(searchtet->loc < 4); +#endif + + // Define 'tetnumber' for exit the loop when it's running endless. + tetnumber = 0l; + while ((maxtetnumber > 0l) && (tetnumber <= maxtetnumber)) { + // Check if we are reaching the boundary of the triangulation. + if (searchtet->tet == dummytet) { + *searchtet = backtracetet; + return OUTSIDE; + } + // Initialize the face for returning the walk-through face. + walkthroface.tet = (tetrahedron *) NULL; + // Adjust the edge ring, so that 'ori1 < 0.0' holds. + searchtet->ver = 0; + // 'toppo' remains unchange for the following orientation tests. + toppo = oppo(*searchtet); + // Check the three sides of 'searchtet' to find the face through which + // we can walk next. + for (side = 0; side < 3; side++) { + forg = org(*searchtet); + fdest = dest(*searchtet); + ori2 = orient3d(forg, fdest, toppo, searchpt); + if (ori2 == 0.0) { + // They are coplanar, check if 'searchpt' lies inside, or on an edge, + // or coindice with a vertex of face (forg, fdest, toppo). + fapex = apex(*searchtet); + ori3 = orient3d(fdest, fapex, toppo, searchpt); + if (ori3 < 0.0) { + // Outside the face (fdest, fapex, toppo), walk through it. + enextself(*searchtet); + fnext(*searchtet, walkthroface); + break; + } + ori4 = orient3d(fapex, forg, toppo, searchpt); + if (ori4 < 0.0) { + // Outside the face (fapex, forg, toppo), walk through it. + enext2self(*searchtet); + fnext(*searchtet, walkthroface); + break; + } + // Remember, ori1 < 0.0, which means 'searchpt' will not on edge + // (forg, fdest) or on vertex forg or fdest. +#ifdef SELF_CHECK + assert(ori1 < 0.0); +#endif + // The rest possible cases are: + // (1) 'searchpt' lies on edge (fdest, toppo); + // (2) 'searchpt' lies on edge (toppo, forg); + // (3) 'searchpt' coincident with toppo; + // (4) 'searchpt' lies inside face (forg, fdest, toppo). + fnextself(*searchtet); + if (ori3 == 0.0) { + if (ori4 == 0.0) { + // Case (4). + enext2self(*searchtet); + return ONVERTEX; + } else { + // Case (1). + enextself(*searchtet); + return ONEDGE; + } + } + if (ori4 == 0.0) { + // Case (2). + enext2self(*searchtet); + return ONEDGE; + } + // Case (4). + return ONFACE; + } else if (ori2 < 0.0) { + // Outside the face (forg, fdest, toppo), walk through it. + fnext(*searchtet, walkthroface); + break; + } + // Go to check next side. + enextself(*searchtet); + } + if (side >= 3) { + // Found! Inside tetrahedron. + return INTETRAHEDRON; + } + // We walk through the face 'walkthroface' and continue the searching. +#ifdef SELF_CHECK + assert(walkthroface.tet != (tetrahedron *) NULL); +#endif + // Store the face handle in 'backtracetet' before we take the real walk. + // So we are able to restore the handle to 'searchtet' if we are + // reaching the outer boundary. + backtracetet = walkthroface; + sym(walkthroface, *searchtet); + tetnumber++; + } + + // Should never be here. + // printf("Internal error in preciselocate(): Point location failed.\n"); + // internalerror(); + return OUTSIDE; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// locate() Find a simplex containing a given point. // +// // +// 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 barycenter is closest to the point we are searcing for. Having chosen // +// the starting tetrahedron, the simple Walk-through algorithm is used to do // +// the real walking. // +// // +// The return value indicates the location of the 'searchpt' (INTETRAHEDRON, // +// or ONFACE, ...). 'searchtet' is adjusted to a tetrahedron corresponding // +// to that value. See the introduction part of preciselocate() for detail. // +// // +// WARNING: This routine is designed for convex triangulations, and will not // +// generally work after the holes and concavities have been carved. // +// // +/////////////////////////////////////////////////////////////////////////////// + +enum tetgenmesh::locateresult tetgenmesh::locate(point searchpt, + triface *searchtet) +{ + tetrahedron *firsttet, *tetptr; + void **sampleblock; + long sampleblocks, samplesperblock, samplenum; + long tetblocks, i, j; + unsigned long alignptr; + REAL searchdist, dist; + + // 'searchtet' should be a valid tetrahedron. + if (isdead(searchtet)) { + searchtet->tet = dummytet; + } + if (searchtet->tet == dummytet) { + // This is an 'Outer Space' handle, get a hull tetrahedron. + searchtet->loc = 0; + symself(*searchtet); + } +#ifdef SELF_CHECK + // assert(!isdead(searchtet)); +#endif + if (isdead(searchtet)) { + printf("Warning: Point location failed.\n"); + return OUTSIDE; + } + + // Get the distance from the suggested starting tet to the point we seek. + searchdist = distance2(searchtet->tet, searchpt); + + // If a recently encountered tetrahedron has been recorded and has not + // been deallocated, test it as a good starting point. + if (!isdead(&recenttet) && (recenttet.tet != searchtet->tet)) { + dist = distance2(recenttet.tet, searchpt); + if (dist < searchdist) { + *searchtet = recenttet; + searchdist = dist; + } + } + + // Select "good" candidate using k random samples, taking the closest one. + // The number of random samples taken is proportional to the fourth root + // of the number of tetrahedra in the mesh. The next bit of code assumes + // that the number of tetrahedra increases monotonically. + while (SAMPLEFACTOR * samples * samples * samples * samples < + tetrahedrons->items) { + samples++; + } + // Find how much blocks in current tet pool. + tetblocks = (tetrahedrons->maxitems + ELEPERBLOCK - 1) / ELEPERBLOCK; + // Find the average samles per block. Each block at least have 1 sample. + samplesperblock = 1 + (samples / tetblocks); + sampleblocks = samples / samplesperblock; + sampleblock = tetrahedrons->firstblock; + for (i = 0; i < sampleblocks; i++) { + alignptr = (unsigned long) (sampleblock + 1); + firsttet = (tetrahedron *) + (alignptr + (unsigned long) tetrahedrons->alignbytes + - (alignptr % (unsigned long) tetrahedrons->alignbytes)); + for (j = 0; j < samplesperblock; j++) { + if (i == tetblocks - 1) { + // This is the last block. + samplenum = randomnation((int) + (tetrahedrons->maxitems - (i * ELEPERBLOCK))); + } else { + samplenum = randomnation(ELEPERBLOCK); + } + tetptr = (tetrahedron *) + (firsttet + (samplenum * tetrahedrons->itemwords)); + if (tetptr[4] != (tetrahedron) NULL) { + dist = distance2(tetptr, searchpt); + if (dist < searchdist) { + searchtet->tet = tetptr; + searchdist = dist; + } + } + } + sampleblock = (void **) *sampleblock; + } + + // Call simple walk-through to locate the point. + return preciselocate(searchpt, searchtet, tetrahedrons->items); +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// adjustlocate() Adjust the precise location of a vertex. // +// // +// 'precise' is the value returned from preciselocate(). It indicates the // +// exact location of the point 'searchpt' with respect to the tetrahedron // +// 'searchtet'. 'epspp' is a given relative tolerance. // +// // +// This routine re-evaluates the orientations of searchpt with respect to // +// the four sides of searchtet. Detects the coplanarities by additinal tests // +// which are based on the given tolerance. If 'precise' is ONFACE or ONEDGE, // +// we can save one or two orientation tests. // +// // +// The return value indicates the location of the 'searchpt' (INTETRAHEDRON, // +// or ONFACE, ...). 'searchtet' is adjusted to a tetrahedron corresponding // +// to that value. See the introduction part of preciselocate() for detail. // +// // +// WARNING: This routine detect degenerate case using relative tolerance. // +// It is better used after locate() or preciselocate(). For general inputs, // +// it may not able to tell the correct location. // +// // +/////////////////////////////////////////////////////////////////////////////// + +enum tetgenmesh::locateresult tetgenmesh::adjustlocate(point searchpt, + triface* searchtet, enum locateresult precise, REAL epspp) +{ + point torg, tdest, tapex, toppo; + REAL s1, s2, s3, s4; + + // For the given 'searchtet', the orientations tests are: + // s1: (tdest, torg, tapex, searchpt); + // s2: (torg, tdest, toppo, searchpt); + // s3: (tdest, tapex, toppo, searchpt); + // s4: (tapex, torg, toppo, searchpt); + adjustedgering(*searchtet, CCW); + torg = org(*searchtet); + tdest = dest(*searchtet); + tapex = apex(*searchtet); + toppo = oppo(*searchtet); + + switch (precise) { + case ONVERTEX: + // This case we don't need do any further test. + return ONVERTEX; + case ONEDGE: + // (torg, tdest); + s1 = 0.0; + s2 = 0.0; + break; + case ONFACE: + // (tdest, torg, tapex); + s1 = 0.0; + s2 = orient3d(torg, tdest, toppo, searchpt); + break; + default: // INTETRAHEDRON or OUTSIDE + s1 = orient3d(tdest, torg, tapex, searchpt); + s2 = orient3d(torg, tdest, toppo, searchpt); + } + + if (s1 != 0.0) { + if (iscoplanar(tdest, torg, tapex, searchpt, s1, epspp)) { + s1 = 0.0; + } + } + if (s1 < 0.0) { + return OUTSIDE; + } + + if (s2 != 0.0) { + if (iscoplanar(torg, tdest, toppo, searchpt, s2, epspp)) { + s2 = 0.0; + } + } + if (s2 < 0.0) { + fnextself(*searchtet); + return OUTSIDE; + } + + s3 = orient3d(tdest, tapex, toppo, searchpt); + if (s3 != 0.0) { + if (iscoplanar(tdest, tapex, toppo, searchpt, s3, epspp)) { + s3 = 0.0; + } + } + if (s3 < 0.0) { + enextfnextself(*searchtet); + return OUTSIDE; + } + + s4 = orient3d(tapex, torg, toppo, searchpt); + if (s4 != 0.0) { + if (iscoplanar(tapex, torg, toppo, searchpt, s4, epspp)) { + s4 = 0.0; + } + } + if (s4 < 0.0) { + enext2fnextself(*searchtet); + return OUTSIDE; + } + + // Determine degenerate cases. + if (s1 == 0.0) { + if (s2 == 0.0) { + if (s3 == 0.0) { + // On tdest. + enextself(*searchtet); + return ONVERTEX; + } + if (s4 == 0.0) { + // On torg. + return ONVERTEX; + } + // On edge (torg, tdest). + return ONEDGE; + } + if (s3 == 0.0) { + if (s4 == 0.0) { + // On tapex. + enext2self(*searchtet); + return ONVERTEX; + } + // On edge (tdest, tapex). + enextself(*searchtet); + return ONEDGE; + } + if (s4 == 0.0) { + // On edge (tapex, torg). + enext2self(*searchtet); + return ONEDGE; + } + // On face (torg, tdest, tapex). + return ONFACE; + } + if (s2 == 0.0) { + fnextself(*searchtet); + if (s3 == 0.0) { + if (s4 == 0.0) { + // On toppo. + enext2self(*searchtet); + return ONVERTEX; + } + // On edge (tdest, toppo). + enextself(*searchtet); + return ONEDGE; + } + if (s4 == 0.0) { + // On edge (toppo, torg). + enext2self(*searchtet); + return ONEDGE; + } + // On face (torg, tdest, toppo). + return ONFACE; + } + if (s3 == 0.0) { + enextfnextself(*searchtet); + if (s4 == 0.0) { + // On edge (tapex, toppo). + enextself(*searchtet); + return ONEDGE; + } + // On face (tdest, tapex, toppo). + return ONFACE; + } + if (s4 == 0.0) { + enext2fnextself(*searchtet); + // On face (tapex, torg, toppo). + return ONFACE; + } + + // Inside tetrahedron. + return INTETRAHEDRON; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// hullwalk() Find a tetrahedron on the hull to continue search. // +// // +/////////////////////////////////////////////////////////////////////////////// + +enum tetgenmesh::locateresult tetgenmesh::hullwalk(point searchpt, + triface *hulltet) +{ + list* travtetlist; + triface travtet, neightet; + point pa, pb, pc; + enum locateresult loc; + REAL ori; + int i; + + travtetlist = new list(sizeof(triface), NULL, 256); + travtet = *hulltet; + infect(travtet); + travtetlist->append(&travtet); + + loc = OUTSIDE; + for (i = 0; i < travtetlist->len(); i++) { + travtet = * (triface *)(* travtetlist)[i]; + // Choose the CCW-edgering in face. + travtet.ver = 0; + // Look for a side where pt lies below it. + for (travtet.loc = 0; travtet.loc < 4; travtet.loc++) { + pa = org(travtet); + pb = dest(travtet); + pc = apex(travtet); + ori = orient3d(pa, pb, pc, searchpt); + if (ori > 0.0) break; + } + // Is pt above all (or coplanar with some of) the four sides? + if (travtet.loc == 4) { + hulltet->tet = travtet.tet; + loc = adjustlocate(searchpt, hulltet, INTETRAHEDRON, b->epsilon); + assert(loc != OUTSIDE); + } else { // ori > 0.0 + // pt is below (behind) this side. We want to walk through it. + sym(travtet, neightet); + if (neightet.tet == dummytet) { + // This is a hull side. Is p approximately on this side. + loc = adjustlocate(searchpt, &travtet, OUTSIDE, b->epsilon); + } + if (loc == OUTSIDE) { + // Let's collect all the neighbors for next searching. + for (travtet.loc = 0; travtet.loc < 4; travtet.loc++) { + sym(travtet, neightet); + if ((neightet.tet != dummytet) && !infected(neightet)) { + // Neighbor exists and not visited. + infect(neightet); + travtetlist->append(&neightet); + } + } // for (travtet.loc = 0; + } // if (loc == OUTSIDE) + } // if (travtet.loc == 4) + if (loc != OUTSIDE) break; + } // for (i = 0; i < travtetlist->len(); i++) + + // Uninfect traversed tets. + for (i = 0; i < travtetlist->len(); i++) { + travtet = * (triface *)(* travtetlist)[i]; + uninfect(travtet); + } + + delete travtetlist; + return loc; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// locatesub() Find a point in the surface mesh of a facet. // +// // +// Searching begins from the input 'searchsh', it should be a handle on the // +// convex hull of the facet triangulation. // +// // +// If 'stopatseg' is nonzero, the search will stop if it tries to walk // +// through a subsegment, and will return OUTSIDE. // +// // +// On completion, 'searchsh' is a subface that contains 'searchpt'. // +// - Returns ONVERTEX if the point lies on an existing vertex. 'searchsh' // +// is a handle whose origin is the existing vertex. // +// - Returns ONEDGE if the point lies on a mesh edge. 'searchsh' is a // +// handle whose primary edge is the edge on which the point lies. // +// - Returns ONFACE if the point lies strictly within a subface. // +// 'searchsh' is a handle on which the point lies. // +// - Returns OUTSIDE if the point lies outside the triangulation. // +// // +// WARNING: This routine is designed for convex triangulations, and will not // +// not generally work after the holes and concavities have been carved. // +// // +/////////////////////////////////////////////////////////////////////////////// + +enum tetgenmesh::locateresult tetgenmesh::locatesub(point searchpt, + face* searchsh, int stopatseg, REAL epspp) +{ + face backtracksh, spinsh, checkedge; + point forg, fdest, fapex; + REAL orgori, destori; + REAL ori, sign; + int moveleft, i; + + if (searchsh->sh == dummysh) { + searchsh->shver = 0; + spivotself(*searchsh); +#ifdef SELF_CHECK + assert(searchsh->sh != dummysh); +#endif + } + // Find the sign to simulate that abovepoint is 'above' the facet. + adjustedgering(*searchsh, CCW); + forg = sorg(*searchsh); + fdest = sdest(*searchsh); + fapex = sapex(*searchsh); + ori = orient3d(forg, fdest, fapex, abovepoint); + sign = ori > 0.0 ? -1 : 1; + + // Orient 'searchsh' so that 'searchpt' is below it (i.e., searchpt has + // CCW orientation with respect to searchsh in plane). Such edge + // should always exist. Save it as (forg, fdest). + for (i = 0; i < 3; i++) { + forg = sorg(*searchsh); + fdest = sdest(*searchsh); + ori = orient3d(forg, fdest, abovepoint, searchpt) * sign; + if (ori > 0.0) break; + senextself(*searchsh); + } +#ifdef SELF_CHECK + assert(i < 3); +#endif + + while (1) { + fapex = sapex(*searchsh); + // Check whether the apex is the point we seek. + if (fapex[0] == searchpt[0] && fapex[1] == searchpt[1] && + fapex[2] == searchpt[2]) { + senext2self(*searchsh); + return ONVERTEX; + } + // Does the point lie on the other side of the line defined by the + // triangle edge opposite the triangle's destination? + destori = orient3d(forg, fapex, abovepoint, searchpt) * sign; + if (epspp > 0.0) { + if (iscoplanar(forg, fapex, abovepoint, searchpt, destori, epspp)) { + destori = 0.0; + } + } + // Does the point lie on the other side of the line defined by the + // triangle edge opposite the triangle's origin? + orgori = orient3d(fapex, fdest, abovepoint, searchpt) * sign; + if (epspp > 0.0) { + if (iscoplanar(fapex, fdest, abovepoint, searchpt, orgori, epspp)) { + orgori = 0.0; + } + } + if (destori > 0.0) { + moveleft = 1; + } else { + if (orgori > 0.0) { + moveleft = 0; + } else { + // The point must be on the boundary of or inside this triangle. + if (destori == 0.0) { + senext2self(*searchsh); + return ONEDGE; + } + if (orgori == 0.0) { + senextself(*searchsh); + return ONEDGE; + } + return ONFACE; + } + } + // Move to another triangle. Leave a trace `backtracksh' in case + // walking off a boundary of the triangulation. + if (moveleft) { + senext2(*searchsh, backtracksh); + fdest = fapex; + } else { + senext(*searchsh, backtracksh); + forg = fapex; + } + // Check if we meet a segment. + sspivot(backtracksh, checkedge); + if (checkedge.sh != dummysh) { + if (stopatseg) { + // The flag indicates we should not cross a segment. Stop. + *searchsh = backtracksh; + return OUTSIDE; + } + // Try to walk through a segment. We need to find a coplanar subface + // sharing this segment to get into. + spinsh = backtracksh; + do { + spivotself(spinsh); + if (spinsh.sh == backtracksh.sh) { + // Turn back, no coplanar subface is found. + break; + } + // Are they belong to the same facet. + if (shellmark(spinsh) == shellmark(backtracksh)) { + // Find a coplanar subface. Walk into it. + *searchsh = spinsh; + break; + } + // Are they (nearly) coplanar? + ori = orient3d(forg, fdest, sapex(backtracksh), sapex(spinsh)); + if (iscoplanar(forg, fdest, sapex(backtracksh), sapex(spinsh), ori, + b->epsilon)) { + // Find a coplanar subface. Walk into it. + *searchsh = spinsh; + break; + } + } while (spinsh.sh != backtracksh.sh); + } else { + spivot(backtracksh, *searchsh); + } + // Check for walking right out of the triangulation. + if ((searchsh->sh == dummysh) || (searchsh->sh == backtracksh.sh)) { + // Go back to the last triangle. + *searchsh = backtracksh; + return OUTSIDE; + } + // To keep the same orientation wrt abovepoint. + if (sorg(*searchsh) != forg) sesymself(*searchsh); +#ifdef SELF_CHECK + assert((sorg(*searchsh) == forg) && (sdest(*searchsh) == fdest)); +#endif + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// adjustlocatesub() Adjust the precise location of a vertex. // +// // +// 'precise' is the precise location (returned from locatesub()) of 'searcht'// +// with respect to 'searchsh'. 'epspp' is the given relative tolerance. // +// // +// This routine re-evaluates the orientations of 'searchpt' with respect to // +// the three edges of 'searchsh'. Detects the collinearities by additinal // +// tests based on the given tolerance. If 'precise' is ONEDGE, one can save // +// one orientation test for the current edge of 'searchsh'. // +// // +// On completion, 'searchsh' is a subface contains 'searchpt'. The returned // +// value indicates one of the following cases: // +// - Returns ONVERTEX if the point lies on an existing vertex. 'searchsh' // +// is a handle whose origin is the existing vertex. // +// - Returns ONEDGE if the point lies on a mesh edge. 'searchsh' is a // +// handle whose primary edge is the edge on which the point lies. // +// - Returns ONFACE if the point lies strictly within a subface. // +// 'searchsh' is a handle on which the point lies. // +// - Returns OUTSIDE if the point lies outside 'searchsh'. // +// // +/////////////////////////////////////////////////////////////////////////////// + +enum tetgenmesh::locateresult tetgenmesh:: +adjustlocatesub(point searchpt, face* searchsh, enum locateresult precise, + REAL epspp) +{ + point pa, pb, pc; + bool s1, s2, s3; + + pa = sorg(*searchsh); + pb = sdest(*searchsh); + pc = sapex(*searchsh); + + if (precise == ONEDGE) { + s1 = true; + } else { + s1 = iscollinear(pa, pb, searchpt, epspp); + } + s2 = iscollinear(pb, pc, searchpt, epspp); + s3 = iscollinear(pc, pa, searchpt, epspp); + if (s1) { + if (s2) { + // on vertex pb. +#ifdef SELF_CHECK + assert(!s3); +#endif + senextself(*searchsh); + return ONVERTEX; + } else if (s3) { + // on vertex pa. + return ONVERTEX; + } else { + // on edge pa->pb. + return ONEDGE; + } + } else if (s2) { + if (s3) { + // on vertex pc. + senext2self(*searchsh); + return ONVERTEX; + } else { + // on edge pb->pc. + senextself(*searchsh); + return ONEDGE; + } + } else if (s3) { + // on edge pc->pa. + senext2self(*searchsh); + return ONEDGE; + } else { + return precise; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// locateseg() Find a point in subsegments. // +// // +// Searching begins from the input 'searchseg', it should be a subsegment of // +// the whole segment. // +// // +// On completion, 'searchseg' is a subsegment that contains 'searchpt'. // +// - Returns ONVERTEX if the point lies on an existing vertex. 'searchseg' // +// is a handle whose origin is the existing vertex. // +// - Returns ONEDGE if the point lies inside 'searchseg'. // +// - Returns OUTSIDE if the point lies outside the segment. // +// // +/////////////////////////////////////////////////////////////////////////////// + +enum tetgenmesh::locateresult tetgenmesh:: +locateseg(point searchpt, face* searchseg) +{ + face backtraceseg; + point pa, pb; + REAL dx, dy, dz; + int moveleft; + int i; + + moveleft = 0; + while (1) { + searchseg->shver = 0; + pa = sorg(*searchseg); + pb = sdest(*searchseg); + // Find the biggest difference in x, y, and z coordinates of a and b. + dx = fabs(pb[0] - pa[0]); + dy = fabs(pb[1] - pa[1]); + dz = fabs(pb[2] - pa[2]); + if (dx > dy) { + if (dx > dz) { + i = 0; + } else { + i = 2; + } + } else { + if (dy > dz) { + i = 1; + } else { + i = 2; + } + } + if (pa[i] < pb[i]) { + if (searchpt[i] < pa[i]) { + moveleft = 1; + } else if (searchpt[i] > pa[i]) { + if (searchpt[i] < pb[i]) { + return ONEDGE; + } else if (searchpt[i] > pb[i]) { + moveleft = 0; + } else { +#ifdef SELF_CHECK + assert(searchpt[i] == pb[i]); +#endif + sesymself(*searchseg); + return ONVERTEX; + } + } else { +#ifdef SELF_CHECK + assert(searchpt[i] == pa[i]); +#endif + return ONVERTEX; + } + } else if (pa[i] > pb[i]) { + if (searchpt[i] < pb[i]) { + moveleft = 0; + } else if (searchpt[i] > pb[i]) { + if (searchpt[i] < pa[i]) { + return ONEDGE; + } else if (searchpt[i] > pa[i]) { + moveleft = 1; + } else { +#ifdef SELF_CHECK + assert(searchpt[i] == pa[i]); +#endif + return ONVERTEX; + } + } else { +#ifdef SELF_CHECK + assert(searchpt[i] == pb[i]); +#endif + sesymself(*searchseg); + return ONVERTEX; + } + } + backtraceseg = *searchseg; + if (moveleft) { + senext2self(*searchseg); + } else { + senextself(*searchseg); + } + spivotself(*searchseg); + if (searchseg->sh == dummysh) { + *searchseg = backtraceseg; + break; + } + } + + return OUTSIDE; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// adjustlocateseg() Adjust the precise location of a vertex on segment. // +// // +// 'searchpt' is either inside or ouside the segment 'searchseg'. It will be // +// adjusted to on vertex if it is very close to an endpoint of 'searchseg'. // +// 'epspp' is the given relative tolerance. // +// // +/////////////////////////////////////////////////////////////////////////////// + +enum tetgenmesh::locateresult tetgenmesh:: +adjustlocateseg(point searchpt, face* searchseg, enum locateresult precise, + REAL epspp) +{ + point pa, pb; + REAL L, d, r; + + pa = sorg(*searchseg); + pb = sdest(*searchseg); + L = distance(pa, pb); + + // Is searchpt approximate to pa? + d = distance(pa, searchpt); + r = d / L; + if (r <= epspp) { + return ONVERTEX; + } + // Is searchpt approximate to pb? + d = distance(pb, searchpt); + r = d / L; + if (r <= epspp) { + sesymself(*searchseg); + return ONVERTEX; + } + + return precise; +} + +// +// End of point location routines +// + +// +// Begin of mesh transformation routines +// + +/////////////////////////////////////////////////////////////////////////////// +// // +// Flip operations // +// // +// If abc is a hull face, it is unflipable, and is locally Delaunay. In the // +// following, we assume abc is an interior face, and the other tetrahedron // +// adjoining at abc is bace. // +// // +// If the convex hull CH of the set {a, b, c, d, e} only has four vertices, // +// i.e., one vertex lies inside CH, then abc is unflipable, and is locally // +// Delaunay. If CH is the vertex set itself, we have the following cases to // +// determine whether abc is flipable or not. // +// // +// If no four points of {a, b, c, d, e} are coplanar, a 2-to-3 flip can be // +// applied to abc if the edge de crosses the triangle abc; a 3-to-2 flip can // +// be applied to abc if ab crosses cde, and abde exists, otherwise, face abc // +// is unflipable, i.e., the tetrahedron abde is not present. // +// // +// If four points of {a, b, c, d, e} are coplanar (two faces are coplanar). // +// Assume faces abd and abe are coplanar (it is impossible be abc). If a, b, // +// d, e form a non-convex quadrilateral, then abc is unflipable, furthermore,// +// it is locally Delaunay. Assume they are convex quadrilateral, if abd and // +// abe are hull faces, a 2-to-2 flip can be applied to abc; if abd and abe // +// are interior faces, assume two tetrahedra adjoining abd and abe at the // +// opposite sides are abdg and abef, respectively. If g = f, a 4-to-4 flip // +// can be applied to abc, otherwise, abc is unflipable. // +// // +// There are other cases which can cause abc unflipable. If abc is a subface,// +// a 2-to-3 flip is forbidden; if ab is a subsegment, flips 3-to-2, 2-to-2, // +// and 4-to-4 are forbidden. // +// // +/////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +// // +// categorizeface() Determine the flip type of a given face. // +// // +// On input, 'horiz' represents the face abc we want to flip (imagine it is // +// parallel to the horizon). Let the tet above it be abcd. // +// // +// This routine determines the suitable type of flip operation for 'horiz'. // +// - Returns T23 if a 2-to-3 flip is applicable. 'horiz' is same as input. // +// - Returns T32 if a 3-to-2 flip is applicable. 'horiz' returns the edge // +// of abc which is the flipable. // +// - Returns T22 if a 2-to-2 or 4-to-4 flip is applicable. 'horiz' returns // +// the edge of abc which is flipable. // +// - Returns N32 indicates it is unflipable due to the absence of a tet. // +// 'horize' returns the unflipable edge. // +// - Returns N40 indicates it is unflipable and is locally Delaunay. // +// - Returns FORBIDDENFACE indicates abc is a subface. // +// - Returns FORBIDDENEDGE indicates the flipable edge of abc is a segment.// +// 'horize' returns the flipable edge. // +// // +// Given a face abc, with two adjoining tetrahedra abcd and bace. If abc is // +// flipable, i.e., T23, T32, T22 or T44, its flip type can be determined by // +// doing five orientation tests: two tests for determining that d, e lie on // +// the different sides of abc, three tests for determining if the edge de // +// intersects the face abc. However, if we use the neighbor information of // +// the mesh data structure, we can reduce the five orientation tests to at // +// most three tests, that is, the two tests for determining whether d and e // +// lie on the different sides of abc can be saved. // +// // +/////////////////////////////////////////////////////////////////////////////// + +enum tetgenmesh::fliptype tetgenmesh::categorizeface(triface& horiz) +{ + triface symhoriz, casing; + face checksh, checkseg; + face cassh1, cassh2; + point pa, pb, pc, pd, pe, pf, pg; + point abdoppo, bcdoppo, cadoppo; + REAL ori1, ori2, ori3; + int adjtet; + + sym(horiz, symhoriz); + if (symhoriz.tet == dummytet) { + // A hull face is unflipable and locally Delaunay. + return N40; + } + + adjustedgering(horiz, CCW); + findedge(&symhoriz, dest(horiz), org(horiz)); + pa = org(horiz); + pb = dest(horiz); + pc = apex(horiz); + pd = oppo(horiz); + pe = oppo(symhoriz); + + // Find the number of adjacent tetrahedra of abc, which have d, e, and one + // of corners of abc as their corners. This number can be 0, 1 and 2. + abdoppo = bcdoppo = cadoppo = (point) NULL; + adjtet = 0; + fnext(horiz, casing); // at edge 'ab'. + symself(casing); + if (casing.tet != dummytet) { + abdoppo = oppo(casing); + if (abdoppo == pe) adjtet++; + } + enextfnext(horiz, casing); // at edge 'bc'. + symself(casing); + if (casing.tet != dummytet) { + bcdoppo = oppo(casing); + if (bcdoppo == pe) adjtet++; + } + enext2fnext(horiz, casing); // at edge 'ca'. + symself(casing); + if (casing.tet != dummytet) { + cadoppo = oppo(casing); + if (cadoppo == pe) adjtet++; + } + + if (adjtet == 0) { + // No adjacent tetrahedron. Types T23, T22 and T44 are possible. + ori1 = orient3d(pa, pb, pd, pe); + if (checksubfaces && ori1 != 0.0) { + // Check if abd and abe are both boundary faces? + fnext(horiz, casing); + tspivot(casing, cassh1); + fnext(symhoriz, casing); + tspivot(casing, cassh2); + if ((cassh1.sh != dummysh) && (cassh2.sh != dummysh)) { + // abd and abe are both boundary faces. Check if ab is a segment. + findedge(&cassh1, pa, pb); + sspivot(cassh1, checkseg); + if (checkseg.sh == dummysh) { + // ab is not a segment - abd and abe belong to the same facet. + // The four points are forced to be coplanar. + ori1 = 0.0; + } else { + // ab is a segment - abd and abe belong to two different facets. + // In principle, a, b, c and d can form a tetrahedron (since + // ori1 != 0.0). However, we should avoid to create a very + // flat one which may form a sequence of extremely badly-shaped + // or even wrong orientational tets. Test with a larger epsilon. + if (iscoplanar(pa, pb, pd, pe, ori1, b->epsilon * 1e+2)) ori1 = 0.0; + } + } else { + // abd and abe are not both boundary faces. Check if abd and bae + // are approximately coplanar with respect to the epsilon. + if (iscoplanar(pa, pb, pd, pe, ori1, b->epsilon)) ori1 = 0.0; + } + } + if (ori1 < 0.0) { + // e lies above abd, unflipable, tet abde is not present. +#ifdef SELF_CHECK + if (!nonconvex) { + // abd and abe should not be hull faces, check it. + fnext(horiz, casing); + symself(casing); + assert(casing.tet != dummytet); + fnext(symhoriz, casing); + symself(casing); + assert(casing.tet != dummytet); + } +#endif + if (checksubfaces) { + // The nonconvexbility may be casued by existing an subsegment. + tsspivot(&horiz, &checkseg); + if (checkseg.sh != dummysh) { + return FORBIDDENEDGE; + } + } + return N32; + } + ori2 = orient3d(pb, pc, pd, pe); + if (checksubfaces && ori2 != 0.0) { + // Check if bcd and cbe are both boundary faces. + enextfnext(horiz, casing); + tspivot(casing, cassh1); + enext2fnext(symhoriz, casing); + tspivot(casing, cassh2); + if (cassh1.sh != dummysh && cassh2.sh != dummysh) { + // bcd and cbe are both boundary faces. Check if bc is a segment. + findedge(&cassh1, pb, pc); + sspivot(cassh1, checkseg); + if (checkseg.sh == dummysh) { + // bc is not a segment - bcd and cbe belong to the same facet. + // The four points are forced to be coplanar. + ori2 = 0.0; + } else { + if (iscoplanar(pb, pc, pd, pe, ori2, b->epsilon * 1e+2)) ori2 = 0.0; + } + } else { + // bcd and cbe are not both boundary faces. Check if bcd and cbe + // are approximately coplanar with respect to the epsilon. + if (iscoplanar(pb, pc, pd, pe, ori2, b->epsilon)) ori2 = 0.0; + } + } + if (ori2 < 0.0) { + // e lies above bcd, unflipable, tet bcde is not present. +#ifdef SELF_CHECK + if (!nonconvex) { + // bcd and cbe should not be hull faces, check it. + enextfnext(horiz, casing); + symself(casing); + assert(casing.tet != dummytet); + enext2fnext(symhoriz, casing); + symself(casing); + assert(casing.tet != dummytet); + } +#endif + enextself(horiz); + if (checksubfaces) { + // The nonconvexbility may be casued by existing an subsegment. + tsspivot(&horiz, &checkseg); + if (checkseg.sh != dummysh) { + return FORBIDDENEDGE; + } + } + return N32; + } + ori3 = orient3d(pc, pa, pd, pe); + if (checksubfaces && ori3 != 0.0) { + // Check if cad and ace are both boundary faces. + enext2fnext(horiz, casing); + tspivot(casing, cassh1); + enextfnext(symhoriz, casing); + tspivot(casing, cassh2); + if (cassh1.sh != dummysh && cassh2.sh != dummysh) { + // cad and ace are both boundary faces. Check if ca is a segment. + findedge(&cassh1, pc, pa); + sspivot(cassh1, checkseg); + if (checkseg.sh == dummysh) { + // ca is not a segment - cad and ace belong to the same facet. + // The four points are forced to be coplanar. + ori3 = 0.0; + } else { + // ca is a segment - cad and ace belong to two different facets. + // In principle, c, a, d and e can form a tetrahedron (since + // ori3 != 0.0). Use a larger eps to test if they're coplanar. + if (iscoplanar(pc, pa, pd, pe, ori3, b->epsilon * 1e+2)) ori3 = 0.0; + } + } else { + // cad and ace are not both boundary faces. Check if cad and ace + // are approximately coplanar with respect to the epsilon. + if (iscoplanar(pc, pa, pd, pe, ori3, b->epsilon)) ori3 = 0.0; + } + } + if (ori3 < 0.0) { + // e lies above cad, unflipable, tet cade is not present. +#ifdef SELF_CHECK + if (!nonconvex) { + // cad and ace should not be hull faces, check it. + enext2fnext(horiz, casing); + symself(casing); + assert(casing.tet != dummytet); + enextfnext(symhoriz, casing); + symself(casing); + assert(casing.tet != dummytet); + } +#endif + enext2self(horiz); + if (checksubfaces) { + // The nonconvexbility may be casued by existing an subsegment. + tsspivot(&horiz, &checkseg); + if (checkseg.sh != dummysh) { + return FORBIDDENEDGE; + } + } + return N32; + } + if (ori1 == 0.0) { + // e is coplanar with abd. + if (ori2 * ori3 == 0.0) { + // only one zero is possible. + // assert(!(ori2 == 0.0 && ori3 == 0.0)); + // Three points (d, e, and a or b) are collinear, abc is unflipable + // and locally Delaunay. + return N40; + } + } else if (ori2 == 0.0) { + // e is coplanar with bcd. + if (ori1 * ori3 == 0.0) { + // only one zero is possible. + // assert(!(ori1 == 0.0 && ori3 == 0.0)); + // Three points (d, e, and b or c) are collinear, abc is unflipable + // and locally Delaunay. + return N40; + } + // Adjust 'horiz' and 'symhoriz' be the edge bc. + enextself(horiz); + enext2self(symhoriz); + } else if (ori3 == 0.0) { + // e is coplanar with cad. + if (ori1 * ori2 == 0.0) { + // only one zero is possible. + // assert(!(ori1 == 0.0 && ori2 == 0.0)); + // Three points (d, e, and c or a) are collinear, abc is unflipable + // and locally Delaunay. + return N40; + } + // Adjust 'horiz' and 'symhoriz' be the edge ca. + enext2self(horiz); + enextself(symhoriz); + } else { + // e lies below all three faces, flipable. + if (checksubfaces) { + tspivot(horiz, checksh); + if (checksh.sh != dummysh) { + // To flip a subface is forbidden. + return FORBIDDENFACE; + } + } + return T23; + } + // Four points are coplanar, T22 or T44 is possible. + if (checksubfaces) { + tsspivot(&horiz, &checkseg); + if (checkseg.sh != dummysh) { + // To flip a subsegment is forbidden. + return FORBIDDENEDGE; + } + tspivot(horiz, checksh); + if (checksh.sh != dummysh) { + // To flip a subface is forbidden. + return FORBIDDENFACE; + } + } + // Assume the four coplanar points are a, b, d, e, abd and abe are two + // coplanar faces. If both abd and abe are hull faces, flipable(T22). + // If they are interior faces, get the opposite tetrahedra abdf and + // abeg, if f = g, flipable (T44). Otherwise, unflipable. + pf = pg = (point) NULL; + fnext(horiz, casing); + symself(casing); + if (casing.tet != dummytet) { + pf = oppo(casing); + } + fnext(symhoriz, casing); + symself(casing); + if (casing.tet != dummytet) { + pg = oppo(casing); + } + if (pf == pg) { + // Either T22 (pf == pg == NULL) or T44 (pf and pg) is possible. + if (checksubfaces) { + // Retreat the corner points a, b, and c. + pa = org(horiz); + pb = dest(horiz); + pc = apex(horiz); + // Be careful not to create an inverted tetrahedron. Check the case. + ori1 = orient3d(pc, pd, pe, pa); + if (ori1 <= 0) return N40; + ori1 = orient3d(pd, pc, pe, pb); + if (ori1 <= 0) return N40; + if (pf != (point) NULL) { + ori1 = orient3d(pd, pf, pe, pa); + if (ori1 <= 0) return N40; + ori1 = orient3d(pf, pd, pe, pb); + if (ori1 <= 0) return N40; + } + } + if (pf == (point) NULL) { + // abd and abe are hull faces, flipable. + return T22; + } else { + // abd and abe are interior faces, flipable. +#ifdef SELF_CHECK + assert(pf != (point) NULL); +#endif + return T44; + } + } else { + // ab has more than four faces around it, unflipable. + return N32; + } + } else if (adjtet == 1) { + // One of its three edges is locally non-convex. Type T32 is possible. + // Adjust current configuration so that edge ab is non-convex. + if (bcdoppo == pe) { + // Edge bc is non-convex. Adjust 'horiz' and 'symhoriz' be edge bc. + enextself(horiz); + enext2self(symhoriz); + pa = org(horiz); + pb = dest(horiz); + pc = apex(horiz); + } else if (cadoppo == pe) { + // Edge ca is non-convex. Adjust 'horiz' and 'symhoriz' be edge ca. + enext2self(horiz); + enextself(symhoriz); + pa = org(horiz); + pb = dest(horiz); + pc = apex(horiz); + } else { + // Edge ab is non-convex. +#ifdef SELF_CHECK + assert(abdoppo == pe); +#endif + } // Now ab is the non-convex edge. + // In order to be flipable, ab should cross face cde. Check it. + ori1 = orient3d(pc, pd, pe, pa); + if (checksubfaces && ori1 != 0.0) { + // Check if cad and ace are both boundary faces. + enext2fnext(horiz, casing); + tspivot(casing, cassh1); + enextfnext(symhoriz, casing); + tspivot(casing, cassh2); + if (cassh1.sh != dummysh && cassh2.sh != dummysh) { + // cad and ace are both boundary faces. Check if ca is a segment. + findedge(&cassh1, pc, pa); + sspivot(cassh1, checkseg); + if (checkseg.sh == dummysh) { + // ca is not a segment. cad and ace belong to the same facet. + // The four points are forced to be coplanar. + ori1 = 0.0; + } else { + // ca is a segment. cad and ace belong to different facets. + // In principle, c, d, e, and a can form a tetrahedron (since + // ori1 != 0.0). However, we should avoid to create a very + // flat tet. Use a larger epsilon to test if they're coplanar. + if (iscoplanar(pc, pd, pe, pa, ori1, b->epsilon * 1e+2)) ori1 = 0.0; + } + } else { + // Check if c, d, e, and a are approximately coplanar. + if (iscoplanar(pc, pd, pe, pa, ori1, b->epsilon)) ori1 = 0.0; + } + } + if (ori1 <= 0.0) { + // a lies above or is coplanar cde, abc is locally Delaunay. + return N40; + } + ori2 = orient3d(pd, pc, pe, pb); + if (checksubfaces && ori2 != 0.0) { + // Check if bcd and cbe are both boundary faces. + enextfnext(horiz, casing); + tspivot(casing, cassh1); + enext2fnext(symhoriz, casing); + tspivot(casing, cassh2); + if (cassh1.sh != dummysh && cassh2.sh != dummysh) { + // bcd and cbe are both boundary faces. Check if bc is a segment. + findedge(&cassh1, pb, pc); + sspivot(cassh1, checkseg); + if (checkseg.sh == dummysh) { + // bc is not a segment. bcd and cbe belong to the same facet. + // The four points are forced to be coplanar. + ori2 = 0.0; + } else { + // bc is a segment. bcd and cbe belong to different facets. + // In principle, d, c, e, and b can form a tetrahedron (since + // ori2 != 0.0). However, we should avoid to create a very + // flat tet. Use a larger epsilon to test if they're coplanar. + if (iscoplanar(pd, pc, pe, pb, ori2, b->epsilon * 1e+2)) ori2 = 0.0; + } + } else { + // Check if d, c, e, and b are approximately coplanar. + if (iscoplanar(pd, pc, pe, pb, ori2, b->epsilon)) ori2 = 0.0; + } + } + if (ori2 <= 0.0) { + // b lies above dce, unflipable, and abc is locally Delaunay. + return N40; + } + // Edge ab crosses face cde properly. + if (checksubfaces) { + // If abc is subface, then ab must be a subsegment (because abde is + // a tetrahedron and ab crosses cde properly). + tsspivot(&horiz, &checkseg); + if (checkseg.sh != dummysh) { + // To flip a subsegment is forbidden. + return FORBIDDENEDGE; + } + // Both abd and bae should not be subfaces (because they're not + // coplanar and ab is not a subsegment). However, they may be + // subfaces and belong to a facet (created during facet recovery), + // that is, abde is an invalid tetrahedron. Find this case out. + fnext(horiz, casing); + tspivot(casing, cassh1); + fnext(symhoriz, casing); + tspivot(casing, cassh2); + if (cassh1.sh != dummysh || cassh2.sh != dummysh) { + if (!b->quiet) { + // Unfortunately, they're subfaces. Corrections need be done here. + printf("Warning: A tetrahedron spans two subfaces of a facet.\n"); + } + // Temporarily, let it be there. + return N32; + } + } + return T32; + } else { + // The convex hull of {a, b, c, d, e} has only four vertices, abc is + // unflipable, furthermore, it is locally Delaunay. + return N40; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// enqueueflipface(), enqueueflipedge() Queue a face (or an edge). // +// // +// The face (or edge) may be non-locally Delaunay. It is queued for process- // +// ing in flip() (or flipsub()). The vertices of the face (edge) are stored // +// seperatly to ensure the face (or edge) is still the same one when we save // +// it since other flips will cause this face (or edge) be changed or dead. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::enqueueflipface(triface& checkface, queue* flipqueue) +{ + badface *queface; + triface symface; + + sym(checkface, symface); + if (symface.tet != dummytet) { + queface = (badface *) flipqueue->push((void *) NULL); + queface->tt = checkface; + queface->foppo = oppo(symface); + } +} + +void tetgenmesh::enqueueflipedge(face& checkedge, queue* flipqueue) +{ + badface *queface; + + queface = (badface *) flipqueue->push((void *) NULL); + queface->ss = checkedge; + queface->forg = sorg(checkedge); + queface->fdest = sdest(checkedge); +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// flip23() Perform a 2-to-3 flip. // +// // +// On input, 'flipface' represents the face will be flipped. Let it is abc, // +// the two tetrahedra sharing abc are abcd, bace. abc is not a subface. // +// // +// A 2-to-3 flip is to change two tetrahedra abcd, bace to three tetrahedra // +// edab, edbc, and edca. As a result, face abc has been removed and three // +// new faces eda, edb and edc have been created. // +// // +// On completion, 'flipface' returns edab. If 'flipqueue' is not NULL, all // +// possibly non-Delaunay faces are added into it. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::flip23(triface* flipface, queue* flipqueue) +{ + triface abcd, bace; // Old configuration. + triface oldabd, oldbcd, oldcad; + triface abdcasing, bcdcasing, cadcasing; + triface oldbae, oldcbe, oldace; + triface baecasing, cbecasing, acecasing; + triface worktet; + face abdsh, bcdsh, cadsh; // The six subfaces on the CH. + face baesh, cbesh, acesh; + face abseg, bcseg, caseg; // The nine segs on the CH. + face adseg, bdseg, cdseg; + face aeseg, beseg, ceseg; + triface edab, edbc, edca; // New configuration. + point pa, pb, pc, pd, pe; + REAL attrib, volume; + int i; + + abcd = *flipface; + adjustedgering(abcd, CCW); // abcd represents edge ab. + pa = org(abcd); + pb = dest(abcd); + pc = apex(abcd); + pd = oppo(abcd); + // sym(abcd, bace); + // findedge(&bace, dest(abcd), org(abcd)); // bace represents edge ba. + sym(abcd, bace); + bace.ver = 0; // CCW. + for (i = 0; (i < 3) && (org(bace) != pb); i++) { + enextself(bace); + } + pe = oppo(bace); + + if (b->verbose > 2) { + printf(" Do T23 on face (%d, %d, %d) %d, %d.\n", pointmark(pa), + pointmark(pb), pointmark(pc), pointmark(pd), pointmark(pe)); + } + flip23s++; + + // Storing the old configuration outside the convex hull. + fnext(abcd, oldabd); + enextfnext(abcd, oldbcd); + enext2fnext(abcd, oldcad); + fnext(bace, oldbae); + enext2fnext(bace, oldcbe); + enextfnext(bace, oldace); + sym(oldabd, abdcasing); + sym(oldbcd, bcdcasing); + sym(oldcad, cadcasing); + sym(oldbae, baecasing); + sym(oldcbe, cbecasing); + sym(oldace, acecasing); + if (checksubfaces) { + tspivot(oldabd, abdsh); + tspivot(oldbcd, bcdsh); + tspivot(oldcad, cadsh); + tspivot(oldbae, baesh); + tspivot(oldcbe, cbesh); + tspivot(oldace, acesh); + } else if (checksubsegs) { + tsspivot1(abcd, abseg); + enext(abcd, worktet); + tsspivot1(worktet, bcseg); + enext2(abcd, worktet); + tsspivot1(worktet, caseg); + enext2(oldabd, worktet); + tsspivot1(worktet, adseg); + enext2(oldbcd, worktet); + tsspivot1(worktet, bdseg); + enext2(oldcad, worktet); + tsspivot1(worktet, cdseg); + enext(oldbae, worktet); + tsspivot1(worktet, aeseg); + enext(oldcbe, worktet); + tsspivot1(worktet, beseg); + enext(oldace, worktet); + tsspivot1(worktet, ceseg); + } + + // Creating the new configuration inside the convex hull. + edab.tet = abcd.tet; // Update abcd to be edab. + setorg (edab, pe); + setdest(edab, pd); + setapex(edab, pa); + setoppo(edab, pb); + edbc.tet = bace.tet; // Update bace to be edbc. + setorg (edbc, pe); + setdest(edbc, pd); + setapex(edbc, pb); + setoppo(edbc, pc); + maketetrahedron(&edca); // Create edca. + setorg (edca, pe); + setdest(edca, pd); + setapex(edca, pc); + setoppo(edca, pa); + // Set the element attributes of the new tetrahedron 'edca'. + for (i = 0; i < in->numberoftetrahedronattributes; i++) { + attrib = elemattribute(abcd.tet, i); + setelemattribute(edca.tet, i, attrib); + } + // Set the volume constraint of the new tetrahedron 'edca' if the -ra + // switches are not used together. In -ra case, the various volume + // constraints can be spreaded very far. + if (b->varvolume && !b->refine) { + volume = volumebound(abcd.tet); + setvolumebound(edca.tet, volume); + } + + // Clear old bonds in edab(was abcd) and edbc(was bace). + for (i = 0; i < 4; i ++) { + edab.tet[i] = (tetrahedron) dummytet; + } + for (i = 0; i < 4; i ++) { + edbc.tet[i] = (tetrahedron) dummytet; + } + // Bond the faces inside the convex hull. + edab.loc = 0; + edca.loc = 1; + bond(edab, edca); + edab.loc = 1; + edbc.loc = 0; + bond(edab, edbc); + edbc.loc = 1; + edca.loc = 0; + bond(edbc, edca); + // Bond the faces on the convex hull. + edab.loc = 2; + bond(edab, abdcasing); + edab.loc = 3; + bond(edab, baecasing); + edbc.loc = 2; + bond(edbc, bcdcasing); + edbc.loc = 3; + bond(edbc, cbecasing); + edca.loc = 2; + bond(edca, cadcasing); + edca.loc = 3; + bond(edca, acecasing); + // There may exist subfaces that need to be bonded to new configuarton. + if (checksubfaces) { + // Clear old flags in edab(was abcd) and edbc(was bace). + for (i = 0; i < 4; i ++) { + edab.loc = i; + tsdissolve(edab); + edbc.loc = i; + tsdissolve(edbc); + } + if (abdsh.sh != dummysh) { + edab.loc = 2; + tsbond(edab, abdsh); + } + if (baesh.sh != dummysh) { + edab.loc = 3; + tsbond(edab, baesh); + } + if (bcdsh.sh != dummysh) { + edbc.loc = 2; + tsbond(edbc, bcdsh); + } + if (cbesh.sh != dummysh) { + edbc.loc = 3; + tsbond(edbc, cbesh); + } + if (cadsh.sh != dummysh) { + edca.loc = 2; + tsbond(edca, cadsh); + } + if (acesh.sh != dummysh) { + edca.loc = 3; + tsbond(edca, acesh); + } + } else if (checksubsegs) { + for (i = 0; i < 6; i++) { + edab.tet[8 + i] = (tetrahedron) dummysh; + } + for (i = 0; i < 6; i++) { + edbc.tet[8 + i] = (tetrahedron) dummysh; + } + edab.loc = edab.ver = 0; + edbc.loc = edab.ver = 0; + edca.loc = edab.ver = 0; + // Operate in tet edab (5 edges). + enext(edab, worktet); + tssbond1(worktet, adseg); + enext2(edab, worktet); + tssbond1(worktet, aeseg); + fnext(edab, worktet); + enextself(worktet); + tssbond1(worktet, bdseg); + enextself(worktet); + tssbond1(worktet, beseg); + enextfnext(edab, worktet); + enextself(worktet); + tssbond1(worktet, abseg); + // Operate in tet edbc (5 edges) + enext(edbc, worktet); + tssbond1(worktet, bdseg); + enext2(edbc, worktet); + tssbond1(worktet, beseg); + fnext(edbc, worktet); + enextself(worktet); + tssbond1(worktet, cdseg); + enextself(worktet); + tssbond1(worktet, ceseg); + enextfnext(edbc, worktet); + enextself(worktet); + tssbond1(worktet, bcseg); + // Operate in tet edca (5 edges) + enext(edca, worktet); + tssbond1(worktet, cdseg); + enext2(edca, worktet); + tssbond1(worktet, ceseg); + fnext(edca, worktet); + enextself(worktet); + tssbond1(worktet, adseg); + enextself(worktet); + tssbond1(worktet, aeseg); + enextfnext(edca, worktet); + enextself(worktet); + tssbond1(worktet, caseg); + } + + edab.loc = 0; + edbc.loc = 0; + edca.loc = 0; + if (b->verbose > 3) { + printf(" Updating edab "); + printtet(&edab); + printf(" Updating edbc "); + printtet(&edbc); + printf(" Creating edca "); + printtet(&edca); + } + + if (flipqueue != (queue *) NULL) { + enextfnext(edab, abdcasing); + enqueueflipface(abdcasing, flipqueue); + enext2fnext(edab, baecasing); + enqueueflipface(baecasing, flipqueue); + enextfnext(edbc, bcdcasing); + enqueueflipface(bcdcasing, flipqueue); + enext2fnext(edbc, cbecasing); + enqueueflipface(cbecasing, flipqueue); + enextfnext(edca, cadcasing); + enqueueflipface(cadcasing, flipqueue); + enext2fnext(edca, acecasing); + enqueueflipface(acecasing, flipqueue); + } + + // Save a live handle in 'recenttet'. + recenttet = edbc; + // Set the return handle be edab. + *flipface = edab; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// flip32() Perform a 3-to-2 flip. // +// // +// On input, 'flipface' represents the face will be flipped. Let it is eda, // +// where edge ed is locally non-convex. Three tetrahedra sharing ed are edab,// +// edbc, and edca. ed is not a subsegment. // +// // +// A 3-to-2 flip is to change the three tetrahedra edab, edbc, and edca into // +// another two tetrahedra abcd and bace. As a result, the edge ed has been // +// removed and the face abc has been created. // +// // +// On completion, 'flipface' returns abcd. If 'flipqueue' is not NULL, all // +// possibly non-Delaunay faces are added into it. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::flip32(triface* flipface, queue* flipqueue) +{ + triface edab, edbc, edca; // Old configuration. + triface oldabd, oldbcd, oldcad; + triface abdcasing, bcdcasing, cadcasing; + triface oldbae, oldcbe, oldace; + triface baecasing, cbecasing, acecasing; + triface worktet; + face abdsh, bcdsh, cadsh; + face baesh, cbesh, acesh; + face abseg, bcseg, caseg; // The nine segs on the CH. + face adseg, bdseg, cdseg; + face aeseg, beseg, ceseg; + triface abcd, bace; // New configuration. + point pa, pb, pc, pd, pe; + int i; + + edab = *flipface; + adjustedgering(edab, CCW); + pa = apex(edab); + pb = oppo(edab); + pd = dest(edab); + pe = org(edab); + fnext(edab, edbc); + symself(edbc); + edbc.ver = 0; + for (i = 0; (i < 3) && (org(edbc) != pe); i++) { + enextself(edbc); + } + pc = oppo(edbc); + fnext(edbc, edca); + symself(edca); + edca.ver = 0; + for (i = 0; (i < 3) && (org(edca) != pe); i++) { + enextself(edca); + } + + if (b->verbose > 2) { + printf(" Do T32 on edge (%d, %d) %d, %d, %d.\n", pointmark(pe), + pointmark(pd), pointmark(pa), pointmark(pb), pointmark(pc)); + } + flip32s++; + + // Storing the old configuration outside the convex hull. + enextfnext(edab, oldabd); + enext2fnext(edab, oldbae); + enextfnext(edbc, oldbcd); + enext2fnext(edbc, oldcbe); + enextfnext(edca, oldcad); + enext2fnext(edca, oldace); + sym(oldabd, abdcasing); + sym(oldbcd, bcdcasing); + sym(oldcad, cadcasing); + sym(oldbae, baecasing); sym(oldcbe, cbecasing); sym(oldace, acecasing); if (checksubfaces) { @@ -9807,8 +10393,7 @@ void tetgenmesh::flip32(triface* flipface, queue* flipqueue) tspivot(oldbae, baesh); tspivot(oldcbe, cbesh); tspivot(oldace, acesh); - } - if (checksubsegs) { + } else if (checksubsegs) { enext(edab, worktet); tsspivot1(worktet, adseg); enext2(edab, worktet); @@ -9873,12 +10458,10 @@ void tetgenmesh::flip32(triface* flipface, queue* flipqueue) if (checksubfaces) { // Clear old bonds in abcd(was edab) and bace(was edbc). for (i = 0; i < 4; i ++) { - abcd.loc = i; - tsdissolve(abcd); + abcd.tet[8 + i] = (tetrahedron) dummysh; } for (i = 0; i < 4; i ++) { - bace.loc = i; - tsdissolve(bace); + bace.tet[8 + i] = (tetrahedron) dummysh; } if (abdsh.sh != dummysh) { abcd.loc = 1; @@ -9904,17 +10487,12 @@ void tetgenmesh::flip32(triface* flipface, queue* flipqueue) bace.loc = 2; tsbond(bace, acesh); } - } - if (checksubsegs) { + } else if (checksubsegs) { for (i = 0; i < 6; i++) { - abcd.loc = edge2locver[i][0]; - abcd.ver = edge2locver[i][1]; - tssdissolve1(abcd); + abcd.tet[8 + i] = (tetrahedron) dummysh; } for (i = 0; i < 6; i++) { - bace.loc = edge2locver[i][0]; - bace.ver = edge2locver[i][1]; - tssdissolve1(bace); + bace.tet[8 + i] = (tetrahedron) dummysh; } abcd.loc = abcd.ver = 0; bace.loc = bace.ver = 0; @@ -9959,13 +10537,6 @@ void tetgenmesh::flip32(triface* flipface, queue* flipqueue) // printtet(&edca); } - // Update point-to-tet map. - setpoint2tet(pa, encode(abcd)); - setpoint2tet(pb, encode(abcd)); - setpoint2tet(pc, encode(abcd)); - setpoint2tet(pd, encode(abcd)); - setpoint2tet(pe, encode(bace)); - if (flipqueue != (queue *) NULL) { fnext(abcd, abdcasing); enqueueflipface(abdcasing, flipqueue); @@ -10071,9 +10642,9 @@ void tetgenmesh::flip22(triface* flipface, queue* flipqueue) #endif } - if (b->verbose > 1) { - printf(" Flip edge (%d, %d) to (%d, %d) %s.\n", pointmark(pa), - pointmark(pb), pointmark(pc), pointmark(pd), mirrorflag ? "T44" : "T22"); + if (b->verbose > 2) { + printf(" Do %s on edge (%d, %d).\n", mirrorflag ? "T44" : "T22", + pointmark(pa), pointmark(pb)); } mirrorflag ? flip44s++ : flip22s++; @@ -10093,8 +10664,7 @@ void tetgenmesh::flip22(triface* flipface, queue* flipqueue) tspivot(olddbe, dbesh); tspivot(abce, abc); tspivot(bade, bad); - } - if (checksubsegs) { + } else if (checksubsegs) { // Coplanar segs: a->d->b->c. enext(bade, worktet); tsspivot1(worktet, adseg); @@ -10132,8 +10702,7 @@ void tetgenmesh::flip22(triface* flipface, queue* flipqueue) tspivot(oldcbf, cbfsh); tspivot(oldbdf, bdfsh); tspivot(olddaf, dafsh); - } - if (checksubsegs) { + } else if (checksubsegs) { // Below segs: a->f, d->f, b->f, c->f. fnext(abdf, worktet); enext2self(worktet); @@ -10177,8 +10746,7 @@ void tetgenmesh::flip22(triface* flipface, queue* flipqueue) } else { tsbond(olddbe, bcesh); } - } - if (checksubsegs) { + } else if (checksubsegs) { // 5 edges in abce are changed. enext(abce, worktet); // fit b->c into c->a. if (caseg.sh == dummysh) { @@ -10276,8 +10844,7 @@ void tetgenmesh::flip22(triface* flipface, queue* flipqueue) } else { tsbond(oldbdf, cbfsh); } - } - if (checksubsegs) { + } else if (checksubsegs) { // 5 edges in bacf are changed. enext2(bacf, worktet); // fit b->c into c->a. if (caseg.sh == dummysh) { @@ -10365,16 +10932,6 @@ void tetgenmesh::flip22(triface* flipface, queue* flipqueue) setapex(abdf, pb); } - // Update point-to-tet map. - setpoint2tet(pa, encode(abce)); - setpoint2tet(pb, encode(bade)); - setpoint2tet(pc, encode(abce)); - setpoint2tet(pd, encode(bade)); - setpoint2tet(pe, encode(abce)); - if (mirrorflag) { - setpoint2tet(pf, encode(bacf)); - } - // Are there subfaces need to be flipped? if (checksubfaces && abc.sh != dummysh) { #ifdef SELF_CHECK @@ -10479,15 +11036,10 @@ void tetgenmesh::flip22sub(face* flipedge, queue* flipqueue) pc = sapex(abc); pd = sapex(bad); - if (b->verbose > 1) { - printf(" Flip subedge (%d, %d) to (%d, %d).\n", pointmark(pa), - pointmark(pb), pointmark(pc), pointmark(pd)); + if (b->verbose > 2) { + printf(" Flip sub edge (%d, %d).\n", pointmark(pa), pointmark(pb)); } - // Unmark the flipped subfaces (used in mesh refinement). 2009-08-17. - sunmarktest(abc); - sunmarktest(bad); - // Save the old configuration outside the quadrilateral. senext(abc, oldbc); senext2(abc, oldca); @@ -10576,7 +11128,7 @@ void tetgenmesh::flip22sub(face* flipedge, queue* flipqueue) sbond1(oldbc, cacasout); } else { // Bond 'oldbc' to itself. - sdissolve(oldbc); // sbond(oldbc, oldbc); + sbond(oldbc, oldbc); // Make sure that dummysh always correctly bonded. dummysh[0] = sencode(oldbc); } @@ -10590,7 +11142,7 @@ void tetgenmesh::flip22sub(face* flipedge, queue* flipqueue) sbond1(oldca, adcasout); } else { // Bond 'oldca' to itself. - sdissolve(oldca); // sbond(oldca, oldca); + sbond(oldca, oldca); // Make sure that dummysh always correctly bonded. dummysh[0] = sencode(oldca); } @@ -10604,7 +11156,7 @@ void tetgenmesh::flip22sub(face* flipedge, queue* flipqueue) sbond1(oldad, dbcasout); } else { // Bond 'oldad' to itself. - sdissolve(oldad); // sbond(oldad, oldad); + sbond(oldad, oldad); // Make sure that dummysh always correctly bonded. dummysh[0] = sencode(oldad); } @@ -10618,7 +11170,7 @@ void tetgenmesh::flip22sub(face* flipedge, queue* flipqueue) sbond1(olddb, bccasout); } else { // Bond 'olddb' to itself. - sdissolve(olddb); // sbond(olddb, olddb); + sbond(olddb, olddb); // Make sure that dummysh always correctly bonded. dummysh[0] = sencode(olddb); } @@ -10635,336 +11187,433 @@ void tetgenmesh::flip22sub(face* flipedge, queue* flipqueue) setsdest(bad, pd); setsapex(bad, pb); - // Update the point-to-subface map. - // Comemnt: After the flip, abc becomes dca, bad becodes cdb. - setpoint2sh(pa, sencode(abc)); // dca - setpoint2sh(pb, sencode(bad)); // cdb - setpoint2sh(pc, sencode(bad)); - setpoint2sh(pd, sencode(bad)); + if (flipqueue != (queue *) NULL) { + enqueueflipedge(bccasout, flipqueue); + enqueueflipedge(cacasout, flipqueue); + enqueueflipedge(adcasout, flipqueue); + enqueueflipedge(dbcasout, flipqueue); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// flip() Flips non-locally Delaunay faces in flipqueue until it is empty.// +// // +// Assumpation: Current tetrahedralization is non-Delaunay after inserting // +// a point or performing a flip operation, all possibly non-Delaunay faces // +// are in 'flipqueue'. // +// // +// If 'plastflip' is not NULL, it is used to return a stack of recently // +// flipped faces. This stack will be used to reverse the flips done in this // +// routine later for removing a newly inserted point because it encroaches // +// any subfaces or subsegments. // +// // +// The return value is the total number of flips done during this invocation.// +// // +/////////////////////////////////////////////////////////////////////////////// + +long tetgenmesh::flip(queue* flipqueue, badface **plastflip) +{ + badface *qface, *newflip; + triface flipface, symface; + point pa, pb, pc, pd, pe; + enum fliptype fc; + REAL sign, bakepsilon; + long flipcount, maxfaces; + int epscount, fcount; + int ia, ib, ic, id, ie; + + if (b->verbose > 1) { + printf(" Do flipface queue: %ld faces.\n", flipqueue->len()); + } + + flipcount = flip23s + flip32s + flip22s + flip44s; + if (checksubfaces) { + maxfaces = (4l * tetrahedrons->items + hullsize) / 2l; + fcount = 0; + } + + if (plastflip != (badface **) NULL) { + // Initialize the stack of the flip sequence. + flipstackers->restart(); + *plastflip = (badface *) NULL; + } + + // Loop until the queue is empty. + while (!flipqueue->empty()) { + qface = (badface *) flipqueue->pop(); + flipface = qface->tt; + if (isdead(&flipface)) continue; + sym(flipface, symface); + // Only do check when the adjacent tet exists and it's not a "fake" tet. + if ((symface.tet != dummytet) && (oppo(symface) == qface->foppo)) { + // For positive orientation that insphere() test requires. + adjustedgering(flipface, CW); + pa = org(flipface); + pb = dest(flipface); + pc = apex(flipface); + pd = oppo(flipface); + pe = oppo(symface); + if (symbolic) { + ia = pointmark(pa); + ib = pointmark(pb); + ic = pointmark(pc); + id = pointmark(pd); + ie = pointmark(pe); + sign = insphere_sos(pa, pb, pc, pd, pe, ia, ib, ic, id, ie); + assert(sign != 0.0); + } else { + sign = insphere(pa, pb, pc, pd, pe); + } + } else { + sign = -1.0; // A hull face is locally Delaunay. + } + if (sign > 0.0) { + // 'flipface' is non-locally Delaunay, try to flip it. + if (checksubfaces) { + fcount++; + bakepsilon = b->epsilon; + epscount = 0; + while (epscount < 32) { + fc = categorizeface(flipface); + if (fc == N40) { + b->epsilon *= 1e-1; + epscount++; + continue; + } + break; + } + b->epsilon = bakepsilon; + if (epscount >= 32) { + if (b->verbose > 0) { + printf("Warning: Can't flip a degenerate tetrahedron.\n"); + } + fc = N40; + } + } else { + fc = categorizeface(flipface); +#ifdef SELF_CHECK + assert(fc != N40); +#endif + } + switch (fc) { + // The following face types are flipable. + case T44: + case T22: + flip22(&flipface, flipqueue); + break; + case T23: + flip23(&flipface, flipqueue); + break; + case T32: + flip32(&flipface, flipqueue); + break; + // The following face types are unflipable. + case N32: + break; + case FORBIDDENFACE: + break; + case FORBIDDENEDGE: + break; + // This case is only possible when the domain is nonconvex. + case N40: + // assert(nonconvex); + break; + } + if (plastflip != (badface **) NULL) { + if ((fc == T44) || (fc == T22) || (fc == T23) || (fc == T32)) { + // Push the flipped face into stack. + newflip = (badface *) flipstackers->alloc(); + newflip->tt = flipface; + newflip->key = (REAL) fc; + newflip->forg = org(flipface); + newflip->fdest = dest(flipface); + newflip->fapex = apex(flipface); + newflip->previtem = *plastflip; + *plastflip = newflip; + } + } + } + } - if (flipqueue != (queue *) NULL) { - enqueueflipedge(bccasout, flipqueue); - enqueueflipedge(cacasout, flipqueue); - enqueueflipedge(adcasout, flipqueue); - enqueueflipedge(dbcasout, flipqueue); + flipcount = flip23s + flip32s + flip22s + flip44s - flipcount; + if (b->verbose > 1) { + printf(" %ld flips.\n", flipcount); } + + return flipcount; } /////////////////////////////////////////////////////////////////////////////// // // -// lawson3d() Perform 3D Lawson flips on non-Delaunay faces/edges. // +// lawson() Flip locally non-Delaunay faces by Lawson's algorithm. // // // /////////////////////////////////////////////////////////////////////////////// -long tetgenmesh::lawson3d(queue* flipqueue) +long tetgenmesh::lawson(list *misseglist, queue* flipqueue) { - badface *qface; - triface flipface, symface, flipedge; - triface neighface, symneighface; + badface *qface, *misseg; + triface flipface, symface; + triface starttet, spintet; face checksh, checkseg; - face neighsh, symneighsh; point pa, pb, pc, pd, pe; - point end1, end2; - REAL sign, ori1, ori2, ori3; - REAL ori4, len, vol; + point swappt; + REAL sign, ori; long flipcount; - int copflag; - int i; + int ia, ib, ic, id, ie; + int hitbdry, i; if (b->verbose > 1) { - printf(" Lawson flip: %ld faces.\n", flipqueue->len()); + printf(" Do flipface queue: %ld faces.\n", flipqueue->len()); } flipcount = flip23s + flip32s + flip22s + flip44s; - // Loop until the queue is empty. + // Go through the stack of possible flips and decide whether to do them. + // Note that during the loop new possible flips will be pushed onto + // this stack, while they popped in this loop. while (!flipqueue->empty()) { qface = (badface *) flipqueue->pop(); flipface = qface->tt; - if (isdead(&flipface)) continue; - if (flipface.tet == dummytet) continue; - // Do not flip it if it is a subface. - tspivot(flipface, checksh); - if (checksh.sh != dummysh) continue; - - sym(flipface, symface); - // Only do check when the adjacent tet exists and it's not a "fake" tet. - if ((symface.tet != dummytet) && (oppo(symface) == qface->foppo)) { - flipface.ver = 0; // CCW. - pa = org(flipface); - pb = dest(flipface); - pc = apex(flipface); - pd = oppo(flipface); - pe = oppo(symface); - sign = insphere_s(pb, pa, pc, pd, pe); - assert(sign != 0.0); - - if (sign > 0.0) { - // flipface is not locally Delaunay. Try to flip it. - ori1 = orient3d(pa, pb, pd, pe); - ori2 = orient3d(pb, pc, pd, pe); - ori3 = orient3d(pc, pa, pd, pe); - - flipedge = flipface; // Initialize flipedge. - copflag = 0; - - // Find a suitable flip. - if (ori1 > 0) { - if (ori2 > 0) { - if (ori3 > 0) { // (+++) - // A 2-to-3 flip is found. - // Do not flip it if it is a subface. - // tspivot(flipface, checksh); - // if (checksh.sh == dummysh) { - // Do not flip it if it will create a tet spanning two - // "coplanar" subfaces. We treat this case as either - // a 2-to-2 or a 4-to-4 flip. - for (i = 0; i < 3; i++) { - tsspivot(&flipface, &checkseg); - if (checkseg.sh == dummysh) { - fnext(flipface, neighface); - tspivot(neighface, neighsh); - if (neighsh.sh != dummysh) { - // Check if there exist another subface. - symedge(flipface, symface); - fnext(symface, symneighface); - tspivot(symneighface, symneighsh); - if (symneighsh.sh != dummysh) { - // Do not flip this face. Try to do a 2-to-2 or a - // 4-to-4 flip instead. - flipedge = flipface; - copflag = 1; - break; - } - } - } - enextself(flipface); - } - if (i == 3) { - // Do not flip if it will create a nearly degenerate tet - // at a segment. Once we created such a tet, it may - // prevent you to split the segment later. An example - // is in dump-.lua - for (i = 0; i < 3; i++) { - tsspivot(&flipface, &checkseg); - if (checkseg.sh != dummysh) { - end1 = (point) checkseg.sh[3]; - end2 = (point) checkseg.sh[4]; - ori4 = orient3d(end1, end2, pd, pe); - len = distance(end1, end2); - vol = len * len * len; - // Is it nearly degnerate? - if ((fabs(ori4) / vol) < b->epsilon) { - flipedge = flipface; - copflag = 0; - break; - } - } - enextself(flipface); - } - if (i == 3) { - flip23(&flipface, flipqueue); - continue; - } - } - // } - } else { - if (ori3 < 0) { // (++-) - // Try to flip edge [c, a]. - flipedge.ver = 4; - copflag = 0; - } else { // (++0) - // A 2-to-2 or 4-to-4 flip at edge [c, a]. - flipedge.ver = 4; - copflag = 1; - } - } - } else { - if (ori2 < 0) { - if (ori3 > 0) { // (+-+) - // Try to flip edge [b, c]. - flipedge.ver = 2; - copflag = 0; - } else { - if (ori3 < 0) { // (+--) - // Not possible when pe is inside the circumsphere of - // the tet [pa.pb, pc, pd]. - assert(0); - } else { // (+-0) - assert(0); // The same reason as above. - } - } - } else { // ori2 == 0 - if (ori3 > 0) { // (+0+) - // A 2-to-2 or 4-to-4 flip at edge [b, c]. - flipedge.ver = 2; - copflag = 1; - } else { - if (ori3 < 0) { // (+0-) - // Not possible when pe is inside the circumsphere of - // the tet [pa.pb, pc, pd]. - assert(0); - } else { // (+00) - assert(0); // The same reason as above. - } - } - } - } + // Check if tet has already been flipped out of existence. + if (!isdead(&flipface)) { + sym(flipface, symface); + // Check if this tet is the same as the one which was stacked. + if ((symface.tet != dummytet) && (oppo(symface) == qface->foppo)) { + flipface.ver = 0; // Select the CCW ring. + pa = org(flipface); + pb = dest(flipface); + pc = apex(flipface); + pd = oppo(flipface); + pe = oppo(symface); + if (symbolic) { + ia = pointmark(pa); + ib = pointmark(pb); + ic = pointmark(pc); + id = pointmark(pd); + ie = pointmark(pe); + sign = insphere_sos(pb, pa, pc, pd, pe, ib, ia, ic, id, ie); } else { - if (ori1 < 0) { - if (ori2 > 0) { - if (ori3 > 0) { // (-++) - // Try to flip edge [a, b]. - flipedge.ver = 0; - copflag = 0; - } else { - if (ori3 < 0) { // (-+-) - // Not possible when pe is inside the circumsphere of - // the tet [pa.pb, pc, pd]. - assert(0); - } else { // (-+0) - assert(0); // The same reason as above. - } - } + sign = insphere(pb, pa, pc, pd, pe); + } + if (sign > 0.0) { + for (i = 0; i < 3; i++) { + ori = orient3d(pa, pb, pd, pe); + if (ori > 0.0) { + // Goto and check the next edge. + swappt = pa; + pa = pb; + pb = pc; + pc = swappt; + enextself(flipface); } else { - if (ori2 < 0) { - if (ori3 > 0) { // (--+) - // Not possible when pe is inside the circumsphere of - // the tet [pa.pb, pc, pd]. - assert(0); - } else { - if (ori3 < 0) { // (---) - assert(0); - } else { // (--0) - assert(0); - } - } - } else { // ori2 == 0 - if (ori3 > 0) { // (-0+) - assert(0); - } else { - if (ori3 < 0) { // (-0-) - assert(0); - } else { // (-00) - assert(0); - } - } - } + break; // either (ori < 0.0) or (ori == 0.0) } - } else { // ori1 == 0 - if (ori2 > 0) { - if (ori3 > 0) { // (0++) - // A 2-to-2 or 4-to-4 flip at edge [a, b]. - flipedge.ver = 0; - copflag = 1; - } else { - if (ori3 < 0) { // (0+-) - assert(0); - } else { // (0+0) - assert(0); - } - } - } else { - if (ori2 < 0) { - if (ori3 > 0) { // (0-+) - assert(0); - } else { - if (ori3 < 0) { // (0--) - assert(0); - } else { // (0-0) - assert(0); - } - } - } else { - if (ori3 > 0) { // (00+) - assert(0); - } else { - if (ori3 < 0) { // (00-) - assert(0); - } else { // (000) - assert(0); - } - } + } // for (i = 0; ....) + if (ori > 0.0) { + // All three edges are convex, a 2-3 flip is possible. + if (checksubfaces) { + tspivot(flipface, checksh); + if (checksh.sh != dummysh) { + // A subface is not flipable. + continue; } } - } - } - - // An edge (flipedge) is going to be flipped. - // Do not flip it it is a subsegment. - tsspivot(&flipedge, &checkseg); - if (checkseg.sh == dummysh) { - symedge(flipedge, symface); - if (copflag == 0) { - // Check if a 3-to-2 flip is possible. - tfnext(flipedge, neighface); - if (neighface.tet != dummytet) { - // Check if neighface is a subface. - tspivot(neighface, neighsh); - if (neighsh.sh == dummysh) { - tfnext(symface, symneighface); - if (neighface.tet == symneighface.tet) { - // symneighface should not be a subface. Check it. - tspivot(symneighface, symneighsh); - assert(symneighsh.sh == dummysh); - // Found a 3-to-2 flip. - flip32(&flipedge, flipqueue); + flip23(&flipface, flipqueue); + } else if (ori < 0.0) { + // The edge (a, b) is non-convex, check for a 3-2 flip. + fnext(flipface, symface); + symself(symface); + if (oppo(symface) == pe) { + // Only three tets adjoining this edge. + if (checksubfaces) { + tsspivot(&flipface, &checkseg); + if (checkseg.sh != dummysh) { + // A subsegment is not flipable. + continue; } - } else { - // neighsh is a subface. Check a potential 4-to-4 flip. - tfnext(symface, symneighface); - tspivot(symneighface, symneighsh); - if (symneighsh.sh != dummysh) { - if (oppo(neighface) == oppo(symneighface)) { - // Found a 4-to-4 flip. - flip22(&flipedge, flipqueue); + } else if (checksubsegs) { + tsspivot1(flipface, checkseg); + if (checkseg.sh != dummysh) { + if (b->verbose > 2) { + printf(" Queuing missing segment (%d, %d).\n", + pointmark(org(flipface)), pointmark(dest(flipface))); } + misseg = (badface *) misseglist->append(NULL); + misseg->ss = checkseg; + misseg->forg = sorg(checkseg); + misseg->fdest = sdest(checkseg); + // Detach all tets having this seg. + starttet = flipface; + adjustedgering(starttet, CCW); + fnextself(starttet); + spintet = starttet; + hitbdry = 0; + do { + tssdissolve1(spintet); + if (!fnextself(spintet)) { + hitbdry++; + if (hitbdry < 2) { + esym(starttet, spintet); + if (!fnextself(spintet)) { + hitbdry++; + } + } + } + } while ((apex(spintet) != apex(starttet)) && (hitbdry < 2)); } - } - } else { - // neightface is a hull face. Since flipedge is not a segment - // and this edge is locally non-convex. - tfnext(symface, symneighface); - // symneighface should also be a hull face. - if (symneighface.tet == dummytet) { - // Force a 2-to-2 flip (recovery of Delaunay). - flip22(&flipedge, flipqueue); - } + } // if (checksubfaces) + flip32(&flipface, flipqueue); } } else { - // Check if a 2-to-2 or 4-to-4 flip is possible. - tfnext(flipedge, neighface); - tfnext(symface, symneighface); - if (neighface.tet != dummytet) { - if (symneighface.tet != dummytet) { - if (oppo(neighface) == oppo(symneighface)) { - // Found a 4-to-4 flip. - flip22(&flipedge, flipqueue); - } + // Four points (a, b, d, e) are coplanar. + fnext(flipface, symface); + if (fnextself(symface)) { + // Check for a 4-4 flip. + fnextself(symface); + if (apex(symface) == pe) { + if (checksubfaces) { + tsspivot(&flipface, &checkseg); + if (checkseg.sh != dummysh) { + // A subsegment is not flippable. + continue; + } + } else if (checksubsegs) { + tsspivot1(flipface, checkseg); + if (checkseg.sh != dummysh) { + if (b->verbose > 2) { + printf(" Queuing missing segment (%d, %d).\n", + pointmark(org(flipface)), pointmark(dest(flipface))); + } + misseg = (badface *) misseglist->append(NULL); + misseg->ss = checkseg; + misseg->forg = sorg(checkseg); + misseg->fdest = sdest(checkseg); + // Detach all tets having this seg. + starttet = flipface; + adjustedgering(starttet, CCW); + fnextself(starttet); + spintet = starttet; + hitbdry = 0; + do { + tssdissolve1(spintet); + if (!fnextself(spintet)) { + hitbdry++; + if (hitbdry < 2) { + esym(starttet, spintet); + if (!fnextself(spintet)) { + hitbdry++; + } + } + } + } while ((apex(spintet) != apex(starttet)) && + (hitbdry < 2)); + } + } // if (checksubfaces) + flip22(&flipface, flipqueue); } } else { - if (symneighface.tet == dummytet) { - // Found a 2-to-2 flip. - flip22(&flipedge, flipqueue); + // Check for a 2-2 flip. + esym(flipface, symface); + fnextself(symface); + symself(symface); + if (symface.tet == dummytet) { + if (checksubfaces) { + tsspivot(&flipface, &checkseg); + if (checkseg.sh != dummysh) { + // A subsegment is not flipable. + continue; + } + } else if (checksubsegs) { + tsspivot1(flipface, checkseg); + if (checkseg.sh != dummysh) { + if (b->verbose > 2) { + printf(" Queuing missing segment (%d, %d).\n", + pointmark(org(flipface)), pointmark(dest(flipface))); + } + misseg = (badface *) misseglist->append(NULL); + misseg->ss = checkseg; + misseg->forg = sorg(checkseg); + misseg->fdest = sdest(checkseg); + // Detach all tets having this seg. + starttet = flipface; + adjustedgering(starttet, CCW); + fnextself(starttet); + spintet = starttet; + hitbdry = 0; + do { + tssdissolve1(spintet); + if (!fnextself(spintet)) { + hitbdry++; + if (hitbdry < 2) { + esym(starttet, spintet); + if (!fnextself(spintet)) { + hitbdry++; + } + } + } + } while ((apex(spintet) != apex(starttet)) && + (hitbdry < 2)); + } + } // if (checksubfaces) + flip22(&flipface, flipqueue); } } - } - } - - } // if (sign > 0) - } + } // if (ori > 0.0) + } // if (sign > 0.0) + } + } // !isdead(&qface->tt) } // while (!flipqueue->empty()) flipcount = flip23s + flip32s + flip22s + flip44s - flipcount; if (b->verbose > 1) { printf(" %ld flips.\n", flipcount); } - return flipcount; } /////////////////////////////////////////////////////////////////////////////// // // -// lawson() Perform lawson flips on non-Delaunay edges. // +// undoflip() Undo the most recent flip sequence induced by flip(). // +// // +// 'lastflip' is the stack of recently flipped faces. Walks through the list // +// of flips, in the reverse of the order in which they were done, and undoes // +// them. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::undoflip(badface *lastflip) +{ + enum fliptype fc; + + while (lastflip != (badface *) NULL) { + // Get the right flipped face. + findface(&lastflip->tt, lastflip->forg, lastflip->fdest, lastflip->fapex); + fc = (enum fliptype) (int) lastflip->key; + switch (fc) { + case T23: + // The reverse operation of T23 is T32. + flip32(&lastflip->tt, NULL); + break; + case T32: + // The reverse operation of T32 is T23. + flip23(&lastflip->tt, NULL); + break; + case T22: + case T44: + // The reverse operation of T22 or T44 is again T22 or T44. + flip22(&lastflip->tt, NULL); + break; + default: // To omit compile warnings. + break; + } + // Go on and process the next transformation. + lastflip = lastflip->previtem; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// flipsub() Flip non-Delaunay edges in a queue of (coplanar) subfaces. // // // // Assumpation: Current triangulation T contains non-Delaunay edges (after // // inserting a point or performing a flip). Non-Delaunay edges are queued in // @@ -10972,7 +11621,7 @@ long tetgenmesh::lawson3d(queue* flipqueue) // // /////////////////////////////////////////////////////////////////////////////// -long tetgenmesh::lawson(queue* flipqueue) +long tetgenmesh::flipsub(queue* flipqueue) { badface *qedge; face flipedge, symedge; @@ -10981,22 +11630,16 @@ long tetgenmesh::lawson(queue* flipqueue) REAL vab[3], vac[3], vad[3]; REAL dot1, dot2, lac, lad; REAL sign, ori; - int edgeflips, maxflips; + int edgeflips; int i; if (b->verbose > 1) { - printf(" Lawson flip: %ld edges.\n", flipqueue->len()); + printf(" Start do edge queue: %ld edges.\n", flipqueue->len()); } - if (b->diagnose) { - maxflips = (int) ((flipqueue->len() + 1l) * 3l); - maxflips *= maxflips; - } else { - maxflips = -1; - } edgeflips = 0; - while (!flipqueue->empty() && maxflips != 0) { + while (!flipqueue->empty()) { qedge = (badface *) flipqueue->pop(); flipedge = qedge->ss; if (flipedge.sh == dummysh) continue; @@ -11044,14 +11687,9 @@ long tetgenmesh::lawson(queue* flipqueue) // Flip the non-Delaunay edge. flip22sub(&flipedge, flipqueue); edgeflips++; - if (maxflips > 0) maxflips--; } } - if (!maxflips && !b->quiet) { - printf("Warning: Maximal number of flips reached !\n"); - } - if (b->verbose > 1) { printf(" Total %d flips.\n", edgeflips); } @@ -11070,21 +11708,19 @@ long tetgenmesh::lawson(queue* flipqueue) // cdb) from their adjoining tets together with a 2-to-2 flip to transform // // two subfaces (abc and bad) into another two (dca and cdb). // // // -// 'adjtetlist[2]' returns the two new boundary faces (in tet) dca and cdb. // -// // // In mesh optimization. It is possible that ab is a segment and abcd is a // // sliver on the hull. Strip abcd will also delete the segment ab. // // // /////////////////////////////////////////////////////////////////////////////// -bool tetgenmesh::removetetbypeeloff(triface *striptet, triface *adjtetlist) +bool tetgenmesh::removetetbypeeloff(triface *striptet) { triface abcd, badc; triface dcacasing, cdbcasing; face abc, bad; face abseg; REAL ang; - + abcd = *striptet; adjustedgering(abcd, CCW); // Get the casing tets at the internal sides. @@ -11155,16 +11791,12 @@ bool tetgenmesh::removetetbypeeloff(triface *striptet, triface *adjtetlist) tsbond(dcacasing, abc); } } - + // Detach abcd from the two internal faces. dissolve(cdbcasing); dissolve(dcacasing); // Delete abcd. tetrahedrondealloc(abcd.tet); - - adjtetlist[0] = cdbcasing; - adjtetlist[1] = dcacasing; - return true; } @@ -11377,8 +12009,6 @@ bool tetgenmesh::removefacebyflip23(REAL *key, triface *abctetlist, // 'abtetlist' contains 3 tets sharing ab. Imaging that ab is perpendicular // // to the screen, where a lies in front of and b lies behind it. The 3 tets // // of the list are: [0]abce, [1]abdc, and [2]abed, respectively. // -// Comment: the edge ab is in CW edge ring of the three faces: abc, abd, and // -// abe. (2009-06-29) // // // // This routine forms two new tets that ab is not an edge of them. Save them // // in 'newtetlist', [0]dcea, [1]cdeb. Note that the new tets may not valid // @@ -11399,7 +12029,7 @@ bool tetgenmesh::removeedgebyflip32(REAL *key, triface *abtetlist, { triface dcea, cdeb; // new configuration. triface newfront, oldfront, adjfront; - face checksh, checkseg; + face checksh; point pa, pb, pc, pd, pe; REAL ori, cosmaxd, d1, d2; REAL attrib, volume; @@ -11431,15 +12061,6 @@ bool tetgenmesh::removeedgebyflip32(REAL *key, triface *abtetlist, } } - // Comment: This edge must not be fixed. It has been checked before. - if (doflip && (elemfliplist != NULL)) { - // Regist this flip. - if (!registerelemflip(T32, pa, pb, dummypoint, pc, pd, pe)) { - // Detected a potential flip loop. Don't do it. - return false; - } - } - if (doflip) { // Create the new tets. maketetrahedron(&dcea); @@ -11560,18 +12181,18 @@ bool tetgenmesh::removeedgebyflip32(REAL *key, triface *abtetlist, bool tetgenmesh::removeedgebytranNM(REAL *key, int n, triface *abtetlist, triface *newtetlist, point e1, point e2, queue *flipque) { - triface tmpabtetlist[21]; // Temporary max 20 tets configuration. + triface tmpabtetlist[9]; // Temporary max 9 tets configuration. triface newfront, oldfront, adjfront; face checksh; - point pa, pb, p[21]; + point pa, pb, p[10]; REAL ori, cosmaxd, d1, d2; REAL tmpkey; REAL attrib, volume; bool doflip, copflag, success; int i, j, k; - // Maximum 20 tets. - assert(n < 20); // n <= b->maxflipedgelinksize + // Maximum 10 tets. + assert(n <= 10); // Two points a and b are fixed. pa = org(abtetlist[0]); pb = dest(abtetlist[0]); @@ -11621,14 +12242,6 @@ bool tetgenmesh::removeedgebytranNM(REAL *key, int n, triface *abtetlist, doflip = *key < cosmaxd; // Can the local quality be improved? } } - if (doflip && (elemfliplist != NULL)) { - // Comment: The flipping face must be not fixed. This case has been - // tested during collecting the face ring of this edge. - // Do not flip this face if it has been registered before. - if (!registerelemflip(T23, pa, pb, p[0], p[1], p[n-1], dummypoint)) { - doflip = false; // Do not flip this face. - } - } if (doflip) { tmpkey = key != NULL ? *key : -1.0; // Create the two new tets. @@ -11775,10 +12388,6 @@ bool tetgenmesh::removeedgebytranNM(REAL *key, int n, triface *abtetlist, return true; } else { // The new configuration is bad, substitue back the old tets. - if (elemfliplist != NULL) { - // Remove the last registered 2-to-3 flip. - elemfliplist->objects--; - } for (j = 0; j < n; j++) { oldfront = abtetlist[(i + j) % n]; esymself(oldfront); @@ -11867,21 +12476,18 @@ bool tetgenmesh::removeedgebytranNM(REAL *key, int n, triface *abtetlist, bool tetgenmesh::removeedgebycombNM(REAL *key, int n, triface *abtetlist, int *n1, triface *bftetlist, triface *newtetlist, queue *flipque) { - triface tmpabtetlist[21]; + triface tmpabtetlist[11]; triface newfront, oldfront, adjfront; face checksh; - point pa, pb, p[21]; + point pa, pb, p[10]; REAL ori, tmpkey, tmpkey2; REAL attrib, volume; bool doflip, success; int twice, count; int i, j, k, m; - // point *ppt; // Used together with fixededgelist. - long bakflipcount; // Used for elemfliplist. - - // Maximal 20 tets in Star(ab). - assert(n < 20); // n <= b->maxflipedgelinksize + // Maximal 10 tets in Star(ab). + assert(n <= 10); // Do the following procedure twice, one for flipping edge b.p_0 and the // other for p_0.a which is symmetric to the first. @@ -11905,12 +12511,6 @@ bool tetgenmesh::removeedgebycombNM(REAL *key, int n, triface *abtetlist, tetalldihedral(pb, p[0], p[n - 1], p[1], NULL, &tmpkey, NULL); if (tmpkey < *key) ori = 0.0; } - if ((fixededgelist != NULL) && (ori <= 0.0)) { - // b.p_0 is either N32 or N44. Do not flip a fixed edge. - if (check4fixededge(pb, p[0])) { - ori = 1.0; // Do not flip this edge. Skip it. - } - } if (ori <= 0.0) { // b.p_0 is either N32 or N44. Try the 1st flipNM. bftetlist[0] = abtetlist[i]; @@ -11922,7 +12522,7 @@ bool tetgenmesh::removeedgebycombNM(REAL *key, int n, triface *abtetlist, *n1 = 0; do { // Is the list full? - if (*n1 == 20) break; + if (*n1 == 10) break; if (checksubfaces) { // Stop if a subface appears. tspivot(bftetlist[*n1], checksh); @@ -11931,13 +12531,10 @@ bool tetgenmesh::removeedgebycombNM(REAL *key, int n, triface *abtetlist, } } // Get the next tet at p_0.b. - if (!fnext(bftetlist[*n1], bftetlist[(*n1) + 1])) { - // Meet a boundary face. Do not flip. - doflip = false; break; - } + fnext(bftetlist[*n1], bftetlist[(*n1) + 1]); (*n1)++; } while (apex(bftetlist[*n1]) != pa); - // 2 < n1 <= b->maxflipedgelinksize. + // 2 <= n1 <= 10. if (doflip) { success = false; tmpkey = -1.0; // = acos(pi). @@ -11947,7 +12544,7 @@ bool tetgenmesh::removeedgebycombNM(REAL *key, int n, triface *abtetlist, // Three tets case. Try flip32. success = removeedgebyflip32(&tmpkey,bftetlist,newtetlist,flipque); m = 2; - } else if ((*n1 > 3) && (*n1 <= b->maxflipedgelinksize)) { + } else if ((*n1 > 3) && (*n1 < 7)) { // Four or more tets case. Try flipNM. success = removeedgebytranNM(&tmpkey, *n1, bftetlist, newtetlist, p[1], p[n - 1], flipque); @@ -12081,10 +12678,6 @@ bool tetgenmesh::removeedgebycombNM(REAL *key, int n, triface *abtetlist, } tmpkey2 = -1; if (key) tmpkey2 = *key; - if (elemfliplist != NULL) { - // Remember the current registered flips. - bakflipcount = elemfliplist->objects; - } if ((n - 1) == 3) { success = removeedgebyflip32(&tmpkey2, tmpabtetlist, &(newtetlist[m - 1]), flipque); @@ -12109,10 +12702,6 @@ bool tetgenmesh::removeedgebycombNM(REAL *key, int n, triface *abtetlist, return true; } else { // The new configuration is bad, substitue back the old tets. - if (elemfliplist != NULL) { - // Restore the registered flips. - elemfliplist->objects = bakflipcount; - } for (j = 0; j < n; j++) { oldfront = abtetlist[(i + j) % n]; esymself(oldfront); @@ -12387,6 +12976,74 @@ void tetgenmesh::splittetrahedron(point newpoint, triface* splittet, *splittet = abcv; } +/////////////////////////////////////////////////////////////////////////////// +// // +// unsplittetrahedron() Reverse the operation of inserting a point into a // +// tetrahedron, so as to remove the newly inserted // +// point from the mesh. // +// // +// Assume the origional tetrahedron is abcd, it was split by v into four // +// tetrahedra abcv, badv, cbdv, and acdv. 'splittet' represents face abc of // +// abcv (i.e., its opposite is v). // +// // +// Point v is removed by expanding abcv to abcd, deleting three tetrahedra // +// badv, cbdv and acdv. On return, point v is not deleted in this routine. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::unsplittetrahedron(triface* splittet) +{ + triface abcv, badv, cbdv, acdv; + triface oldabv, oldbcv, oldcav; + triface badcasing, cbdcasing, acdcasing; + face badsh, cbdsh, acdsh; + + abcv = *splittet; + adjustedgering(abcv, CCW); // for sure. + fnext(abcv, oldabv); + fnext(oldabv, badv); + esymself(badv); + enextfnext(abcv, oldbcv); + fnext(oldbcv, cbdv); + esymself(cbdv); + enext2fnext(abcv, oldcav); + fnext(oldcav, acdv); + esymself(acdv); + + if (b->verbose > 1) { + printf(" Removing point %d in tetrahedron (%d, %d, %d, %d).\n", + pointmark(oppo(abcv)), pointmark(org(abcv)), pointmark(dest(abcv)), + pointmark(apex(abcv)), pointmark(apex(badv))); + } + + sym(badv, badcasing); + tspivot(badv, badsh); + sym(cbdv, cbdcasing); + tspivot(cbdv, cbdsh); + sym(acdv, acdcasing); + tspivot(acdv, acdsh); + + // Expanding abcv to abcd. + setoppo(abcv, apex(badv)); + bond(oldabv, badcasing); + if (badsh.sh != dummysh) { + tsbond(oldabv, badsh); + } + bond(oldbcv, cbdcasing); + if (cbdsh.sh != dummysh) { + tsbond(oldbcv, cbdsh); + } + bond(oldcav, acdcasing); + if (acdsh.sh != dummysh) { + tsbond(oldcav, acdsh); + } + + // Delete the three split-out tetrahedra. + tetrahedrondealloc(badv.tet); + tetrahedrondealloc(cbdv.tet); + tetrahedrondealloc(acdv.tet); +} + /////////////////////////////////////////////////////////////////////////////// // // // splittetface() Insert a point on a face of a mesh. // @@ -12728,6 +13385,126 @@ void tetgenmesh::splittetface(point newpoint, triface* splittet, } } +/////////////////////////////////////////////////////////////////////////////// +// // +// unsplittetface() Reverse the operation of inserting a point on a face, // +// so as to remove the newly inserted point. // +// // +// Assume the original face is abc, the tetrahedron containing abc is abcd. // +// If abc is not a hull face, bace is the tetrahedron at the opposite of d. // +// After face abc was split by a point v, tetrahedron abcd had been split // +// into three tetrahedra, abvd, bcvd, cavd, and bace (if it exists) had been // +// split into bave, cbve, acve. 'splittet' represents abvd (its apex is v). // +// // +// Point v is removed by expanding abvd to abcd, deleting two tetrahedra // +// bcvd, cavd. Expanding bave(if it exists) to bace, deleting two tetrahedra // +// cbve, acve. If abv is a subface, routine unsplitsubface() will be called // +// to reverse the operation of splitting a subface. On completion, point v // +// is not deleted in this routine. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::unsplittetface(triface* splittet) +{ + triface abvd, bcvd, cavd, bave, cbve, acve; + triface oldbvd, oldvad, oldvbe, oldave; + triface bcdcasing, cadcasing, cbecasing, acecasing; + face bcdsh, cadsh, cbesh, acesh; + face abvsh; + bool mirrorflag; + + abvd = *splittet; + adjustedgering(abvd, CCW); // for sure. + enextfnext(abvd, oldbvd); + fnext(oldbvd, bcvd); + esymself(bcvd); + enextself(bcvd); + enext2fnext(abvd, oldvad); + fnext(oldvad, cavd); + esymself(cavd); + enext2self(cavd); + // Is there a second tetrahedron? + sym(abvd, bave); + mirrorflag = bave.tet != dummytet; + if (mirrorflag) { + findedge(&bave, dest(abvd), org(abvd)); + enextfnext(bave, oldave); + fnext(oldave, acve); + esymself(acve); + enextself(acve); + enext2fnext(bave, oldvbe); + fnext(oldvbe, cbve); + esymself(cbve); + enext2self(cbve); + } else { + // Unsplit a hull face decrease the number of boundary faces. + hullsize -= 2; + } + // Is there a subface at abv. + tspivot(abvd, abvsh); + if (abvsh.sh != dummysh) { + // Exists! Keep the edge ab of both handles be the same. + findedge(&abvsh, org(abvd), dest(abvd)); + } + + if (b->verbose > 1) { + printf(" Removing point %d on face (%d, %d, %d).\n", + pointmark(apex(abvd)), pointmark(org(abvd)), pointmark(dest(abvd)), + pointmark(dest(bcvd))); + } + + fnextself(bcvd); // bcvd has changed to bcdv. + sym(bcvd, bcdcasing); + tspivot(bcvd, bcdsh); + fnextself(cavd); // cavd has changed to cadv. + sym(cavd, cadcasing); + tspivot(cavd, cadsh); + if (mirrorflag) { + fnextself(acve); // acve has changed to acev. + sym(acve, acecasing); + tspivot(acve, acesh); + fnextself(cbve); // cbve has changed to cbev. + sym(cbve, cbecasing); + tspivot(cbve, cbesh); + } + + // Expand abvd to abcd. + setapex(abvd, dest(bcvd)); + bond(oldbvd, bcdcasing); + if (bcdsh.sh != dummysh) { + tsbond(oldbvd, bcdsh); + } + bond(oldvad, cadcasing); + if (cadsh.sh != dummysh) { + tsbond(oldvad, cadsh); + } + if (mirrorflag) { + // Expanding bave to bace. + setapex(bave, dest(acve)); + bond(oldave, acecasing); + if (acesh.sh != dummysh) { + tsbond(oldave, acesh); + } + bond(oldvbe, cbecasing); + if (cbesh.sh != dummysh) { + tsbond(oldvbe, cbesh); + } + } + + // Unsplit a subface if there exists. + if (abvsh.sh != dummysh) { + unsplitsubface(&abvsh); + } + + // Delete the split-out tetrahedra. + tetrahedrondealloc(bcvd.tet); + tetrahedrondealloc(cavd.tet); + if (mirrorflag) { + tetrahedrondealloc(acve.tet); + tetrahedrondealloc(cbve.tet); + } +} + /////////////////////////////////////////////////////////////////////////////// // // // splitsubface() Insert a point on a subface, split it into three. // @@ -12780,26 +13557,30 @@ void tetgenmesh::splitsubface(point newpoint, face* splitface, spivot(oldbc, bccasout); sspivot(oldbc, bc); if (bc.sh != dummysh) { - if (bccasout.sh != dummysh) { + if (oldbc.sh != bccasout.sh) { // 'oldbc' is not self-bonded. spinsh = bccasout; do { bccasin = spinsh; spivotself(spinsh); } while (spinsh.sh != oldbc.sh); + } else { + bccasout.sh = dummysh; } ssdissolve(oldbc); } spivot(oldca, cacasout); sspivot(oldca, ca); if (ca.sh != dummysh) { - if (cacasout.sh != dummysh) { + if (oldca.sh != cacasout.sh) { // 'oldca' is not self-bonded. spinsh = cacasout; do { cacasin = spinsh; spivotself(spinsh); } while (spinsh.sh != oldca.sh); + } else { + cacasout.sh = dummysh; } ssdissolve(oldca); } @@ -12839,7 +13620,7 @@ void tetgenmesh::splitsubface(point newpoint, face* splitface, sbond1(bcv, bccasout); } else { // Bond 'bcv' to itsself. - sdissolve(bcv); // sbond(bcv, bcv); + sbond(bcv, bcv); } ssbond(bcv, bc); } else { @@ -12851,7 +13632,7 @@ void tetgenmesh::splitsubface(point newpoint, face* splitface, sbond1(cav, cacasout); } else { // Bond 'cav' to itself. - sdissolve(cav); // sbond(cav, cav); + sbond(cav, cav); } ssbond(cav, ca); } else { @@ -12924,8 +13705,110 @@ void tetgenmesh::splitsubface(point newpoint, face* splitface, enqueueflipedge(cav, flipqueue); } - // Set the return handle be abv. - *splitface = abv; + // Set the return handle be abv. + *splitface = abv; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// unsplitsubface() Reverse the operation of inserting a point on a // +// subface, so as to remove the newly inserted point. // +// // +// Assume the original subface is abc, it was split by a point v into three // +// subfaces abv, bcv and cav. 'splitsh' represents abv. // +// // +// To remove point v is to expand abv to abc, delete bcv and cav. If edge bc // +// or ca is a subsegment, the connection at a subsegment is a subface link, // +// '-casin' and '-casout' are used to save the predecessor and successor of // +// bcv or cav. On completion, point v is not deleted in this routine. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::unsplitsubface(face* splitsh) +{ + face abv, bcv, cav; + face oldbv, oldva, bc, ca, spinsh; + face bccasin, bccasout, cacasin, cacasout; + + abv = *splitsh; + senext(abv, oldbv); + spivot(oldbv, bcv); + if (sorg(bcv) != sdest(oldbv)) { + sesymself(bcv); + } + senextself(bcv); + senext2(abv, oldva); + spivot(oldva, cav); + if (sorg(cav) != sdest(oldva)) { + sesymself(cav); + } + senext2self(cav); + + if (b->verbose > 1) { + printf(" Removing point %d on subface (%d, %d, %d).\n", + pointmark(sapex(abv)), pointmark(sorg(abv)), pointmark(sdest(abv)), + pointmark(sdest(bcv))); + } + + spivot(bcv, bccasout); + sspivot(bcv, bc); + if (bc.sh != dummysh) { + if (bcv.sh != bccasout.sh) { + // 'bcv' is not self-bonded. + spinsh = bccasout; + do { + bccasin = spinsh; + spivotself(spinsh); + } while (spinsh.sh != bcv.sh); + } else { + bccasout.sh = dummysh; + } + } + spivot(cav, cacasout); + sspivot(cav, ca); + if (ca.sh != dummysh) { + if (cav.sh != cacasout.sh) { + // 'cav' is not self-bonded. + spinsh = cacasout; + do { + cacasin = spinsh; + spivotself(spinsh); + } while (spinsh.sh != cav.sh); + } else { + cacasout.sh = dummysh; + } + } + + // Expand abv to abc. + setsapex(abv, sdest(bcv)); + if (bc.sh != dummysh) { + if (bccasout.sh != dummysh) { + sbond1(bccasin, oldbv); + sbond1(oldbv, bccasout); + } else { + // Bond 'oldbv' to itself. + sbond(oldbv, oldbv); + } + ssbond(oldbv, bc); + } else { + sbond(oldbv, bccasout); + } + if (ca.sh != dummysh) { + if (cacasout.sh != dummysh) { + sbond1(cacasin, oldva); + sbond1(oldva, cacasout); + } else { + // Bond 'oldva' to itself. + sbond(oldva, oldva); + } + ssbond(oldva, ca); + } else { + sbond(oldva, cacasout); + } + + // Delete two split-out subfaces. + shellfacedealloc(subfaces, bcv.sh); + shellfacedealloc(subfaces, cav.sh); } /////////////////////////////////////////////////////////////////////////////// @@ -12952,7 +13835,7 @@ void tetgenmesh::splitsubface(point newpoint, face* splitface, // // /////////////////////////////////////////////////////////////////////////////// -bool tetgenmesh::splittetedge(point newpoint, triface* splittet, +void tetgenmesh::splittetedge(point newpoint, triface* splittet, queue* flipqueue) { triface *bots, *newtops; @@ -12994,7 +13877,275 @@ bool tetgenmesh::splittetedge(point newpoint, triface* splittet, // It may happen that some tetrahedra containing ab (a subsegment) are // completely disconnected with others. If it happens, use the face // link of ab to cross the boundary. - while (true) { + while (true) { + if (!fnextself(spintet)) { + // Meet a boundary, walk through it. + hitbdry ++; + tspivot(spintet, spinsh); +#ifdef SELF_CHECK + assert(spinsh.sh != dummysh); +#endif + findedge(&spinsh, pa, pb); + sfnextself(spinsh); + stpivot(spinsh, spintet); +#ifdef SELF_CHECK + assert(spintet.tet != dummytet); +#endif + findedge(&spintet, pa, pb); + // Remember this position (hull face) in 'splittet'. + *splittet = spintet; + // Split two hull faces increase the hull size; + hullsize += 2; + } + if (apex(spintet) == n1) break; + wrapcount ++; + } + if (hitbdry > 0) { + wrapcount -= hitbdry; + } + } else { + // All the tetrahedra containing ab are connected together. If there + // are subfaces, 'splitsh' keeps one of them. + splitsh.sh = dummysh; + while (hitbdry < 2) { + if (checksubfaces && splitsh.sh == dummysh) { + tspivot(spintet, splitsh); + } + if (fnextself(spintet)) { + if (apex(spintet) == n1) break; + wrapcount++; + } else { + hitbdry ++; + if (hitbdry < 2) { + esym(*splittet, spintet); + } + } + } + if (hitbdry > 0) { + // ab is on the hull. + wrapcount -= 1; + // 'spintet' now is a hull face, inverse its edge direction. + esym(spintet, *splittet); + // Split two hull faces increases the number of hull faces. + hullsize += 2; + } + } + + // Make arrays of updating (bot, oldtop) and new (newtop) tetrahedra. + bots = new triface[wrapcount]; + newtops = new triface[wrapcount]; + // Spin around ab, gather tetrahedra and set up new tetrahedra. + spintet = *splittet; + for (i = 0; i < wrapcount; i++) { + // Get 'bots[i] = an1n2b'. + enext2fnext(spintet, bots[i]); + esymself(bots[i]); + // Create 'newtops[i]'. + maketetrahedron(&(newtops[i])); + // Go to the next. + fnextself(spintet); + if (checksubfaces && abseg.sh != dummysh) { + if (!issymexist(&spintet)) { + // We meet a hull face, walk through it. + tspivot(spintet, spinsh); +#ifdef SELF_CHECK + assert(spinsh.sh != dummysh); +#endif + findedge(&spinsh, pa, pb); + sfnextself(spinsh); + stpivot(spinsh, spintet); +#ifdef SELF_CHECK + assert(spintet.tet != dummytet); +#endif + findedge(&spintet, pa, pb); + } + } + } + + // Set the vertices of updated and new tetrahedra. + for (i = 0; i < wrapcount; i++) { + // Update 'bots[i] = an1n2v'. + setoppo(bots[i], newpoint); + // Set 'newtops[i] = bn2n1v'. + n1 = dest(bots[i]); + n2 = apex(bots[i]); + // Set 'newtops[i]'. + setorg(newtops[i], pb); + setdest(newtops[i], n2); + setapex(newtops[i], n1); + setoppo(newtops[i], newpoint); + // Set the element attributes of a new tetrahedron. + for (j = 0; j < in->numberoftetrahedronattributes; j++) { + attrib = elemattribute(bots[i].tet, j); + setelemattribute(newtops[i].tet, j, attrib); + } + if (b->varvolume) { + // Set the area constraint of a new tetrahedron. + volume = volumebound(bots[i].tet); + setvolumebound(newtops[i].tet, volume); + } +#ifdef SELF_CHECK + // Make sure no inversed tetrahedron has been created. + // volume = orient3d(pa, n1, n2, newpoint); + // if (volume >= 0.0) { + // printf("Internal error in splittetedge(): volume = %.12g.\n", volume); + // } + // volume = orient3d(pb, n2, n1, newpoint); + // if (volume >= 0.0) { + // printf("Internal error in splittetedge(): volume = %.12g.\n", volume); + // } +#endif + } + + // Bond newtops to topcasings and bots. + for (i = 0; i < wrapcount; i++) { + // Get 'oldtop = n1n2va' from 'bots[i]'. + enextfnext(bots[i], oldtop); + sym(oldtop, topcasing); + bond(newtops[i], topcasing); + if (checksubfaces) { + tspivot(oldtop, topsh); + if (topsh.sh != dummysh) { + tsdissolve(oldtop); + tsbond(newtops[i], topsh); + } + } + enextfnext(newtops[i], tmpbond0); + bond(oldtop, tmpbond0); + } + // Bond between newtops. + fnext(newtops[0], tmpbond0); + enext2fnext(bots[0], spintet); + for (i = 1; i < wrapcount; i ++) { + if (issymexist(&spintet)) { + enext2fnext(newtops[i], tmpbond1); + bond(tmpbond0, tmpbond1); + } + fnext(newtops[i], tmpbond0); + enext2fnext(bots[i], spintet); + } + // Bond the last to the first if no boundary. + if (issymexist(&spintet)) { + enext2fnext(newtops[0], tmpbond1); + bond(tmpbond0, tmpbond1); + } + if (checksubsegs) { + for (i = 0; i < wrapcount; i++) { + enextfnext(bots[i], worktet); // edge n1->n2. + tsspivot1(worktet, n1n2seg); + if (n1n2seg.sh != dummysh) { + enext(newtops[i], tmpbond0); + tssbond1(tmpbond0, n1n2seg); + } + enextself(worktet); // edge n2->v ==> n2->b + tsspivot1(worktet, n2vseg); + if (n2vseg.sh != dummysh) { + tssdissolve1(worktet); + tssbond1(newtops[i], n2vseg); + } + enextself(worktet); // edge v->n1 ==> b->n1 + tsspivot1(worktet, n1vseg); + if (n1vseg.sh != dummysh) { + tssdissolve1(worktet); + enext2(newtops[i], tmpbond0); + tssbond1(tmpbond0, n1vseg); + } + } + } + + // Is there exist subfaces and subsegment need to be split? + if (checksubfaces) { + if (abseg.sh != dummysh) { + // A subsegment needs be split. + spivot(abseg, splitsh); +#ifdef SELF_CHECK + assert(splitsh.sh != dummysh); +#endif + } + if (splitsh.sh != dummysh) { + // Split subfaces (and subsegment). + findedge(&splitsh, pa, pb); + splitsubedge(newpoint, &splitsh, (queue *) NULL); + } + } + + if (b->verbose > 3) { + for (i = 0; i < wrapcount; i++) { + printf(" Updating bots[%i] ", i); + printtet(&(bots[i])); + printf(" Creating newtops[%i] ", i); + printtet(&(newtops[i])); + } + } + + if (flipqueue != (queue *) NULL) { + for (i = 0; i < wrapcount; i++) { + enqueueflipface(bots[i], flipqueue); + enqueueflipface(newtops[i], flipqueue); + } + } + + // Set the return handle be avn1n2. It is got by transforming from + // 'bots[0]' (which is an1n2v). + fnext(bots[0], spintet); // spintet is an1vn2. + esymself(spintet); // spintet is n1avn2. + enextself(spintet); // spintet is avn1n2. + *splittet = spintet; + + delete [] bots; + delete [] newtops; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// unsplittetedge() Reverse the operation of splitting an edge, so as to // +// remove the newly inserted point. // +// // +// Assume the original edge is ab, the tetrahedron containing ab is abn1n2. // +// After ab was split by a point v, every tetrahedron containing ab (e.g., // +// abn1n2) has been split into two (e.g., an1n2v and bn2n1v). 'splittet' // +// represents avn1n2 (i.e., its destination is v). // +// // +// To remove point v is to expand each split tetrahedron containing ab (e.g.,// +// (avn1n2 to abn1n2), then delete the redundant one(e.g., vbn1n2). If there // +// exists any subface around ab, routine unsplitsubedge() will be called to // +// reverse the operation of splitting a edge (or a subsegment) of subfaces. // +// On completion, point v is not deleted in this routine. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::unsplittetedge(triface* splittet) +{ + triface *bots, *newtops; + triface oldtop, topcasing; + triface spintet; + face avseg, splitsh, topsh, spinsh; + point pa, pv, n1; + int wrapcount, hitbdry; + int i; + + spintet = *splittet; + pa = org(spintet); + pv = dest(spintet); + if (checksubfaces) { + // Is there a subsegment need to be unsplit together? + tsspivot(splittet, &avseg); + if (avseg.sh != dummysh) { + // The subsegment's direction should conform to 'splittet'. + if (sorg(avseg) != pa) { + sesymself(avseg); + } + } + } + + n1 = apex(spintet); + hitbdry = 0; + wrapcount = 1; + if (checksubfaces && avseg.sh != dummysh) { + // It may happen that some tetrahedra containing ab (a subsegment) are + // completely disconnected with others. If it happens, use the face + // link of ab to cross the boundary. + while (true) { if (!fnextself(spintet)) { // Meet a boundary, walk through it. hitbdry ++; @@ -13002,13 +14153,13 @@ bool tetgenmesh::splittetedge(point newpoint, triface* splittet, #ifdef SELF_CHECK assert(spinsh.sh != dummysh); #endif - findedge(&spinsh, pa, pb); + findedge(&spinsh, pa, pv); sfnextself(spinsh); stpivot(spinsh, spintet); #ifdef SELF_CHECK assert(spintet.tet != dummytet); #endif - findedge(&spintet, pa, pb); + findedge(&spintet, pa, pv); // Remember this position (hull face) in 'splittet'. *splittet = spintet; // Split two hull faces increase the hull size; @@ -13051,185 +14202,79 @@ bool tetgenmesh::splittetedge(point newpoint, triface* splittet, // Make arrays of updating (bot, oldtop) and new (newtop) tetrahedra. bots = new triface[wrapcount]; newtops = new triface[wrapcount]; - // Spin around ab, gather tetrahedra and set up new tetrahedra. + // Spin around av, gather tetrahedra and set up new tetrahedra. spintet = *splittet; for (i = 0; i < wrapcount; i++) { - // Get 'bots[i] = an1n2b'. + // Get 'bots[i] = an1n2v'. enext2fnext(spintet, bots[i]); esymself(bots[i]); - // Create 'newtops[i]'. - maketetrahedron(&(newtops[i])); + // Get 'oldtop = n1n2va'. + enextfnext(bots[i], oldtop); + // Get 'newtops[i] = 'bn1n2v' + fnext(oldtop, newtops[i]); // newtop = n1n2bv + esymself(newtops[i]); // newtop = n2n1bv + enext2self(newtops[i]); // newtop = bn2n1v // Go to the next. fnextself(spintet); - if (checksubfaces && abseg.sh != dummysh) { + if (checksubfaces && avseg.sh != dummysh) { if (!issymexist(&spintet)) { // We meet a hull face, walk through it. tspivot(spintet, spinsh); #ifdef SELF_CHECK assert(spinsh.sh != dummysh); #endif - findedge(&spinsh, pa, pb); + findedge(&spinsh, pa, pv); sfnextself(spinsh); stpivot(spinsh, spintet); #ifdef SELF_CHECK assert(spintet.tet != dummytet); #endif - findedge(&spintet, pa, pb); + findedge(&spintet, pa, pv); } } } - - // Set the vertices of updated and new tetrahedra. - for (i = 0; i < wrapcount; i++) { - // Update 'bots[i] = an1n2v'. - setoppo(bots[i], newpoint); - // Set 'newtops[i] = bn2n1v'. - n1 = dest(bots[i]); - n2 = apex(bots[i]); - // Set 'newtops[i]'. - setorg(newtops[i], pb); - setdest(newtops[i], n2); - setapex(newtops[i], n1); - setoppo(newtops[i], newpoint); - // Set the element attributes of a new tetrahedron. - for (j = 0; j < in->numberoftetrahedronattributes; j++) { - attrib = elemattribute(bots[i].tet, j); - setelemattribute(newtops[i].tet, j, attrib); - } - if (b->varvolume) { - // Set the area constraint of a new tetrahedron. - volume = volumebound(bots[i].tet); - setvolumebound(newtops[i].tet, volume); - } -//#ifdef SELF_CHECK - // Make sure no inversed tetrahedron has been created. - volume = orient3d(pa, n1, n2, newpoint); - if (volume >= 0.0) { - //printf("Internal error in splittetedge(): volume = %.12g.\n", volume); - break; - } - volume = orient3d(pb, n2, n1, newpoint); - if (volume >= 0.0) { - //printf("Internal error in splittetedge(): volume = %.12g.\n", volume); - break; - } -//#endif - } - if (i < wrapcount) { - // Do not insert this point. It will result inverted or degenerated tet. - // Restore have updated tets in "bots". - for (; i >= 0; i--) { - setoppo(bots[i], pb); - } - // Deallocate tets in "newtops". - for (i = 0; i < wrapcount; i++) { - tetrahedrondealloc(newtops[i].tet); - } - delete [] newtops; - delete [] bots; - return false; + if (b->verbose > 1) { + printf(" Removing point %d from edge (%d, %d).\n", + pointmark(oppo(bots[0])), pointmark(org(bots[0])), + pointmark(org(newtops[0]))); } - // Bond newtops to topcasings and bots. for (i = 0; i < wrapcount; i++) { - // Get 'oldtop = n1n2va' from 'bots[i]'. + // Expand an1n2v to an1n2b. + setoppo(bots[i], org(newtops[i])); + // Get 'oldtop = n1n2va' from 'bot[i]'. enextfnext(bots[i], oldtop); - sym(oldtop, topcasing); - bond(newtops[i], topcasing); + // Get 'topcasing' from 'newtop[i]' + sym(newtops[i], topcasing); + // Bond them. + bond(oldtop, topcasing); if (checksubfaces) { - tspivot(oldtop, topsh); + tspivot(newtops[i], topsh); if (topsh.sh != dummysh) { - tsdissolve(oldtop); - tsbond(newtops[i], topsh); - } - } - enextfnext(newtops[i], tmpbond0); - bond(oldtop, tmpbond0); - } - // Bond between newtops. - fnext(newtops[0], tmpbond0); - enext2fnext(bots[0], spintet); - for (i = 1; i < wrapcount; i ++) { - if (issymexist(&spintet)) { - enext2fnext(newtops[i], tmpbond1); - bond(tmpbond0, tmpbond1); - } - fnext(newtops[i], tmpbond0); - enext2fnext(bots[i], spintet); - } - // Bond the last to the first if no boundary. - if (issymexist(&spintet)) { - enext2fnext(newtops[0], tmpbond1); - bond(tmpbond0, tmpbond1); - } - if (checksubsegs) { - for (i = 0; i < wrapcount; i++) { - enextfnext(bots[i], worktet); // edge n1->n2. - tsspivot1(worktet, n1n2seg); - if (n1n2seg.sh != dummysh) { - enext(newtops[i], tmpbond0); - tssbond1(tmpbond0, n1n2seg); - } - enextself(worktet); // edge n2->v ==> n2->b - tsspivot1(worktet, n2vseg); - if (n2vseg.sh != dummysh) { - tssdissolve1(worktet); - tssbond1(newtops[i], n2vseg); - } - enextself(worktet); // edge v->n1 ==> b->n1 - tsspivot1(worktet, n1vseg); - if (n1vseg.sh != dummysh) { - tssdissolve1(worktet); - enext2(newtops[i], tmpbond0); - tssbond1(tmpbond0, n1vseg); + tsbond(oldtop, topsh); } } + // Delete the tetrahedron above an1n2v. + tetrahedrondealloc(newtops[i].tet); } - // Is there exist subfaces and subsegment need to be split? + // If there exists any subface, unsplit them. if (checksubfaces) { - if (abseg.sh != dummysh) { - // A subsegment needs be split. - spivot(abseg, splitsh); + if (avseg.sh != dummysh) { + spivot(avseg, splitsh); #ifdef SELF_CHECK assert(splitsh.sh != dummysh); #endif } if (splitsh.sh != dummysh) { - // Split subfaces (and subsegment). - findedge(&splitsh, pa, pb); - splitsubedge(newpoint, &splitsh, (queue *) NULL); - } - } - - if (b->verbose > 3) { - for (i = 0; i < wrapcount; i++) { - printf(" Updating bots[%i] ", i); - printtet(&(bots[i])); - printf(" Creating newtops[%i] ", i); - printtet(&(newtops[i])); - } - } - - if (flipqueue != (queue *) NULL) { - for (i = 0; i < wrapcount; i++) { - enqueueflipface(bots[i], flipqueue); - enqueueflipface(newtops[i], flipqueue); + findedge(&splitsh, pa, pv); + unsplitsubedge(&splitsh); } } - // Set the return handle be avn1n2. It is got by transforming from - // 'bots[0]' (which is an1n2v). - fnext(bots[0], spintet); // spintet is an1vn2. - esymself(spintet); // spintet is n1avn2. - enextself(spintet); // spintet is avn1n2. - *splittet = spintet; - delete [] bots; delete [] newtops; - - return true; } /////////////////////////////////////////////////////////////////////////////// @@ -13290,15 +14335,13 @@ void tetgenmesh::splitsubedge(point newpoint, face* splitsh, queue* flipqueue) if (sorg(spinabc) != pa) { sesymself(spinabc); } - // Unmark the face for splitting (used for refinement) 2009-08-17. - sunmarktest(spinabc); // Save old configuration at edge bc, if bc has a subsegment, save the // face link of it and dissolve it from bc. senext(spinabc, oldbc); spivot(oldbc, bccasout); sspivot(oldbc, bc); if (bc.sh != dummysh) { - if (bccasout.sh != dummysh) { + if (spinabc.sh != bccasout.sh) { // 'spinabc' is not self-bonded. spinsh = bccasout; do { @@ -13343,7 +14386,7 @@ void tetgenmesh::splitsubedge(point newpoint, face* splitsh, queue* flipqueue) sbond1(vbc, bccasout); } else { // Bond 'vbc' to itself. - sdissolve(vbc); // sbond(vbc, vbc); + sbond(vbc, vbc); } ssbond(vbc, bc); } else { @@ -13395,8 +14438,6 @@ void tetgenmesh::splitsubedge(point newpoint, face* splitsh, queue* flipqueue) // Split ab if it is a subsegment. if (ab.sh != dummysh) { - // Unmark the segment for mesh optimization. 2009-08-17. - sunmarktest(ab); // Update subsegment ab to av. av = ab; setsdest(av, newpoint); @@ -13447,9 +14488,9 @@ void tetgenmesh::splitsubedge(point newpoint, face* splitsh, queue* flipqueue) ssbond(vbc, vb); // Go to the next. spivotself(spinabc); - if (spinabc.sh == dummysh) { - break; // There's only one facet at the segment.rr - } +#ifdef SELF_CHECK + assert(spinabc.sh != dummysh); +#endif } while (spinabc.sh != startabc.sh); } @@ -13558,99 +14599,409 @@ void tetgenmesh::splitsubedge(point newpoint, face* splitsh, queue* flipqueue) /////////////////////////////////////////////////////////////////////////////// // // -// formstarpolyhedron() Get the star ployhedron of a point 'pt'. // +// unsplitsubedge() Reverse the operation of splitting an edge of subface,// +// so as to remove a point from the edge. // // // -// The polyhedron P is formed by faces of tets having 'pt' as a vertex. If // -// 'complete' is TRUE, P is the complete star of 'pt'. Otherwise, P is boun- // -// ded by subfaces, i.e. P is only part of the star of 'pt'. // +// Assume the original edge is ab, the subface containing it is abc. It was // +// split by a point v into avc, and vbc. 'splitsh' represents avc, further- // +// more, if av is a subsegment, av should be the zero version of the split // +// subsegment (i.e., av.shver = 0), so we are sure that the destination (v) // +// of both avc and av is the deleting point. // // // -// 'tetlist' T returns the tets, it has one of such tets on input. Moreover, // -// if t is in T, then oppo(t) = p. Topologically, T is the star of p; and // -// the faces of T is the link of p. 'verlist' V returns the vertices of T. // +// To remove point v is to expand avc to abc, delete vbc, do the same for // +// other subfaces containing av and vb. If av and vb are subsegments, expand // +// av to ab, delete vb. On completion, point v is not deleted. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::formstarpolyhedron(point pt, list* tetlist, list* verlist, - bool complete) +void tetgenmesh::unsplitsubedge(face* splitsh) { - triface starttet, neightet; - face checksh; - point ver[3]; - int idx, i, j; + face startavc, spinavc, spinbcv; + face oldvc, bccasin, bccasout, spinsh; + face av, vb, bc; + point pa, pv, pb; - // Get a tet t containing p. - starttet = * (triface *)(* tetlist)[0]; - // Let oppo(t) = p. - for (starttet.loc = 0; starttet.loc < 4; starttet.loc++) { - if (oppo(starttet) == pt) break; + startavc = *splitsh; + sspivot(startavc, av); + if (av.sh != dummysh) { + // Orient the direction of subsegment to conform the subface. + if (sorg(av) != sorg(startavc)) { + sesymself(av); + } +#ifdef SELF_CHECK + assert(av.shver == 0); +#endif } - assert(starttet.loc < 4); - // Add t into T. - * (triface *)(* tetlist)[0] = starttet; - infect(starttet); - if (verlist != (list *) NULL) { - // Add three verts of t into V. - ver[0] = org(starttet); - ver[1] = dest(starttet); - ver[2] = apex(starttet); - for (i = 0; i < 3; i++) { - // Mark the vert by inversing the index of the vert. - idx = pointmark(ver[i]); - setpointmark(ver[i], -idx - 1); // -1 to distinguish the zero. - verlist->append(&(ver[i])); + senext(startavc, oldvc); + spivot(oldvc, vb); // vb is subface vbc + if (sorg(vb) != sdest(oldvc)) { + sesymself(vb); + } + senextself(vb); + pa = sorg(startavc); + pv = sdest(startavc); + pb = sdest(vb); + + if (b->verbose > 1) { + printf(" Removing point %d from subedge (%d, %d).\n", + pointmark(pv), pointmark(pa), pointmark(pb)); + } + + // Spin arround av, unsplit every subface containing av. + spinavc = startavc; + do { + // Adjust spinavc be edge av. + if (sorg(spinavc) != pa) { + sesymself(spinavc); + } + // Save old configuration at edge bc, if bc has a subsegment, save the + // face link of it. + senext(spinavc, oldvc); + spivot(oldvc, spinbcv); + if (sorg(spinbcv) != sdest(oldvc)) { + sesymself(spinbcv); + } + senext2self(spinbcv); + spivot(spinbcv, bccasout); + sspivot(spinbcv, bc); + if (bc.sh != dummysh) { + if (spinbcv.sh != bccasout.sh) { + // 'spinbcv' is not self-bonded. + spinsh = bccasout; + do { + bccasin = spinsh; + spivotself(spinsh); + } while (spinsh.sh != spinbcv.sh); + } else { + bccasout.sh = dummysh; + } + } + // Expand avc to abc. + setsdest(spinavc, pb); + if (bc.sh != dummysh) { + if (bccasout.sh != dummysh) { + sbond1(bccasin, oldvc); + sbond1(oldvc, bccasout); + } else { + // Bond 'oldbc' to itself. + sbond(oldvc, oldvc); + } + ssbond(oldvc, bc); + } else { + sbond(oldvc, bccasout); + } + // Delete bcv. + shellfacedealloc(subfaces, spinbcv.sh); + // Go to next subface at edge av. + spivotself(spinavc); + if (spinavc.sh == dummysh) { + break; // 'av' is a hull edge. + } + } while (spinavc.sh != startavc.sh); + + // Is there a subsegment need to be unsplit? + if (av.sh != dummysh) { + senext(av, oldvc); // Re-use oldvc. + spivot(oldvc, vb); + vb.shver = 0; +#ifdef SELF_CHECK + assert(sdest(av) == sorg(vb)); +#endif + senext(vb, spinbcv); // Re-use spinbcv. + spivot(spinbcv, bccasout); + // Expand av to ab. + setsdest(av, pb); + sbond(oldvc, bccasout); + // Delete vb. + shellfacedealloc(subsegs, vb.sh); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// insertsite() Insert a point into the mesh. // +// // +// The 'newpoint' is located. If 'searchtet->tet' is not NULL, the search // +// for the containing tetrahedron begins from 'searchtet', otherwise, a full // +// point location procedure is called. If 'newpoint' is found inside a // +// tetrahedron, the tetrahedron is split into four (by splittetrahedron()); // +// if 'newpoint' lies on a face, the face is split into three, thereby // +// splitting the two adjacent tetrahedra into six (by splittetface()); if // +// 'newpoint' lies on an edge, the edge is split into two, thereby, every // +// tetrahedron containing this edge is split into two. If 'newpoint' lies on // +// an existing vertex, no action is taken, and the value DUPLICATEPOINT is // +// returned and 'searchtet' is set to a handle whose origin is the vertex. // +// // +// If 'flipqueue' is not NULL, after 'newpoint' is inserted, it returns all // +// faces which may become non-Delaunay due to the newly inserted point. Flip // +// operations can be performed as necessary on them to maintain the Delaunay // +// property. // +// // +/////////////////////////////////////////////////////////////////////////////// + +enum tetgenmesh::insertsiteresult tetgenmesh::insertsite(point newpoint, + triface* searchtet, bool approx, queue* flipqueue) +{ + enum locateresult intersect, exactloc; + point checkpt; + REAL epspp, checklen; + int count; + + if (b->verbose > 1) { + printf(" Insert point to mesh: (%.12g, %.12g, %.12g) %d.\n", + newpoint[0], newpoint[1], newpoint[2], pointmark(newpoint)); + } + + if (searchtet->tet == (tetrahedron *) NULL) { + // Search for a tetrahedron containing 'newpoint'. + searchtet->tet = dummytet; + exactloc = locate(newpoint, searchtet); + } else { + // Start searching from the tetrahedron provided by the caller. + exactloc = preciselocate(newpoint, searchtet, tetrahedrons->items); + } + intersect = exactloc; + if (approx && (exactloc != ONVERTEX)) { + // Adjust the exact location to an approx. location wrt. epsilon. + epspp = b->epsilon; + count = 0; + while (count < 16) { + intersect = adjustlocate(newpoint, searchtet, exactloc, epspp); + if (intersect == ONVERTEX) { + checkpt = org(*searchtet); + checklen = distance(checkpt, newpoint); + if (checklen / longest > b->epsilon) { + epspp *= 1e-2; + count++; + continue; + } + } + break; + } + } + // Keep current search state for next searching. + recenttet = *searchtet; + + // Insert the point using the right routine + switch (intersect) { + case ONVERTEX: + // There's already a vertex there. Return in 'searchtet' a tetrahedron + // whose origin is the existing vertex. + if (b->verbose > 1) { + printf(" Not insert for duplicating point.\n"); + } + return DUPLICATEPOINT; + + case OUTSIDE: + if (b->verbose > 1) { + printf(" Not insert for locating outside the mesh.\n"); } + return OUTSIDEPOINT; + + case ONEDGE: + // 'newpoint' falls on an edge. + splittetedge(newpoint, searchtet, flipqueue); + return SUCCESSONEDGE; + + case ONFACE: + // 'newpoint' falls on a face. + splittetface(newpoint, searchtet, flipqueue); + return SUCCESSONFACE; + + case INTETRAHEDRON: + // 'newpoint' falls inside a tetrahedron. + splittetrahedron(newpoint, searchtet, flipqueue); + return SUCCESSINTET; + + default: + // Impossible case. + return OUTSIDEPOINT; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// undosite() Undo the most recently point insertion. // +// // +// 'insresult' indicates in where the newpoint has been inserted, i.e., in a // +// tetrahedron, on a face, or on an edge. A correspoding routine will be // +// called to undo the point insertion. 'splittet' is a handle represent one // +// of the resulting tetrahedra, but it may be changed after transformation, // +// even may be dead. Four points 'torg', ... 'toppo' are the corners which // +// 'splittet' should have. On finish, 'newpoint' is not removed. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::undosite(enum insertsiteresult insresult, triface* splittet, + point torg, point tdest, point tapex, point toppo) +{ + // Set the four corners of 'splittet' exactly be 'torg', ... 'toppo'. + findface(splittet, torg, tdest, tapex); + if (oppo(*splittet) != toppo) { + symself(*splittet); +#ifdef SELF_CHECK + assert(oppo(*splittet) == toppo); +#endif + // The sym() operation may inverse the edge, correct it if so. + findedge(splittet, torg, tdest); + } + + // Unsplit the tetrahedron according to 'insresult'. + switch (insresult) { + case SUCCESSINTET: + // 'splittet' should be the face with 'newpoint' as its opposite. + unsplittetrahedron(splittet); + break; + case SUCCESSONFACE: + // 'splittet' should be the one of three splitted face with 'newpoint' + // as its apex. + unsplittetface(splittet); + break; + case SUCCESSONEDGE: + // 'splittet' should be the tet with destination is 'newpoint'. + unsplittetedge(splittet); + break; + default: // To omit compile warnings. + break; } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// closeopenface() Close "open" faces recursively. // +// // +// This is the support routine of inserthullsite(). A point p which lies out-// +// side of CH(T). p is inserted to T by forming a tet t from p and a visible // +// CH face f. The three sides of f which have p as a vertex is called "open" // +// face. Each open face will be closed by either creating a tet on top of it // +// or become a new CH face. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::closeopenface(triface* openface, queue* flipque) +{ + triface newtet, oldhull; + triface newopenface, closeface; + point inspoint, pa, pb, pc; + REAL attrib, volume; + int i; - // Find other tets by a broadth-first search. - for (i = 0; i < tetlist->len(); i++) { - starttet = * (triface *)(* tetlist)[i]; - starttet.ver = 0; - for (j = 0; j < 3; j++) { - fnext(starttet, neightet); - tspivot(neightet, checksh); - // Should we cross a subface. - if ((checksh.sh == dummysh) || complete) { - // Get the neighbor n. - symself(neightet); - if ((neightet.tet != dummytet) && !infected(neightet)) { - // Let oppo(n) = p. - for (neightet.loc = 0; neightet.loc < 4; neightet.loc++) { - if (oppo(neightet) == pt) break; - } - assert(neightet.loc < 4); - // Add n into T. - infect(neightet); - tetlist->append(&neightet); - if (verlist != (list *) NULL) { - // Add the apex vertex in n into V. - ver[0] = org(starttet); - ver[1] = dest(starttet); - findedge(&neightet, ver[0], ver[1]); - ver[2] = apex(neightet); - idx = pointmark(ver[2]); - if (idx >= 0) { - setpointmark(ver[2], -idx - 1); - verlist->append(&(ver[2])); - } - } + // Get the new point p. + inspoint = apex(*openface); + // Find the old CH face f_o (f and f_o share the same edge). + esym(*openface, oldhull); + while (fnextself(oldhull)) ; + if (apex(oldhull) != inspoint) { + // Is f_o visible by p? + pa = org(oldhull); + pb = dest(oldhull); + pc = apex(oldhull); + if (orient3d(pa, pb, pc, inspoint) < 0.0) { + // Yes. Create a new tet t above f_o. + maketetrahedron(&newtet); + setorg(newtet, pa); + setdest(newtet, pb); + setapex(newtet, pc); + setoppo(newtet, inspoint); + for (i = 0; i < in->numberoftetrahedronattributes; i++) { + attrib = elemattribute(oldhull.tet, i); + setelemattribute(newtet.tet, i, attrib); + } + if (b->varvolume) { + volume = volumebound(oldhull.tet); + setvolumebound(newtet.tet, volume); + } + // Connect t to T. + bond(newtet, oldhull); + // Close f. + fnext(newtet, newopenface); + bond(newopenface, *openface); + // f_o becomes an interior face. + enqueueflipface(oldhull, flipque); + // Hull face number decreases. + hullsize--; + // Two faces of t become open face. + enextself(newtet); + for (i = 0; i < 2; i++) { + fnext(newtet, newopenface); + sym(newopenface, closeface); + if (closeface.tet == dummytet) { + closeopenface(&newopenface, flipque); } + enextself(newtet); } - enextself(starttet); + } else { + // Inivisible. f becomes a new CH face. + hullsize++; + // Let 'dummytet' holds f for the next point location. + dummytet[0] = encode(*openface); } + } else { + // f_o is co-incident with f --> f is closed by f_o. + bond(*openface, oldhull); + // f is an interior face. + enqueueflipface(*openface, flipque); } +} - // Uninfect tets. - for (i = 0; i < tetlist->len(); i++) { - starttet = * (triface *)(* tetlist)[i]; - uninfect(starttet); +/////////////////////////////////////////////////////////////////////////////// +// // +// inserthullsite() Insert a point which lies outside the convex hull. // +// // +// The 'inspoint' p lies outside the tetrahedralization T. The 'horiz' f is // +// on the convex hull of T, CH(T), which is visible by p (Imagine f is para- // +// llel to the horizon). To insert p into T we have to enlarge the CH(T) and // +// update T so that p is on the new CH(T). // +// // +// To enlarge the CH(T). We need to find the set F of faces which are on CH // +// (T) and visible by p (F can be formed by a depth-first search from f). p // +// is then inserted into T by mounting new tets formed by p and these faces. // +// Faces of F become interior faces and may non-locally Delaunay. They are // +// queued in 'flipqueue' for flip tests. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::inserthullsite(point inspoint, triface* horiz, queue* flipque) +{ + triface firstnewtet; + triface openface, closeface; + REAL attrib, volume; + int i; + + // Let f face to p. + adjustedgering(*horiz, CW); + // Create the first tet t (from f and p). + maketetrahedron(&firstnewtet); + setorg (firstnewtet, org(*horiz)); + setdest(firstnewtet, dest(*horiz)); + setapex(firstnewtet, apex(*horiz)); + setoppo(firstnewtet, inspoint); + for (i = 0; i < in->numberoftetrahedronattributes; i++) { + attrib = elemattribute(horiz->tet, i); + setelemattribute(firstnewtet.tet, i, attrib); } - if (verlist != (list *) NULL) { - // Uninfect vertices. - for (i = 0; i < verlist->len(); i++) { - ver[0] = * (point *)(* verlist)[i]; - idx = pointmark(ver[0]); - setpointmark(ver[0], -(idx + 1)); + if (b->varvolume) { + volume = volumebound(horiz->tet); + setvolumebound(firstnewtet.tet, volume); + } + // Connect t to T. + bond(firstnewtet, *horiz); + // f is not on CH(T) anymore. + enqueueflipface(*horiz, flipque); + // Hull face number decreases. + hullsize--; + + // Call the faces of t which have p as a vertex "open" face. + for (i = 0; i < 3; i++) { + // Get an open face f_i of t. + fnext(firstnewtet, openface); + // Close f_i if it is still open. + sym(openface, closeface); + if (closeface.tet == dummytet) { + closeopenface(&openface, flipque); } + // Go to the next open face of t. + enextself(firstnewtet); } } @@ -14243,31 +15594,6 @@ void tetgenmesh::updatebowatcavitysub(list* sublist, list* subceillist, } } if (remcount > 0) { - // Some subfaces have been removed from the cavity. - if (checkpbcs) { - // Check if the facet has a PBC defined. - checksh = * (face *)(* sublist)[0]; - if (shellpbcgroup(checksh) >= 0) { - // Yes, A PBC facet. Remove all subfaces -- Do not insert the point. - for (i = 0; i < sublist->len(); i++) { - checksh = * (face *)(* sublist)[i]; - suninfect(checksh); - // Remove both side tets from the cavity. - for (j = 0; j < 2; j++) { - stpivot(checksh, adjtet); - if (adjtet.tet != dummytet) { - if (infected(adjtet)) { - uninfect(adjtet); - (*cutcount)++; - } - } - sesymself(checksh); - } - } - remcount += sublist->len(); - sublist->clear(); - } - } if (b->verbose > 2) { printf(" Removed %d subfaces from CBC(p).\n", remcount); } @@ -14705,7 +16031,7 @@ void tetgenmesh::bowatinsertsite(point bp,face* splitseg,int n,list** sublists, sspivot(oldsh, checkseg); if (checkseg.sh != dummysh) { // A segment. Insert s into the face ring, ie, s_in -> s -> s_out. - if (casingout.sh != dummysh) { // if (oldsh.sh != casingout.sh) { + if (oldsh.sh != casingout.sh) { // s is not bonded to itself. spinsh = casingout; do { @@ -14718,7 +16044,7 @@ void tetgenmesh::bowatinsertsite(point bp,face* splitseg,int n,list** sublists, sbond1(newsh, casingout); } else { // Bond newsh -> newsh. - sdissolve(newsh); // sbond(newsh, newsh); + sbond(newsh, newsh); } // Bond the segment. ssbond(newsh, checkseg); @@ -14898,1187 +16224,1077 @@ void tetgenmesh::bowatinsertsite(point bp,face* splitseg,int n,list** sublists, } } // if (splitseg != (face *) NULL) - // Delete subfaces of old CBC_i(p)s. - for (k = 0; k < n; k++) { - for (i = 0; i < sublists[k]->len(); i++) { - oldsh = * (face *)(* (sublists[k]))[i]; - shellfacedealloc(subfaces, oldsh.sh); + // Delete subfaces of old CBC_i(p)s. + for (k = 0; k < n; k++) { + for (i = 0; i < sublists[k]->len(); i++) { + oldsh = * (face *)(* (sublists[k]))[i]; + shellfacedealloc(subfaces, oldsh.sh); + } + // Clear the list so that the subs will not get unmarked later in + // routine releasebowatcavity() which only frees the memory. + sublists[k]->clear(); + // Only do once if p is on a facet. + if (splitseg == (face *) NULL) break; + } + + // Check for newly encroached subfaces if the flag is set. + if (chkencsub) { + // Check if new subfaces of C_i(p) are encroached by other vertices. + for (k = 0; k < n; k++) { + subceillist = subceillists[k]; + for (i = 0; i < subceillist->len(); i++) { + newsh = * (face *)(* subceillist)[i]; + checksub4encroach(&newsh, NULL, true); + } + // Only do once if p is on a facet. + if (splitseg == (face *) NULL) break; + } + // Check if the adjacent subfaces are encroached by p. + tallencsubs(bp, n, ceillists); + } + } // if (subceillists != (list **) NULL) + + // Delete tets of old BC_i(p)s. + for (k = 0; k < n; k++) { + for (i = 0; i < tetlists[k]->len(); i++) { + oldtet = * (triface *)(* (tetlists[k]))[i]; + tetrahedrondealloc(oldtet.tet); + } + // Clear the list so that the tets will not get unmarked later in + // routine releasebowatcavity() which only frees the memory. + tetlists[k]->clear(); + } + + // check for bad quality tets if the flags is set. + if (chkbadtet) { + for (k = 0; k < n; k++) { + ceillist = ceillists[k]; + for (i = 0; i < ceillist->len(); i++) { + newtet = * (triface *)(* ceillist)[i]; + checktet4badqual(&newtet, true); + } + } + } + + if (flipque != (queue *) NULL) { + // Newly created internal faces of BC(p) (excluding faces on C(p)s) are + // in 'flipque'. Some of these faces may be locally non-Delaunay due, + // to the existence of non-constrained tets. check and fix them. + repairflipcount += flip(flipque, NULL); + } +} + +// +// End of mesh transformation routines +// + +// +// Begin Delaunay tetrahedralization routines +// + +/////////////////////////////////////////////////////////////////////////////// +// // +// formstarpolyhedron() Get the star ployhedron of a point 'pt'. // +// // +// The polyhedron P is formed by faces of tets having 'pt' as a vertex. If // +// 'complete' is TRUE, P is the complete star of 'pt'. Otherwise, P is boun- // +// ded by subfaces, i.e. P is only part of the star of 'pt'. // +// // +// 'tetlist' T returns the tets, it has one of such tets on input. Moreover, // +// if t is in T, then oppo(t) = p. Topologically, T is the star of p; and // +// the faces of T is the link of p. 'verlist' V returns the vertices of T. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::formstarpolyhedron(point pt, list* tetlist, list* verlist, + bool complete) +{ + triface starttet, neightet; + face checksh; + point ver[3]; + int idx, i, j; + + // Get a tet t containing p. + starttet = * (triface *)(* tetlist)[0]; + // Let oppo(t) = p. + for (starttet.loc = 0; starttet.loc < 4; starttet.loc++) { + if (oppo(starttet) == pt) break; + } + assert(starttet.loc < 4); + // Add t into T. + * (triface *)(* tetlist)[0] = starttet; + infect(starttet); + if (verlist != (list *) NULL) { + // Add three verts of t into V. + ver[0] = org(starttet); + ver[1] = dest(starttet); + ver[2] = apex(starttet); + for (i = 0; i < 3; i++) { + // Mark the vert by inversing the index of the vert. + idx = pointmark(ver[i]); + setpointmark(ver[i], -idx - 1); // -1 to distinguish the zero. + verlist->append(&(ver[i])); + } + } + + // Find other tets by a broadth-first search. + for (i = 0; i < tetlist->len(); i++) { + starttet = * (triface *)(* tetlist)[i]; + starttet.ver = 0; + for (j = 0; j < 3; j++) { + fnext(starttet, neightet); + tspivot(neightet, checksh); + // Should we cross a subface. + if ((checksh.sh == dummysh) || complete) { + // Get the neighbor n. + symself(neightet); + if ((neightet.tet != dummytet) && !infected(neightet)) { + // Let oppo(n) = p. + for (neightet.loc = 0; neightet.loc < 4; neightet.loc++) { + if (oppo(neightet) == pt) break; + } + assert(neightet.loc < 4); + // Add n into T. + infect(neightet); + tetlist->append(&neightet); + if (verlist != (list *) NULL) { + // Add the apex vertex in n into V. + ver[0] = org(starttet); + ver[1] = dest(starttet); + findedge(&neightet, ver[0], ver[1]); + ver[2] = apex(neightet); + idx = pointmark(ver[2]); + if (idx >= 0) { + setpointmark(ver[2], -idx - 1); + verlist->append(&(ver[2])); + } + } + } + } + enextself(starttet); + } + } + + // Uninfect tets. + for (i = 0; i < tetlist->len(); i++) { + starttet = * (triface *)(* tetlist)[i]; + uninfect(starttet); + } + if (verlist != (list *) NULL) { + // Uninfect vertices. + for (i = 0; i < verlist->len(); i++) { + ver[0] = * (point *)(* verlist)[i]; + idx = pointmark(ver[0]); + setpointmark(ver[0], -(idx + 1)); + } + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// unifypoint() Unify two distinct points if they're very close. // +// // +// This function is used for dealing with inputs from CAD tools. Two points // +// p and q are unified if: dist(p, q) / longest < eps. Where dist() is the // +// Euclidean distance between p and q, longest is the maximum edge size of // +// the input point set, eps is the tolerrence specified by user, default is // +// 1e-6, it can be adjusted by '-T' switch. // +// // +/////////////////////////////////////////////////////////////////////////////// + +bool tetgenmesh::unifypoint(point testpt, triface *starttet, enum locateresult + loc, REAL eps) +{ + triface symtet, spintet; + point checkpt, tapex; + REAL tol; + bool merged; + int hitbdry; + int i; + + merged = false; + tol = longest * eps; + if ((loc == OUTSIDE) || (loc == INTETRAHEDRON) || (loc == ONFACE)) { + // Check p is close to the four corners of the tet. + for (i = 0; i < 4; i++) { + checkpt = (point) starttet->tet[4 + i]; + if (distance(testpt, checkpt) < tol) { + merged = true; // Found a merge point p'. + break; } - // Clear the list so that the subs will not get unmarked later in - // routine releasebowatcavity() which only frees the memory. - sublists[k]->clear(); - // Only do once if p is on a facet. - if (splitseg == (face *) NULL) break; } - - // Check for newly encroached subfaces if the flag is set. - if (chkencsub) { - // Check if new subfaces of C_i(p) are encroached by other vertices. - for (k = 0; k < n; k++) { - subceillist = subceillists[k]; - for (i = 0; i < subceillist->len(); i++) { - newsh = * (face *)(* subceillist)[i]; - checksub4encroach(&newsh, NULL, true); + if (!merged && (loc == ONFACE)) { + // Check the opposite point of the neighbor tet if it exists. + sym(*starttet, symtet); + if (symtet.tet != dummytet) { + checkpt = oppo(symtet); + if (distance(testpt, checkpt) < tol) { + merged = true; // Found a merge point p'. } - // Only do once if p is on a facet. - if (splitseg == (face *) NULL) break; } - // Check if the adjacent subfaces are encroached by p. - tallencsubs(bp, n, ceillists); } - } // if (subceillists != (list **) NULL) - - // Delete tets of old BC_i(p)s. - for (k = 0; k < n; k++) { - for (i = 0; i < tetlists[k]->len(); i++) { - oldtet = * (triface *)(* (tetlists[k]))[i]; - tetrahedrondealloc(oldtet.tet); + } else if (loc == ONEDGE) { + // Check two endpoints of the edge. + checkpt = org(*starttet); + if (distance(testpt, checkpt) < tol) { + merged = true; // Found a merge point p'. } - // Clear the list so that the tets will not get unmarked later in - // routine releasebowatcavity() which only frees the memory. - tetlists[k]->clear(); - } - - // check for bad quality tets if the flags is set. - if (chkbadtet) { - for (k = 0; k < n; k++) { - ceillist = ceillists[k]; - for (i = 0; i < ceillist->len(); i++) { - newtet = * (triface *)(* ceillist)[i]; - checktet4badqual(&newtet, true); + if (!merged) { + checkpt = dest(*starttet); + if (distance(testpt, checkpt) < tol) { + merged = true; // Found a merge point p'. } } + if (!merged) { + // Check apexes of the faces having the edge. + spintet = *starttet; + tapex = apex(*starttet); + hitbdry = 0; + do { + checkpt = apex(spintet); + if (distance(testpt, checkpt) < tol) { + merged = true; // Found a merge point p'. + break; + } + if (!fnextself(spintet)) { + hitbdry++; + if (hitbdry < 2) { + esym(*starttet, spintet); + if (!fnextself(spintet)) { + hitbdry++; + } + } + } + } while ((apex(spintet) != tapex) && (hitbdry < 2)); + } } - - if (flipque != (queue *) NULL) { - // Newly created internal faces of BC(p) (excluding faces on C(p)s) are - // in 'flipque'. Some of these faces may be locally non-Delaunay due - // to the existence of non-constrained tets. check and fix them. - lawson3d(flipque); + if (merged) { + if (b->object != tetgenbehavior::STL) { + if (!b->quiet) { + printf("Warning: Point %d is unified to point %d.\n", + pointmark(testpt), pointmark(checkpt)); + } + // Count the number of duplicated points. + dupverts++; + } + // Remember it is a duplicated point. + setpointtype(testpt, DUPLICATEDVERTEX); + // Set a pointer to the point it duplicates. + setpoint2ppt(testpt, checkpt); } + return merged; } -//// //// -//// //// -//// flip_cxx ///////////////////////////////////////////////////////////////// - -//// delaunay_cxx ///////////////////////////////////////////////////////////// -//// //// -//// //// - /////////////////////////////////////////////////////////////////////////////// // // -// btree_sort() Sort vertices using a binary space partition (bsp) tree. // +// incrflipdelaunay() Construct a delaunay tetrahedrization from a set of // +// 3D points by the incremental flip algorithm. // +// // +// The incremental flip algorithm (by Edelsbrunner and Shah) can be describ- // +// ed as follows: // +// // +// S be a set of points in 3D, Let 4 <= i <= n and assume that the // +// Delaunay tetrahedralization of the first i-1 points in S is already // +// constructed; call it D(i-1). Add the i-th point p_i (belong to S) to // +// D(i-1), and restore Delaunayhood by flipping; this result in D(i). // +// Repeat this procedure until i = n. // +// // +// This strategy always leads to the Delaunay triangulation of a point set. // +// The return value is the number of convex hull faces of D. // // // /////////////////////////////////////////////////////////////////////////////// -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::incrflipdelaunay(triface* oldtet, point* insertarray, + long arraysize, bool jump, bool merge, REAL eps, queue* flipque) { - point *leftarray, *rightarray; - point **pptary, swapvert; - REAL split; - bool lflag, rflag; - int i, j, k; + triface newtet, searchtet; + point swappt, lastpt; + enum locateresult loc; + REAL det, n[3]; + REAL attrib, volume; + int i, j; + clock_t loc_start, loc_end; - if (b->verbose > 2) { - 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")); + if (b->verbose > 0) { + printf(" Creating initial tetrahedralization.\n"); } - if (depth > max_btree_depth) { - max_btree_depth = depth; + // The initial tetrahedralization T only has one tet formed by 4 affinely + // linear independent vertices of the point set V = 'insertarray'. The + // first point a = insertarray[0]. + + // Get the second point b, that is not identical or very close to a. + for (i = 1; i < arraysize; i++) { + det = distance(insertarray[0], insertarray[i]); + if (det > (longest * eps)) break; } - - 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); + if (i == arraysize) { + printf("\nAll points seem to be identical.\n"); + return; } else { - // Split along z-axis. - split = 0.5 * (bzmin + bzmax); + // Swap to move b from index i to index 1. + swappt = insertarray[i]; + insertarray[i] = insertarray[1]; + insertarray[1] = swappt; + } + // Get the third point c, that is not collinear with a and b. + for (i++; i < arraysize; i++) { + if (!iscollinear(insertarray[0], insertarray[1], insertarray[i], eps)) + break; + } + if (i == arraysize) { + printf("\nAll points seem to be collinear.\n"); + return; + } else { + // Swap to move c from index i to index 2. + swappt = insertarray[i]; + insertarray[i] = insertarray[2]; + insertarray[2] = swappt; + } + // Get the fourth point d, that is not coplanar with a, b, and c. + for (i++; i < arraysize; i++) { + det = orient3d(insertarray[0], insertarray[1], insertarray[2], + insertarray[i]); + if (det == 0.0) continue; + if (!iscoplanar(insertarray[0], insertarray[1], insertarray[2], + insertarray[i], det, eps)) break; + } + if (i == arraysize) { + // It's a 2D problem. + in->mesh_dim = 2; + // All points are coplanar. + if (b->plc) { + // Create an abovepoint. Maybe a surface triangulation can be formed. + facenormal(insertarray[0], insertarray[1], insertarray[2], n, &det); + if (det != 0.0) for (j = 0; j < 3; j++) n[j] /= det; + // Take the average edge length of the bounding box. + det = (0.5*(xmax - xmin) + 0.5*(ymax - ymin) + 0.5*(zmax - zmin)) / 3.0; + // Temporarily create a point. It will be removed by jettison(); + makepoint(&lastpt); + for (j = 0; j < 3; j++) lastpt[j] = insertarray[0][j] + det * n[j]; + abovepoint = lastpt; + det = orient3d(insertarray[0], insertarray[1], insertarray[2], lastpt); + // The index of the next inserting point is 3. + i = 3; + } else { + printf("\nAll points seem to be coplanar.\n"); + return; + } + } else { + // Swap to move d from index i to index 3. + swappt = insertarray[i]; + insertarray[i] = insertarray[3]; + insertarray[3] = swappt; + lastpt = insertarray[3]; + // The index of the next inserting point is 4. + i = 4; + } + + // Create the initial tet. + maketetrahedron(&newtet); + if (det > 0.0) { + // For keeping the positive orientation. + swappt = insertarray[0]; + insertarray[0] = insertarray[1]; + insertarray[1] = swappt; + } + if (b->verbose > 2) { + printf(" Create the first tet (%d, %d, %d, %d).\n", + pointmark(insertarray[0]), pointmark(insertarray[1]), + pointmark(insertarray[2]), pointmark(lastpt)); + } + setorg(newtet, insertarray[0]); + setdest(newtet, insertarray[1]); + setapex(newtet, insertarray[2]); + setoppo(newtet, lastpt); + if (oldtet != (triface *) NULL) { + 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); + } + } + // Set vertex type be FREEVOLVERTEX if it has no type yet. + if (pointtype(insertarray[0]) == UNUSEDVERTEX) { + setpointtype(insertarray[0], FREEVOLVERTEX); + } + if (pointtype(insertarray[1]) == UNUSEDVERTEX) { + setpointtype(insertarray[1], FREEVOLVERTEX); + } + if (pointtype(insertarray[2]) == UNUSEDVERTEX) { + setpointtype(insertarray[2], FREEVOLVERTEX); + } + if (pointtype(lastpt) == UNUSEDVERTEX) { + setpointtype(lastpt, FREEVOLVERTEX); + } + // Bond to 'dummytet' for point location. + dummytet[0] = encode(newtet); + if (b->verbose > 3) { + printf(" Creating tetra "); + printtet(&newtet); + } + // At init, all faces of this tet are hull faces. + hullsize = 4; + + if (b->verbose > 0) { + printf(" Incrementally inserting points.\n"); } - i = 0; - j = arraysize - 1; + flip23s = flip32s = flip22s = flip44s = 0; + searchtet.tet = (tetrahedron *) NULL; - // Partition the vertices into left- and right-arraies. - do { - for (; i < arraysize; i++) { - if (vertexarray[i][axis] >= split) { - break; - } + // Insert the rest of points, one by one. + for (; i < arraysize; i++) { + // Locate p_i in T. +#ifdef SELF_CHECK + loc_start = clock(); +#endif + if (jump) { + loc = locate(insertarray[i], &searchtet); + } else { + loc = preciselocate(insertarray[i], &searchtet, tetrahedrons->items); } - for (; j >= 0; j--) { - if (vertexarray[j][axis] < split) { - break; +#ifdef SELF_CHECK + loc_end = clock(); + tloctime += ((REAL) (loc_end - loc_start)) / CLOCKS_PER_SEC; +#endif + // Keep current search state for next searching. + recenttet = searchtet; + if (loc == ONVERTEX) { + if (b->object != tetgenbehavior::STL) { + if (!b->quiet) { + printf("Warning: Point %d is identical with point %d.\n", + pointmark(insertarray[i]), pointmark(org(searchtet))); + } } + // Count the number of duplicated points. + dupverts++; + // Remember it is a duplicated point. + setpointtype(insertarray[i], DUPLICATEDVERTEX); + if (b->plc || b->refine) { + // Set a pointer to the point it duplicates. + setpoint2ppt(insertarray[i], org(searchtet)); + } + continue; // p_i is not inserted. } - // Is the partition finished? - if (i == (j + 1)) { - break; + if (merge) { + // Unify p_i if it is too close to a point of T. + if (unifypoint(insertarray[i], &searchtet, loc, eps)) { + continue; // p_i is not inserted. + } } - // 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 > 2) { - printf(" leftsize = %d, rightsize = %d\n", i, arraysize - i); - } - lflag = rflag = false; - - // if (depth < max_tree_depth) { - 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); + // Insert p_i in T. + if (loc != OUTSIDE) { + if (b->verbose > 1) { + printf(" Insert point %d in tetrahedralization.\n", + pointmark(insertarray[i])); } - } 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); + if (loc == INTETRAHEDRON) { + splittetrahedron(insertarray[i], &searchtet, flipque); + } else if (loc == ONFACE) { + splittetface(insertarray[i], &searchtet, flipque); + } else if (loc == ONEDGE) { + splittetedge(insertarray[i], &searchtet, flipque); } } else { - rflag = true; + if (b->verbose > 1) { + printf(" Insert point %d on convex hull.\n", + pointmark(insertarray[i])); + } + inserthullsite(insertarray[i], &searchtet, flipque); } - // } 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; + if (pointtype(insertarray[i]) == UNUSEDVERTEX) { + // p_i becomes a (volume) vertex of T. + setpointtype(insertarray[i], FREEVOLVERTEX); } - // 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. - // Put all points in this array. - for (k = 0; k < i; k++) { - leftarray[k + 1] = vertexarray[k]; - setpoint2ppt(leftarray[k + 1], (point) leftarray); +#ifdef SELF_CHECK + loc_start = clock(); +#endif + if (!b->noflip) { + // Recover Delaunayness of T by flipping. + flip(flipque, NULL); + } else { + lawson(NULL, flipque); + // T remains regular. + // flipque->clear(); } - // Save this array in list. - btreenode_list->newindex((void **) &pptary); - *pptary = leftarray; +#ifdef SELF_CHECK + loc_end = clock(); + tfliptime += ((REAL) (loc_end - loc_start)) / CLOCKS_PER_SEC; +#endif } - // 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. - // 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; + if (b->verbose > 0) { + printf(" %ld Flips (T23 %ld, T32 %ld, T22 %ld, T44 %ld)\n", + flip23s+flip32s+flip22s+flip44s, flip23s, flip32s, flip22s, flip44s); } } /////////////////////////////////////////////////////////////////////////////// // // -// 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) ptary[0]; - // Insert the point into the node. - ptary[arylen + 1] = insertpt; - // Increase the array length by 1. - ptary[0] = (point) (arylen + 1); -} - -/////////////////////////////////////////////////////////////////////////////// +// delaunizevertices() Form a Delaunay tetrahedralization. // // // -// btree_search() Search a near point for an inserting point. // +// Given a point set V (saved in 'points'). The Delaunay tetrahedralization // +// D of V is created by incrementally inserting vertices. Returns the number // +// of triangular faces bounding the convex hull of D. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::btree_search(point insertpt, triface* searchtet) +long tetgenmesh::delaunizevertices() { - 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) ptary[0]; - - if (arylen == 0) { - searchtet->tet = NULL; - return; - } + queue *flipque; + point *insertarray; + long arraysize; + int i, j; - if (arylen < 10) { - ptsamples = arylen; - } else { - ptsamples = 10; // Take at least 10 samples. - // The number of random samples taken is proportional to the third root - // of the number of points in the cell. - while (ptsamples * ptsamples * ptsamples < arylen) { - ptsamples++; + if (!b->quiet) { + if (!b->noflip) { + printf("Constructing Delaunay tetrahedralization.\n"); + } else { + printf("Constructing regular tetrahedralization.\n"); } } - // Select "good" candidate using k random samples, taking the closest one. - mindist2 = 1.79769E+308; // The largest double value (8 byte). - nearpt = NULL; + flipque = new queue(sizeof(badface)); + // Prepare the array of points for inserting. + arraysize = points->items; + insertarray = new point[arraysize]; + points->traversalinit(); - 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; - } + // Randomize the point order. + // randomseed = b->srandseed; + for (i = 0; i < arraysize; i++) { + j = (int) randomnation(i + 1); // 0 <= j <= i; + insertarray[i] = insertarray[j]; + insertarray[j] = pointtraverse(); } - if (b->verbose > 1) { - printf(" Get point %d (cell size %ld).\n", pointmark(nearpt), arylen); - } + // Use lawson flip. + b->noflip = 1; + + // Form the DT by incremental flip Delaunay algorithm. + incrflipdelaunay(NULL, insertarray, arraysize, true, b->plc, b->epsilon, + flipque); + + b->noflip = 0; - decode(point2tet(nearpt), *searchtet); + delete [] insertarray; + delete flipque; + return hullsize; } +// +// End Delaunay tetrahedralization routines +// + +// +// Begin of surface triangulation routines +// + /////////////////////////////////////////////////////////////////////////////// // // -// ordervertices() Order the vertices for incremental inserting. // +// formstarpolygon() Form the star polygon of a point in facet. // +// // +// The polygon P is formed by all coplanar subfaces having 'pt' as a vertex. // +// P is bounded by segments, e.g, if no segments, P is the full star of pt. // // // -// We assume the vertices have been sorted by a binary tree. // +// 'trilist' T returns the subfaces, it has one of such subfaces on input. // +// In addition, if f is in T, then sapex(f) = p. 'vertlist' V are verts of P.// +// Topologically, T is the star of p; V and the edges of T are the link of p.// // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::ordervertices(point* vertexarray, int arraysize) +void tetgenmesh::formstarpolygon(point pt, list* trilist, list* vertlist) { - point **ipptary, **jpptary, *swappptary; - point *ptary; - long arylen; - int index, i, j; + face steinsh, lnextsh, rnextsh; + face checkseg; + point pa, pb, pc, pd; + int i; - // 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. + // Get a subface f containing p. + steinsh = * (face *)(* trilist)[0]; + steinsh.shver = 0; // CCW + // Let sapex(f) be p. + for (i = 0; i < 3; i++) { + if (sapex(steinsh) == pt) break; + senextself(steinsh); + } + assert(i < 3); + // Add the edge f into list. + * (face *)(* trilist)[0] = steinsh; + pa = sorg(steinsh); + pb = sdest(steinsh); + if (vertlist != (list *) NULL) { + // Add two verts a, b into V, + vertlist->append(&pa); + vertlist->append(&pb); } - 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) ptary[0]; - for (j = 2; j <= arylen; j++) { // Skip the first point. - vertexarray[index] = ptary[j]; - index++; + // Rotate edge pa to the left (CW) until meet pb or a segment. + lnextsh = steinsh; + pc = pa; + do { + senext2self(lnextsh); + assert(sorg(lnextsh) == pt); + sspivot(lnextsh, checkseg); + if (checkseg.sh != dummysh) break; // Do not cross a segment. + // Get neighbor subface n (must exist). + spivotself(lnextsh); + if (lnextsh.sh == dummysh) break; // It's a hull edge. + // Go to the edge ca opposite to p. + if (sdest(lnextsh) != pt) sesymself(lnextsh); + assert(sdest(lnextsh) == pt); + senext2self(lnextsh); + // Add n (at edge ca) to T. + trilist->append(&lnextsh); + // Add edge ca to E. + pc = sorg(lnextsh); + if (pc == pb) break; // Rotate back. + if (vertlist != (list *) NULL) { + // Add vert c into V. + vertlist->append(&pc); } - // 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] - } + } while (true); - // Make sure we've done correctly. - assert(index == arraysize); + if (pc != pb) { + // Rotate edge bp to the right (CCW) until meet a segment. + rnextsh = steinsh; + do { + senextself(rnextsh); + assert(sdest(rnextsh) == pt); + sspivot(rnextsh, checkseg); + if (checkseg.sh != dummysh) break; // Do not cross a segment. + // Get neighbor subface n (must exist). + spivotself(rnextsh); + if (rnextsh.sh == dummysh) break; // It's a hull edge. + // Go to the edge bd opposite to p. + if (sorg(rnextsh) != pt) sesymself(rnextsh); + assert(sorg(rnextsh) == pt); + senextself(rnextsh); + // Add n (at edge bd) to T. + trilist->append(&rnextsh); + // Add edge bd to E. + pd = sdest(rnextsh); + if (pd == pa) break; // Rotate back. + if (vertlist != (list *) NULL) { + // Add vert d into V. + vertlist->append(&pd); + } + } while (true); + } } /////////////////////////////////////////////////////////////////////////////// // // -// insertvertexbw() Insert a vertex using the Boywer-Watson algorithm. // +// About the 'abovepoint' // +// // +// The 'abovepoint' of a facet is a point which is exactly non-coplanar with // +// the plane containing that facet. With such an point, the 3D predicates: // +// orient3d(), and insphere() can be used to substitute the corresponding 2D // +// siblings, e.g. orient2d(), and incircle(). Its location is not critical, // +// but floating-point accuracy is improved if it is nicely placed over the // +// facet, not too close or too far away. // // // -// The point p will be first located in T. 'searchtet' is a suggested start- // -// tetrahedron, it can be NULL. Note that p may lies outside T. In such case,// -// the convex hull of T will be updated to include p as a vertex. // +// We take the convention that the abovepoint of a facet always lies above // +// the facet. By this convention, given three points a, b, and c in a facet, // +// we say c has the counterclockwise order with ab is corresponding to say // +// that c is below the plane abp, where p is the lift point. // // // -// If 'bwflag' is TRUE, the Bowyer-Watson algorithm is used to recover the // -// Delaunayness of T. Otherwise, do nothing with regard to the Delaunayness // -// T (T may be non-Delaunay after this function). // +/////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// // // -// If 'visflag' is TRUE, force to check the visibility of the boundary faces // -// of cavity. This is needed when T is not Delaunay. // +// getfacetabovepoint() Get a point above a plane pass through a facet. // // // -// If 'noencflag' is TRUE, only insert the new point p if it does not cause // -// any existing (sub)segment be non-Delaunay. This option only is checked // -// when the global variable 'checksubsegs' is set. // +// The calculcated point is saved in 'facetabovepointarray'. The 'abovepoint'// +// is set on return. // // // /////////////////////////////////////////////////////////////////////////////// -enum tetgenmesh::locateresult tetgenmesh::insertvertexbw(point insertpt, - triface *searchtet, bool bwflag, bool visflag, bool noencsegflag, - bool noencsubflag) -{ - triface neightet, spintet, newtet, neineitet; - triface *cavetet, *parytet, *parytet1; - face checksh, *pssub; - face checkseg, *paryseg; - point pa, pb, pc, *ppt; - enum locateresult loc; - REAL attrib, volume; - REAL sign, ori; - long tetcount; - bool enqflag; - int hitbdry; - int i, j; - - arraypool *swaplist; // for updating cavity. - long updatecount; - - if (b->verbose > 1) { - printf(" Insert point %d\n", pointmark(insertpt)); - } - - tetcount = ptloc_count; - updatecount = 0; - - // Locate the point. - if (searchtet->tet == NULL) { - if (btreenode_list) { // default option - // Use bsp-tree to select a starting tetrahedron. - btree_search(insertpt, searchtet); - } else { // -u0 option - // Randomly select a starting tetrahedron. - randomsample(insertpt, searchtet); - } - loc = preciselocate(insertpt, searchtet, tetrahedrons->items); - } else { - // Start from 'searchtet'. - loc = locate2(insertpt, searchtet, NULL); - } - - if (b->verbose > 1) { - printf(" Walk distance (# tets): %ld\n", ptloc_count - tetcount); - } - - if (ptloc_max_count < (ptloc_count - tetcount)) { - ptloc_max_count = (ptloc_count - tetcount); - } - - if (b->verbose > 1) { - printf(" Located (%d) tet (%d, %d, %d, %d).\n", (int) loc, - pointmark(org(*searchtet)), pointmark(dest(*searchtet)), - pointmark(apex(*searchtet)), pointmark(oppo(*searchtet))); - } - - if (loc == ONVERTEX) { - // The point already exists. Mark it and do nothing on it. - if (b->object != tetgenbehavior::STL) { - if (!b->quiet) { - printf("Warning: Point #%d is duplicated with Point #%d. Ignored!\n", - pointmark(insertpt), pointmark(org(*searchtet))); - } - } - setpoint2ppt(insertpt, org(*searchtet)); - setpointtype(insertpt, DUPLICATEDVERTEX); - dupverts++; - return loc; - } - - tetcount = 0l; // The number of deallocated tets. - - // Create the initial boundary of the cavity. - if (loc == INTETRAHEDRON) { - // Add four boundary faces of this tet into list. - neightet.tet = searchtet->tet; - for (neightet.loc = 0; neightet.loc < 4; neightet.loc++) { - cavetetlist->newindex((void **) &parytet); - *parytet = neightet; - } - infect(*searchtet); - caveoldtetlist->newindex((void **) &parytet); - *parytet = *searchtet; - tetcount++; - flip14count++; - } else if (loc == ONFACE) { - // Add at most six boundary faces into list. - neightet.tet = searchtet->tet; - for (i = 0; i < 3; i++) { - neightet.loc = locpivot[searchtet->loc][i]; - cavetetlist->newindex((void **) &parytet); - *parytet = neightet; - } - infect(*searchtet); - caveoldtetlist->newindex((void **) &parytet); - *parytet = *searchtet; - tetcount++; - decode(searchtet->tet[searchtet->loc], spintet); - if (spintet.tet != dummytet) { - neightet.tet = spintet.tet; - for (i = 0; i < 3; i++) { - neightet.loc = locpivot[spintet.loc][i]; - cavetetlist->newindex((void **) &parytet); - *parytet = neightet; - } - infect(spintet); - caveoldtetlist->newindex((void **) &parytet); - *parytet = spintet; - tetcount++; - } else { - // Split a hull face into three hull faces. - hullsize += 2; - } - flip26count++; - } else if (loc == ONEDGE) { - // Add all adjacent boundary tets into list. - spintet = *searchtet; - pc = apex(spintet); - hitbdry = 0; - do { - tetcount++; - neightet.tet = spintet.tet; - neightet.loc = locverpivot[spintet.loc][spintet.ver][0]; - cavetetlist->newindex((void **) &parytet); - *parytet = neightet; - neightet.loc = locverpivot[spintet.loc][spintet.ver][1]; - cavetetlist->newindex((void **) &parytet); - *parytet = neightet; - infect(spintet); - caveoldtetlist->newindex((void **) &parytet); - *parytet = spintet; - // Go to the next tet (may be dummytet). - tfnext(spintet, neightet); - if (neightet.tet == dummytet) { - hitbdry++; - if (hitbdry == 2) break; - esym(*searchtet, spintet); // Go to another direction. - tfnext(spintet, neightet); - if (neightet.tet == dummytet) break; - } - spintet = neightet; - } while (apex(spintet) != pc); - // Update hull size if it is a hull edge. - if (hitbdry > 0) { - // Split a hull edge deletes two hull faces, adds four new hull faces. - hullsize += 2; - } - flipn2ncount++; - } else if (loc == OUTSIDE) { - // p lies outside the convex hull. Enlarge the convex hull by including p. - if (b->verbose > 1) { - printf(" Insert a hull vertex.\n"); - } - // 'searchtet' refers to a hull face which is visible by p. - adjustedgering(*searchtet, CW); - // Create the first tet t (from f and p). - maketetrahedron(&newtet); - setorg (newtet, org(*searchtet)); - setdest(newtet, dest(*searchtet)); - setapex(newtet, apex(*searchtet)); - setoppo(newtet, insertpt); - for (i = 0; i < in->numberoftetrahedronattributes; i++) { - attrib = elemattribute(searchtet->tet, i); - setelemattribute(newtet.tet, i, attrib); - } - if (b->varvolume) { - volume = volumebound(searchtet->tet); - setvolumebound(newtet.tet, volume); - } - // Connect t to T. - bond(newtet, *searchtet); - // Removed a hull face, added three "new hull faces". - hullsize += 2; - - // Add a cavity boundary face. - cavetetlist->newindex((void **) &parytet); - *parytet = newtet; - // Add a cavity tet. - infect(newtet); - caveoldtetlist->newindex((void **) &parytet); - *parytet = newtet; - tetcount++; - - // Add three "new hull faces" into list (re-use cavebdrylist). - newtet.ver = 0; - for (i = 0; i < 3; i++) { - fnext(newtet, neightet); - cavebdrylist->newindex((void **) &parytet); - *parytet = neightet; - enextself(newtet); - } - - // Find all actual new hull faces. - for (i = 0; i < (int) cavebdrylist->objects; i++) { - // Get a queued "new hull face". - parytet = (triface *) fastlookup(cavebdrylist, i); - // Every "new hull face" must have p as its apex. - assert(apex(*parytet) == insertpt); - assert((parytet->ver & 1) == 1); // It's CW edge ring. - // Check if it is still a hull face. - sym(*parytet, neightet); - if (neightet.tet == dummytet) { - // Yes, get its adjacent hull face (at its edge). - esym(*parytet, neightet); - while (1) { - fnextself(neightet); - // Does its adjacent tet exist? - sym(neightet, neineitet); - if (neineitet.tet == dummytet) break; - symedgeself(neightet); - } - // neightet is an adjacent hull face. - pc = apex(neightet); - if (pc != insertpt) { - // Check if p is visible by the hull face ('neightet'). - pa = org(neightet); - pb = dest(neightet); - ori = orient3d(pa, pb, pc, insertpt); orient3dcount++; - if (ori < 0) { - // Create a new tet adjacent to neightet. - maketetrahedron(&newtet); - setorg (newtet, pa); - setdest(newtet, pb); - setapex(newtet, pc); - setoppo(newtet, insertpt); - for (j = 0; j < in->numberoftetrahedronattributes; j++) { - attrib = elemattribute(neightet.tet, j); - setelemattribute(newtet.tet, j, attrib); - } - if (b->varvolume) { - volume = volumebound(neightet.tet); - setvolumebound(newtet.tet, volume); - } - bond(newtet, neightet); - fnext(newtet, neineitet); - bond(neineitet, *parytet); - // Comment: We removed two hull faces, and added two "new hull - // faces", hence hullsize remains unchanged. - // Add a cavity boundary face. - cavetetlist->newindex((void **) &parytet1); - *parytet1 = newtet; - // Add a cavity tet. - infect(newtet); - caveoldtetlist->newindex((void **) &parytet1); - *parytet1 = newtet; - tetcount++; - // Add two "new hull faces" into list. - enextself(newtet); - for (j = 0; j < 2; j++) { - fnext(newtet, neineitet); - cavebdrylist->newindex((void **) &parytet1); - *parytet1 = neineitet; - enextself(newtet); - } - } - } else { - // Two hull faces matched. Bond the two adjacent tets. - bond(*parytet, neightet); - hullsize -= 2; - } - } // if (neightet.tet == dummytet) - } // i - cavebdrylist->restart(); - inserthullcount++; - } - - if (!bwflag) return loc; - - // Form the Boywer-Watson cavity. - for (i = 0; i < (int) cavetetlist->objects; i++) { - // Get a cavity boundary face. - parytet = (triface *) fastlookup(cavetetlist, i); - assert(parytet->tet != dummytet); - assert(infected(*parytet)); // The tet is inside the cavity. - enqflag = false; - // Get the adjacent tet. - sym(*parytet, neightet); - if (neightet.tet != dummytet) { - if (!infected(neightet)) { - if (!marktested(neightet)) { - ppt = (point *) &(neightet.tet[4]); - sign = insphere_s(ppt[0], ppt[1], ppt[2], ppt[3], insertpt); - enqflag = (sign < 0.0); - // Avoid redundant insphere tests. - marktest(neightet); - } - } else { - enqflag = true; - } - } - if (enqflag) { // Found a tet in the cavity. - if (!infected(neightet)) { // Avoid to add it multiple times. - // Put other three faces in check list. - neineitet.tet = neightet.tet; - for (j = 0; j < 3; j++) { - neineitet.loc = locpivot[neightet.loc][j]; - cavetetlist->newindex((void **) &parytet1); - *parytet1 = neineitet; - } - infect(neightet); - caveoldtetlist->newindex((void **) &parytet1); - *parytet1 = neightet; - tetcount++; - } - } else { - // Found a boundary face of the cavity. - if (neightet.tet == dummytet) { - // Check for a possible flat tet (see m27.node, use -J option). - pa = org(*parytet); - pb = dest(*parytet); - pc = apex(*parytet); - ori = orient3d(pa, pb, pc, insertpt); - if (ori != 0) { - cavebdrylist->newindex((void **) &parytet1); - *parytet1 = *parytet; - // futureflip = flippush(futureflip, parytet, insertpt); - } - } else { - cavebdrylist->newindex((void **) &parytet1); - *parytet1 = *parytet; - } - } - } // i +void tetgenmesh::getfacetabovepoint(face* facetsh) +{ + list *verlist, *trilist, *tetlist; + triface adjtet; + face symsh; + point p1, p2, p3, pa; + enum locateresult loc; + REAL smallcos, cosa; + REAL largevol, volume; + REAL v1[3], v2[3], len; + int smallidx, largeidx; + int shmark; + int i, j; - if (b->verbose > 1) { - printf(" Cavity formed: %ld tets, %ld faces.\n", tetcount, - cavebdrylist->objects); - } - - totaldeadtets += tetcount; - totalbowatcavsize += cavebdrylist->objects; - if (maxbowatcavsize < (long) cavebdrylist->objects) { - maxbowatcavsize = cavebdrylist->objects; - } - - if (checksubsegs || noencsegflag) { - // Check if some (sub)segments are inside the cavity. - for (i = 0; i < (int) caveoldtetlist->objects; i++) { - parytet = (triface *) fastlookup(caveoldtetlist, i); - for (j = 0; j < 6; j++) { - parytet->loc = edge2locver[j][0]; - parytet->ver = edge2locver[j][1]; - tsspivot1(*parytet, checkseg); - if ((checkseg.sh != dummysh) && !sinfected(checkseg)) { - // Check if this segment is inside the cavity. - spintet = *parytet; - pa = apex(spintet); - enqflag = true; - hitbdry = 0; - while (1) { - tfnextself(spintet); - if (spintet.tet == dummytet) { - hitbdry++; - if (hitbdry == 2) break; - esym(*parytet, spintet); - tfnextself(spintet); - if (spintet.tet == dummytet) break; - } - if (!infected(spintet)) { - enqflag = false; break; // It is not inside. - } - if (apex(spintet) == pa) break; - } - if (enqflag) { - if (b->verbose > 1) { - printf(" Queue a missing segment (%d, %d).\n", - pointmark(sorg(checkseg)), pointmark(sdest(checkseg))); - } - sinfect(checkseg); // Only save it once. - subsegstack->newindex((void **) &paryseg); - *paryseg = checkseg; - } - } - } - } - } + abovecount++; + // Initialize working lists. + verlist = new list(sizeof(point *), NULL); + trilist = new list(sizeof(face), NULL); + tetlist = new list(sizeof(triface), NULL); - if (noencsegflag && (subsegstack->objects > 0)) { - // Found encroached subsegments! Do not insert this point. - for (i = 0; i < (int) caveoldtetlist->objects; i++) { - parytet = (triface *) fastlookup(caveoldtetlist, i); - uninfect(*parytet); - unmarktest(*parytet); - } - // Unmark cavity neighbor tets (outside the cavity). - for (i = 0; i < (int) cavebdrylist->objects; i++) { - parytet = (triface *) fastlookup(cavebdrylist, i); - sym(*parytet, neightet); - if (neightet.tet != dummytet) { - unmarktest(neightet); - } - } - cavetetlist->restart(); - cavebdrylist->restart(); - caveoldtetlist->restart(); - return ENCSEGMENT; - } + // Get three pivotal points p1, p2, and p3 in the facet as a base triangle + // which is non-trivil and has good base angle (close to 90 degree). - if (checksubfaces || noencsubflag) { - // Check if some subfaces are inside the cavity. - for (i = 0; i < (int) caveoldtetlist->objects; i++) { - parytet = (triface *) fastlookup(caveoldtetlist, i); - neightet.tet = parytet->tet; - for (neightet.loc = 0; neightet.loc < 4; neightet.loc++) { - tspivot(neightet, checksh); - if (checksh.sh != dummysh) { - sym(neightet, neineitet); - // Do not check it if it is a hull tet. - if (neineitet.tet != dummytet) { - if (infected(neineitet)) { - if (b->verbose > 1) { - printf(" Queue a missing subface (%d, %d, %d).\n", - pointmark(sorg(checksh)), pointmark(sdest(checksh)), - pointmark(sapex(checksh))); - } - tsdissolve(neineitet); // Disconnect a tet-sub bond. - stdissolve(checksh); // Disconnect the sub-tet bond. - sesymself(checksh); - stdissolve(checksh); - // Add the missing subface into list. - subfacstack->newindex((void **) &pssub); - *pssub = checksh; - } - } - } + // p1 is chosen as the one which has the smallest index in pa, pb, pc. + p1 = sorg(*facetsh); + pa = sdest(*facetsh); + if (pointmark(pa) < pointmark(p1)) p1 = pa; + pa = sapex(*facetsh); + if (pointmark(pa) < pointmark(p1)) p1 = pa; + // Form the star polygon of p1. + trilist->append(facetsh); + formstarpolygon(p1, trilist, verlist); + + // Get the second pivotal point p2. + p2 = * (point *)(* verlist)[0]; + // Get vector v1 = p1->p2. + for (i = 0; i < 3; i++) v1[i] = p2[i] - p1[i]; + len = sqrt(dot(v1, v1)); + assert(len > 0.0); // p2 != p1. + for (i = 0; i < 3; i++) v1[i] /= len; + + // Get the third pivotal point p3. p3 is chosen as the one in 'verlist' + // which forms an angle with v1 closer to 90 degree than others do. + smallcos = 1.0; // The cosine value of 0 degree. + smallidx = 1; // Default value. + for (i = 1; i < verlist->len(); i++) { + p3 = * (point *)(* verlist)[i]; + for (j = 0; j < 3; j++) v2[j] = p3[j] - p1[j]; + len = sqrt(dot(v2, v2)); + if (len > 0.0) { // v2 is not too small. + cosa = fabs(dot(v1, v2)) / len; + if (cosa < smallcos) { + smallidx = i; + smallcos = cosa; } } } + assert(smallcos < 1.0); // p1->p3 != p1->p2. + p3 = * (point *)(* verlist)[smallidx]; + verlist->clear(); - if (noencsubflag && (subfacstack->objects > 0)) { - // Found encroached subfaces! Do not insert this point. - /*for (i = 0; i < caveoldtetlist->objects; i++) { - cavetet = (triface *) fastlookup(caveoldtetlist, i); - uninfect(*cavetet); - unmarktest(*cavetet); - } - for (i = 0; i < cavebdrylist->objects; i++) { - cavetet = (triface *) fastlookup(cavebdrylist, i); - unmarktest(*cavetet); // Unmark it. - } - if (bwflag && (futureflip != NULL)) { - flippool->restart(); - futureflip = NULL; - } - cavetetlist->restart(); - cavebdrylist->restart(); - caveoldtetlist->restart(); - return ENCFACE; - */ - } - - if (visflag) { - // If T is not a Delaunay triangulation, the formed cavity may not be - // star-shaped (fig/dump-cavity-case8). Validation is needed. - cavetetlist->restart(); // Re-use it. - for (i = 0; i < (int) cavebdrylist->objects; i++) { - cavetet = (triface *) fastlookup(cavebdrylist, i); - if (infected(*cavetet)) { - sym(*cavetet, neightet); - if (neightet.tet == dummytet || !infected(neightet)) { - if (neightet.tet != dummytet) { - cavetet->ver = 4; // CCW edge ring. - pa = dest(*cavetet); - pb = org(*cavetet); - pc = apex(*cavetet); - ori = orient3d(pa, pb, pc, insertpt); orient3dcount++; - assert(ori != 0.0); // SELF_CHECK - enqflag = (ori > 0.0); - } else { - enqflag = true; // A hull face. - } - if (enqflag) { - // This face is valid, save it. - cavetetlist->newindex((void **) &parytet); - *parytet = *cavetet; - } else { - if (b->verbose > 1) { - printf(" Cut tet (%d, %d, %d, %d)\n", pointmark(pb), - pointmark(pa), pointmark(pc), pointmark(oppo(*cavetet))); - } - uninfect(*cavetet); - unmarktest(*cavetet); - if (neightet.tet != dummytet) { - unmarktest(neightet); - } - updatecount++; - // Add three new faces to find new boundaries. - for (j = 0; j < 3; j++) { - fnext(*cavetet, neineitet); - sym(neineitet, neightet); - if (neightet.tet != dummytet) { - if (infected(neightet)) { - neightet.ver = 4; - cavebdrylist->newindex((void **) &parytet); - *parytet = neightet; - } else { - unmarktest(neightet); - } - } - enextself(*cavetet); - } - } - } else { - // This face is not on the cavity boundary anymore. - unmarktest(*cavetet); - } + if (tetrahedrons->items > 0l) { + // Get a tet having p1 as a vertex. + stpivot(*facetsh, adjtet); + if (adjtet.tet == dummytet) { + sesym(*facetsh, symsh); + stpivot(symsh, adjtet); + } + if (adjtet.tet == dummytet) { + decode(point2tet(p1), adjtet); + if (isdead(&adjtet)) { + adjtet.tet = dummytet; } else { - assert(!marktested(*cavetet)); - } - } - if (updatecount > 0) { - // Update the cavity boundary faces (fig/dump-cavity-case9). - cavebdrylist->restart(); - for (i = 0; i < (int) cavetetlist->objects; i++) { - cavetet = (triface *) fastlookup(cavetetlist, i); - // 'cavetet' was boundary face of the cavity. - if (infected(*cavetet)) { - sym(*cavetet, neightet); - if ((neightet.tet != dummytet) || !infected(neightet)) { - // It is a cavity boundary face. - cavebdrylist->newindex((void **) &parytet); - *parytet = *cavetet; - } else { - // Not a cavity boundary face. - unmarktest(*cavetet); - } - } else { - assert(!marktested(*cavetet)); - } - } - // Update the list of old tets. - cavetetlist->restart(); - for (i = 0; i < (int) caveoldtetlist->objects; i++) { - cavetet = (triface *) fastlookup(caveoldtetlist, i); - if (infected(*cavetet)) { - cavetetlist->newindex((void **) &parytet); - *parytet = *cavetet; + if (!findorg(&adjtet, p1)) { + adjtet.tet = dummytet; } } - assert((int) cavetetlist->objects < i); - // Swap 'cavetetlist' and 'caveoldtetlist'. - swaplist = caveoldtetlist; - caveoldtetlist = cavetetlist; - cavetetlist = swaplist; - if (b->verbose > 1) { - printf(" Size of the updated cavity: %d faces %d tets.\n", - (int) cavebdrylist->objects, (int) caveoldtetlist->objects); - } - } - } - - // Re-use this list for new cavity faces. - cavetetlist->restart(); - - // Create new tetrahedra in the Bowyer-Watson cavity and Connect them. - for (i = 0; i < (int) cavebdrylist->objects; i++) { - parytet = (triface *) fastlookup(cavebdrylist, i); - assert(infected(*parytet)); // The tet is inside the cavity. - parytet->ver = 0; // In CCW edge ring. - maketetrahedron(&newtet); - setorg (newtet, org(*parytet)); - setdest(newtet, dest(*parytet)); - setapex(newtet, apex(*parytet)); - setoppo(newtet, insertpt); - for (j = 0; j < in->numberoftetrahedronattributes; j++) { - attrib = elemattribute(parytet->tet, j); - setelemattribute(newtet.tet, j, attrib); - } - if (b->varvolume) { - volume = volumebound(parytet->tet); - setvolumebound(newtet.tet, volume); - } - // Bond the new tet to the adjacent tet outside the cavity. - sym(*parytet, neightet); - if (neightet.tet != dummytet) { - // The tet was marked (to avoid redundant insphere tests). - unmarktest(neightet); - bond(newtet, neightet); - } else { - // Bond newtet to dummytet. - dummytet[0] = encode(newtet); } - // mark the other three faces of this tet as "open". - neightet.tet = newtet.tet; - for (j = 0; j < 3; j++) { - neightet.tet[locpivot[0][j]] = NULL; - } - // Let the oldtet knows newtet (for connecting adjacent new tets). - parytet->tet[parytet->loc] = encode(newtet); - if (checksubsegs) { - // newtet and parytet share at the same edge. - for (j = 0; j < 3; j++) { - tsspivot1(*parytet, checkseg); - if (checkseg.sh != dummysh) { - if (sinfected(checkseg)) { - // This subsegment is not missing. Unmark it. - if (b->verbose > 1) { - printf(" Dequeue a segment (%d, %d).\n", - pointmark(sorg(checkseg)), pointmark(sdest(checkseg))); - } - suninfect(checkseg); // Dequeue a non-missing segment. - } - tssbond1(newtet, checkseg); - } - enextself(*parytet); - enextself(newtet); + if (adjtet.tet == dummytet) { + loc = locate(p1, &adjtet); + if (loc == ONVERTEX) { + setpoint2tet(p1, encode(adjtet)); + } else { + adjtet.tet = dummytet; } } - if (checksubfaces) { - // Bond subface to the new tet. - tspivot(*parytet, checksh); - if (checksh.sh != dummysh) { - tsbond(newtet, checksh); - // The other-side-connection of checksh should be no change. - } + if (adjtet.tet != dummytet) { + // Get the star polyhedron of p1. + tetlist->append(&adjtet); + formstarpolyhedron(p1, tetlist, verlist, false); } - } // i - - // Set a handle for speeding point location. - recenttet = newtet; - setpoint2tet(insertpt, encode(newtet)); + } - // Connect adjacent new tetrahedra together. Here we utilize the connections - // of the old cavity tets to find the new adjacent tets. - for (i = 0; i < (int) cavebdrylist->objects; i++) { - parytet = (triface *) fastlookup(cavebdrylist, i); - decode(parytet->tet[parytet->loc], newtet); - // assert(org(newtet) == org(*parytet)); // SELF_CHECK - // assert((newtet.ver & 1) == 0); // in CCW edge ring. - for (j = 0; j < 3; j++) { - fnext(newtet, neightet); // Go to the "open" face. - if (neightet.tet[neightet.loc] == NULL) { - spintet = *parytet; - while (1) { - fnextself(spintet); - symedgeself(spintet); - if (spintet.tet == dummytet) break; - if (!infected(spintet)) break; - } - if (spintet.tet != dummytet) { - // 'spintet' is the adjacent tet of the cavity. - fnext(spintet, neineitet); - assert(neineitet.tet[neineitet.loc] == NULL); // SELF_CHECK - bond(neightet, neineitet); - } else { - // This side is a hull face. - neightet.tet[neightet.loc] = (tetrahedron) dummytet; - dummytet[0] = encode(neightet); - } + // Get the abovepoint in 'verlist'. It is the one form the largest valid + // volumw with the base triangle over other points in 'verlist. + largevol = 0.0; + largeidx = 0; + for (i = 0; i < verlist->len(); i++) { + pa = * (point *)(* verlist)[i]; + volume = orient3d(p1, p2, p3, pa); + if (!iscoplanar(p1, p2, p3, pa, volume, b->epsilon * 1e+2)) { + if (fabs(volume) > largevol) { + largevol = fabs(volume); + largeidx = i; } - setpoint2tet(org(newtet), encode(newtet)); - enextself(newtet); - enextself(*parytet); } } - // Delete the old cavity tets. - for (i = 0; i < (int) caveoldtetlist->objects; i++) { - parytet = (triface *) fastlookup(caveoldtetlist, i); - tetrahedrondealloc(parytet->tet); - } - - // Set the point type. - if (pointtype(insertpt) == UNUSEDVERTEX) { - setpointtype(insertpt, FREEVOLVERTEX); - } - - if (btreenode_list) { - btree_insert(insertpt); + // Do we have the abovepoint? + if (largevol > 0.0) { + abovepoint = * (point *)(* verlist)[largeidx]; + if (b->verbose > 1) { + printf(" Chosen abovepoint %d for facet %d.\n", pointmark(abovepoint), + shellmark(*facetsh)); + } + } else { + // Calculate an abovepoint for this facet. + facenormal(p1, p2, p3, v1, &len); + if (len != 0.0) for (i = 0; i < 3; i++) v1[i] /= len; + // Take the average edge length of the bounding box. + len = (0.5*(xmax - xmin) + 0.5*(ymax - ymin) + 0.5*(zmax - zmin)) / 3.0; + // Temporarily create a point. It will be removed by jettison(); + makepoint(&abovepoint); + setpointtype(abovepoint, UNUSEDVERTEX); + unuverts++; + for (i = 0; i < 3; i++) abovepoint[i] = p1[i] + len * v1[i]; + if (b->verbose > 1) { + printf(" Calculated abovepoint %d for facet %d.\n", + pointmark(abovepoint), shellmark(*facetsh)); + } } - - cavetetlist->restart(); - cavebdrylist->restart(); - caveoldtetlist->restart(); - - return loc; + // Save the abovepoint in 'facetabovepointarray'. + shmark = shellmark(*facetsh); + facetabovepointarray[shmark] = abovepoint; + + delete trilist; + delete tetlist; + delete verlist; } /////////////////////////////////////////////////////////////////////////////// // // -// unifypoint() Unify two distinct points if they're very close. // +// collectcavsubs() Collect non-locally Delaunay subfaces wrt a point. // // // -// This function is used for dealing with inputs from CAD tools. Two points // -// p and q are unified if: dist(p, q) / longest < eps. Where dist() is the // -// Euclidean distance between p and q, longest is the maximum edge size of // -// the input point set, eps is the tolerrence specified by user, default is // -// 1e-6, it can be adjusted by '-T' switch. // +// 'cavsublist' returns the list of subfaces. On input, it conatins at least // +// one subface. // // // /////////////////////////////////////////////////////////////////////////////// -bool tetgenmesh::unifypoint(point testpt, triface *starttet, enum locateresult - loc, REAL eps) +void tetgenmesh::collectcavsubs(point newpoint, list* cavsublist) { - triface symtet, spintet; - point checkpt, tapex; - REAL tol; - bool merged; - int hitbdry; - int i; + face startsub, neighsub; + face checkseg; + point pa, pb, pc; + REAL sign, ori; + int i, j; - merged = false; - tol = longest * eps; - if ((loc == OUTSIDE) || (loc == INTETRAHEDRON) || (loc == ONFACE)) { - // Check p is close to the four corners of the tet. - for (i = 0; i < 4; i++) { - checkpt = (point) starttet->tet[4 + i]; - if (distance(testpt, checkpt) < tol) { - merged = true; // Found a merge point p'. - break; - } - } - if (!merged && (loc == ONFACE)) { - // Check the opposite point of the neighbor tet if it exists. - sym(*starttet, symtet); - if (symtet.tet != dummytet) { - checkpt = oppo(symtet); - if (distance(testpt, checkpt) < tol) { - merged = true; // Found a merge point p'. - } - } - } - } else if (loc == ONEDGE) { - // Check two endpoints of the edge. - checkpt = org(*starttet); - if (distance(testpt, checkpt) < tol) { - merged = true; // Found a merge point p'. - } - if (!merged) { - checkpt = dest(*starttet); - if (distance(testpt, checkpt) < tol) { - merged = true; // Found a merge point p'. - } - } - if (!merged) { - // Check apexes of the faces having the edge. - spintet = *starttet; - tapex = apex(*starttet); - hitbdry = 0; - do { - checkpt = apex(spintet); - if (distance(testpt, checkpt) < tol) { - merged = true; // Found a merge point p'. - break; - } - if (!fnextself(spintet)) { - hitbdry++; - if (hitbdry < 2) { - esym(*starttet, spintet); - if (!fnextself(spintet)) { - hitbdry++; - } + // First infect subfaces in 'cavsublist'. + for (i = 0; i < cavsublist->len(); i++) { + startsub = * (face *)(* cavsublist)[i]; + sinfect(startsub); + } + // Find the other subfaces by a broadth-first searching. + for (i = 0; i < cavsublist->len(); i++) { + startsub = * (face *)(* cavsublist)[i]; + for (j = 0; j < 3; j++) { + sspivot(startsub, checkseg); + // Is there a segment? + if (checkseg.sh == dummysh) { + // No segment. Get the neighbor. + spivot(startsub, neighsub); + if (!sinfected(neighsub)) { + pa = sorg(neighsub); + pb = sdest(neighsub); + pc = sapex(neighsub); + sign = insphere(pa, pb, pc, abovepoint, newpoint); + ori = orient3d(pa, pb, pc, abovepoint); + if (sign != 0.0) { + // Correct the sign. + sign = ori > 0.0 ? sign : -sign; + } + if (sign > 0.0) { + // neighsub is encroached by newpoint. + sinfect(neighsub); + cavsublist->append(&neighsub); } } - } while ((apex(spintet) != tapex) && (hitbdry < 2)); - } - } - if (merged) { - if (b->object != tetgenbehavior::STL) { - if (!b->quiet) { - printf("Warning: Point %d is unified to point %d.\n", - pointmark(testpt), pointmark(checkpt)); } - // Count the number of duplicated points. - dupverts++; + senextself(startsub); } - // Remember it is a duplicated point. - setpointtype(testpt, DUPLICATEDVERTEX); - // Set a pointer to the point it duplicates. - setpoint2ppt(testpt, checkpt); } - return merged; + // Having found all subfaces, uninfect them before return. + for (i = 0; i < cavsublist->len(); i++) { + startsub = * (face *)(* cavsublist)[i]; + suninfect(startsub); + } } /////////////////////////////////////////////////////////////////////////////// // // -// incrflipdelaunay() Construct a delaunay tetrahedrization from a set of // -// 3D points by the incremental flip algorithm. // +// collectvisiblesubs() Collect convex hull edges which are visible from // +// the inserting point. Construct new subfaces from // +// these edges and the point. // // // -// The incremental flip algorithm (by Edelsbrunner and Shah) can be describ- // -// ed as follows: // +// Let T be the current Delaunay triangulation (of vertices of a facet F). // +// 'shmark', the index of F in 'in->facetlist' (starts from 1); 'inspoint' // +// lies outside of T; 'horiz' is a hull edge of T which is visible by it. // // // -// S be a set of points in 3D, Let 4 <= i <= n and assume that the // -// Delaunay tetrahedralization of the first i-1 points in S is already // -// constructed; call it D(i-1). Add the i-th point p_i (belong to S) to // -// D(i-1), and restore Delaunayhood by flipping; this result in D(i). // -// Repeat this procedure until i = n. // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::collectvisiblesubs(int shmark, point inspoint, face* horiz, + queue* flipqueue) +{ + face newsh, hullsh; + face rightsh, leftsh, spinedge; + point horg, hdest; + bool aboveflag; + REAL ori, sign; + + // Get the sign of abovepoint (so we can assume it is above the plane). + adjustedgering(*horiz, CCW); + horg = sorg(*horiz); + hdest = sdest(*horiz); + ori = orient3d(horg, hdest, sapex(*horiz), abovepoint); + sign = ori > 0.0 ? -1 : 1; + + // Create a new subface above 'horiz'. + makeshellface(subfaces, &newsh); + setsorg(newsh, hdest); + setsdest(newsh, horg); + setsapex(newsh, inspoint); + setshellmark(newsh, shmark); + if (b->quality && varconstraint) { + setareabound(newsh, areabound(*horiz)); + } + if (checkpbcs) { + setshellpbcgroup(newsh, shellpbcgroup(*horiz)); + } + // Make the connection. + sbond(newsh, *horiz); + // 'horiz' becomes interior edge. + enqueueflipedge(*horiz, flipqueue); + + // Finish the hull edges at the right side of the newsh. + hullsh = *horiz; + while (1) { + senext(newsh, rightsh); + // Get the right hull edge of 'horiz' by spinning inside edges around + // 'horg' until reaching the 'dummysh'. + spinedge = hullsh; + do { + hullsh = spinedge; + senext2self(hullsh); + spivot(hullsh, spinedge); + if (spinedge.sh == dummysh) break; + if (sorg(spinedge) != horg) sesymself(spinedge); + assert(sorg(spinedge) == horg); + } while (true); + horg = sorg(hullsh); + // Test whether 'inspoint' is visible by 'hullsh'. + ori = orient3d(horg, sdest(hullsh), abovepoint, inspoint); + ori *= sign; + aboveflag = ori < 0.0; + if (aboveflag) { + // It's visible. + makeshellface(subfaces, &newsh); + setsorg(newsh, sdest(hullsh)); + setsdest(newsh, horg); + setsapex(newsh, inspoint); + setshellmark(newsh, shmark); + if (b->quality && varconstraint) { + setareabound(newsh, areabound(hullsh)); + } + if (checkpbcs) { + setshellpbcgroup(newsh, shellpbcgroup(hullsh)); + } + // Make the connection. + sbond(newsh, hullsh); + senext2(newsh, leftsh); + sbond(leftsh, rightsh); + // 'hullsh' becomes interior edge. + enqueueflipedge(hullsh, flipqueue); + } else { + // 'rightsh' is a new hull edge. + dummysh[0] = sencode(rightsh); + break; + } + } + + // Finish the hull edges at the left side of the newsh. + hullsh = *horiz; + spivot(*horiz, newsh); + while (1) { + senext2(newsh, leftsh); + // Get the left hull edge of 'horiz' by spinning edges around 'hdest'. + spinedge = hullsh; + do { + hullsh = spinedge; + senextself(hullsh); + spivot(hullsh, spinedge); + if (spinedge.sh == dummysh) break; + if (sdest(spinedge) != hdest) sesymself(spinedge); + assert(sdest(spinedge) == hdest); + } while (true); + // Update 'hdest'. + hdest = sdest(hullsh); + // Test whether 'inspoint' is visible from 'hullsh'. + ori = orient3d(sorg(hullsh), hdest, abovepoint, inspoint); + ori *= sign; + aboveflag = ori < 0.0; + if (aboveflag) { + // It's a visible hull edge. + makeshellface(subfaces, &newsh); + setsorg(newsh, hdest); + setsdest(newsh, sorg(hullsh)); + setsapex(newsh, inspoint); + setshellmark(newsh, shmark); + if (b->quality && varconstraint) { + setareabound(newsh, areabound(hullsh)); + } + if (checkpbcs) { + setshellpbcgroup(newsh, shellpbcgroup(hullsh)); + } + // Make the connection. + sbond(newsh, hullsh); + senext(newsh, rightsh); + sbond(rightsh, leftsh); + // 'horiz' becomes interior edge. + enqueueflipedge(hullsh, flipqueue); + } else { + // 'leftsh' is a new hull edge. + dummysh[0] = sencode(leftsh); + break; + } + } +} + +/////////////////////////////////////////////////////////////////////////////// // // -// This strategy always leads to the Delaunay triangulation of a point set. // -// The return value is the number of convex hull faces of D. // +// incrflipdelaunaysub() Create a DT from a 3D coplanar point set using // +// the incremental flip algorithm. // // // -// If the input point set is degenerate, i.e., all points are collinear or // -// are coplanar, then no 3D DT is created and return FALSE. // +// Let T be the current Delaunay triangulation (of vertices of a facet F). // +// 'shmark', the index of F in 'in->facetlist' (starts from 1). // // // /////////////////////////////////////////////////////////////////////////////// -bool tetgenmesh::incrflipdelaunay(triface* oldtet, point* insertarray, - long arraysize, bool jump, bool merge, REAL eps, queue* flipque) +void tetgenmesh::incrflipdelaunaysub(int shmark, REAL eps, list* ptlist, + int holes, REAL* holelist, queue* flipque) { - triface newtet, searchtet; - point swappt, lastpt; + face newsh, startsh; + point *insertarray; + point swappt; + pbcdata *pd; enum locateresult loc; - REAL det; - REAL attrib, volume; - int i, j; + REAL det, area; + bool aboveflag; + int arraysize; + int epscount; + int fmarker; + int idx, i, j, k; - // The initial tetrahedralization T only has one tet formed by 4 affinely - // linear independent vertices of the point set V = 'insertarray'. The - // first point a = insertarray[0]. - - // Get the second point b, that is not identical or very close to a. + // Get the point array (saved in 'ptlist'). + insertarray = (point *) ptlist->base; + arraysize = ptlist->len(); + if (arraysize < 3) return; + + // Do calculation of 'abovepoint' if number of points > 3. + aboveflag = (arraysize > 3); + + // The initial triangulation T only has one triangle formed by 3 not + // cillinear points of the set V = 'insertarray'. The first point: + // a = insertarray[0]. + + epscount = 0; + while (true) { for (i = 1; i < arraysize; i++) { det = distance(insertarray[0], insertarray[i]); if (det > (longest * eps)) break; } - if (i == arraysize) { - // printf("\nAll points seem to be identical.\n"); - return false; - } else { + if (i < arraysize) { // Swap to move b from index i to index 1. swappt = insertarray[i]; insertarray[i] = insertarray[1]; @@ -16086,2572 +17302,2765 @@ bool tetgenmesh::incrflipdelaunay(triface* oldtet, point* insertarray, } // Get the third point c, that is not collinear with a and b. for (i++; i < arraysize; i++) { - if (!iscollinear(insertarray[0], insertarray[1], insertarray[i], eps)) + if (!iscollinear(insertarray[0], insertarray[1], insertarray[i], eps)) break; } - if (i == arraysize) { - // printf("\nAll points seem to be collinear.\n"); - return false; - } else { + if (i < arraysize) { // Swap to move c from index i to index 2. swappt = insertarray[i]; insertarray[i] = insertarray[2]; insertarray[2] = swappt; - } - // Get the fourth point d, that is not coplanar with a, b, and c. - for (i++; i < arraysize; i++) { - det = orient3d(insertarray[0], insertarray[1], insertarray[2], - insertarray[i]); - if (det == 0.0) continue; - if (!iscoplanar(insertarray[0], insertarray[1], insertarray[2], - insertarray[i], det, eps)) break; - } - if (i == arraysize) { - return false; + i = 3; // The next inserting point. } else { - // Swap to move d from index i to index 3. - swappt = insertarray[i]; - insertarray[i] = insertarray[3]; - insertarray[3] = swappt; - lastpt = insertarray[3]; - // The index of the next inserting point is 4. - i = 4; - } - - if (det > 0.0) { - // For keeping the positive orientation. - swappt = insertarray[0]; - insertarray[0] = insertarray[1]; - insertarray[1] = swappt; - } - - // Create the initial tet. - if (b->verbose > 1) { - printf(" Create the first tet (%d, %d, %d, %d).\n", - pointmark(insertarray[0]), pointmark(insertarray[1]), - pointmark(insertarray[2]), pointmark(lastpt)); - } - - maketetrahedron(&newtet); - setorg(newtet, insertarray[0]); - setdest(newtet, insertarray[1]); - setapex(newtet, insertarray[2]); - setoppo(newtet, lastpt); - if (oldtet != (triface *) NULL) { - 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); + // The set of vertices is not good (or nearly degenerate). However, + // a trivial triangulation can be formed (using 3 vertices). It may + // be corrected (or deleted) by mergefacet(). + if ((eps == 0.0) || (epscount > 16)) { + printf("Error: Invalid PLC.\n"); + printf(" Facet (%d, %d, %d", pointmark(insertarray[0]), + pointmark(insertarray[1]), pointmark(insertarray[2])); + if (ptlist->len() > 3) { + printf(", ..."); + } + printf(") (%d) is not a valid polygon.\n", shmark); + terminatetetgen(1); } + // Decrease the eps, and continue to try. + eps *= 1e-2; + epscount++; + continue; } - // Set vertex type be FREEVOLVERTEX if it has no type yet. - if (pointtype(insertarray[0]) == UNUSEDVERTEX) { - setpointtype(insertarray[0], FREEVOLVERTEX); - } - if (pointtype(insertarray[1]) == UNUSEDVERTEX) { - setpointtype(insertarray[1], FREEVOLVERTEX); - } - if (pointtype(insertarray[2]) == UNUSEDVERTEX) { - setpointtype(insertarray[2], FREEVOLVERTEX); - } - if (pointtype(lastpt) == UNUSEDVERTEX) { - setpointtype(lastpt, FREEVOLVERTEX); - } - // Bond to 'dummytet' for point location. - dummytet[0] = encode(newtet); - recenttet = newtet; - // Update the point-to-tet map. - setpoint2tet(insertarray[0], encode(newtet)); - setpoint2tet(insertarray[1], encode(newtet)); - setpoint2tet(insertarray[2], encode(newtet)); - setpoint2tet(lastpt, encode(newtet)); - if (b->verbose > 3) { - printf(" Creating tetra "); - printtet(&newtet); - } - // At init, all faces of this tet are hull faces. - hullsize = 4; - - if (b->verbose > 1) { - printf(" Incrementally inserting points.\n"); - } + break; + } // while (true); - // Insert the rest of points, one by one. - for (; i < arraysize; i++) { - if (jump) { - searchtet.tet = NULL; - } else { - searchtet = recenttet; - } - loc = insertvertexbw(insertarray[i],&searchtet,true,false,false,false); + // Create the initial triangle. + makeshellface(subfaces, &newsh); + setsorg(newsh, insertarray[0]); + setsdest(newsh, insertarray[1]); + setsapex(newsh, insertarray[2]); + // Remeber the facet it belongs to. + setshellmark(newsh, shmark); + // Set vertex type be FREESUBVERTEX if it has no type yet. + if (pointtype(insertarray[0]) == FREEVOLVERTEX) { + setpointtype(insertarray[0], FREESUBVERTEX); } - - return true; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// delaunizevertices() Form a Delaunay tetrahedralization. // -// // -// Given a point set V (saved in 'points'). The Delaunay tetrahedralization // -// D of V is created by incrementally inserting vertices. Returns the number // -// of triangular faces bounding the convex hull of D. // -// // -/////////////////////////////////////////////////////////////////////////////// - -long tetgenmesh::delaunizevertices() -{ - point *insertarray; - long arraysize; - bool success; - int i, j; - - if (!b->quiet) { - printf("Constructing Delaunay tetrahedralization.\n"); + if (pointtype(insertarray[1]) == FREEVOLVERTEX) { + setpointtype(insertarray[1], FREESUBVERTEX); } - - if (b->btree) { - btreenode_list = new arraypool(sizeof(point*), 10); - max_btreenode_size = 0; - max_btree_depth = 0; + if (pointtype(insertarray[2]) == FREEVOLVERTEX) { + setpointtype(insertarray[2], FREESUBVERTEX); } + // Let 'dummysh' point to it (for point location). + dummysh[0] = sencode(newsh); - if (cavetetlist == NULL) { - cavetetlist = new arraypool(sizeof(triface), 10); - cavebdrylist = new arraypool(sizeof(triface), 10); - caveoldtetlist = new arraypool(sizeof(triface), 10); + // Are there area constraints? + if (b->quality && (in->facetconstraintlist != (REAL *) NULL)) { + idx = in->facetmarkerlist[shmark - 1]; // The actual facet marker. + for (k = 0; k < in->numberoffacetconstraints; k++) { + fmarker = (int) in->facetconstraintlist[k * 2]; + if (fmarker == idx) { + area = in->facetconstraintlist[k * 2 + 1]; + setareabound(newsh, area); + break; + } + } } - // Prepare the array of points for inserting. - arraysize = points->items; - insertarray = new point[arraysize]; - - points->traversalinit(); - if (b->btree) { // -u option. - // Use the input order. - for (i = 0; i < arraysize; i++) { - insertarray[i] = pointtraverse(); - } - if (b->verbose) { - printf(" Sorting vertices by a bsp-tree.\n"); - } - // Sort the points using a binary tree recursively. - btree_sort(insertarray, 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(insertarray, in->numberofpoints); - } else { - if (b->verbose) { - printf(" Permuting vertices.\n"); - } - // Randomize the point order. - for (i = 0; i < arraysize; i++) { - j = (int) randomnation(i + 1); // 0 <= j <= i; - insertarray[i] = insertarray[j]; - insertarray[j] = pointtraverse(); + // Are there pbc conditions? + if (checkpbcs) { + idx = in->facetmarkerlist[shmark - 1]; // The actual facet marker. + for (k = 0; k < in->numberofpbcgroups; k++) { + pd = &subpbcgrouptable[k]; + for (j = 0; j < 2; j++) { + if (pd->fmark[j] == idx) { + setshellpbcgroup(newsh, k); + pd->ss[j] = newsh; + } + } } } - if (b->verbose) { - printf(" Incrementally inserting vertices.\n"); + if (aboveflag) { + // Compute the 'abovepoint' for orient3d(). + abovepoint = facetabovepointarray[shmark]; + if (abovepoint == (point) NULL) { + getfacetabovepoint(&newsh); + } } - // Form the DT by incremental flip Delaunay algorithm. - success = incrflipdelaunay(NULL, insertarray, arraysize, true, b->plc, - 0.0, NULL); - - if (b->btree) { - point **pptary; - for (i = 0; i < (int) btreenode_list->objects; i++) { - pptary = (point **) fastlookup(btreenode_list, i); - delete [] *pptary; + if (holes > 0) { + // Project hole points onto the plane containing the facet. + REAL prj[3]; + for (k = 0; k < holes; k++) { + projpt2face(&(holelist[k * 3]), insertarray[0], insertarray[1], + insertarray[2], prj); + for (j = 0; j < 3; j++) holelist[k * 3 + j] = prj[j]; } - delete btreenode_list; - btreenode_list = NULL; } - delete [] insertarray; - - if (!success) { - return 0l; - } else { - return hullsize; + // Incrementally insert the rest of points into T. + for (; i < arraysize; i++) { + // Insert p_i. + startsh.sh = dummysh; + loc = locatesub(insertarray[i], &startsh, 0, 0.0); + if (loc == ONFACE) { + splitsubface(insertarray[i], &startsh, flipque); + } else if (loc == ONEDGE) { + splitsubedge(insertarray[i], &startsh, flipque); + } else if (loc == OUTSIDE) { + collectvisiblesubs(shmark, insertarray[i], &startsh, flipque); + } else if (loc == ONVERTEX) { + // !should not happen! + } + // Set p_i's type FREESUBVERTEX if it has no type yet. + if (pointtype(insertarray[i]) == FREEVOLVERTEX) { + setpointtype(insertarray[i], FREESUBVERTEX); + } + flipsub(flipque); } } -//// //// -//// //// -//// delaunay_cxx ///////////////////////////////////////////////////////////// - -//// surface_cxx ////////////////////////////////////////////////////////////// -//// //// -//// //// - /////////////////////////////////////////////////////////////////////////////// // // -// sinsertvertex() Insert a vertex into a triangulation of a facet. // -// // -// The new point (p) will be located. Searching from 'splitsh'. If 'splitseg'// -// is not NULL, p is on a segment, no search is needed. // +// finddirectionsub() Find the first subface in a facet on the path from // +// one point to another. // // // -// If 'cflag' is not TRUE, the triangulation may be not convex. Don't insert // -// p if it is found in outside. // +// Finds the subface in the facet that intersects a line segment drawn from // +// the origin of `searchsh' to the point `tend', and returns the result in // +// `searchsh'. The origin of `searchsh' does not change, even though the // +// subface returned may differ from the one passed in. // // // -// Comment: This routine assumes the 'abovepoint' of this facet has been set,// -// i.e., the routine getabovepoint() has been executed before it is called. // +// The return value notes whether the destination or apex of the found face // +// is collinear with the two points in question. // // // /////////////////////////////////////////////////////////////////////////////// -enum tetgenmesh::locateresult tetgenmesh::sinsertvertex(point insertpt, - face *splitsh, face *splitseg, bool bwflag, bool cflag) +enum tetgenmesh::finddirectionresult tetgenmesh::finddirectionsub( + face* searchsh, point tend) { - face *abfaces, *parysh, *pssub; - face neighsh, newsh, casout, casin; - face aseg, bseg, aoutseg, boutseg; - face checkseg; - triface neightet; - point pa, pb, pc, *ppt; - enum locateresult loc; - REAL sign, ori, area; - int n, s, i, j; - - if (splitseg != NULL) { - spivot(*splitseg, *splitsh); - loc = ONEDGE; - } else { - // Locate the point, '1' means the flag stop-at-segment is on. - loc = locatesub(insertpt, splitsh, 1, 0); - } - - // Return if p lies on a vertex. - if (loc == ONVERTEX) return loc; - - if (loc == OUTSIDE) { - // Return if 'cflag' is not set. - if (!cflag) return loc; - } + face checksh; + point startpoint, leftpoint, rightpoint; + REAL leftccw, rightccw; + REAL ori, sign; + int leftflag, rightflag; - if (loc == ONEDGE) { - if (splitseg == NULL) { - // Do not split a segment. - sspivot(*splitsh, checkseg); - if (checkseg.sh != dummysh) return loc; // return ONSUBSEG; - // Check if this edge is on the hull. - spivot(*splitsh, neighsh); - if (neighsh.sh == dummysh) { - // A convex hull edge. The new point is on the hull. - loc = OUTSIDE; - } - } - } + startpoint = sorg(*searchsh); + // Find the sign to simulate that abovepoint is 'above' the facet. + adjustedgering(*searchsh, CCW); + // Make sure 'startpoint' is the origin. + if (sorg(*searchsh) != startpoint) senextself(*searchsh); + rightpoint = sdest(*searchsh); + leftpoint = sapex(*searchsh); + ori = orient3d(startpoint, rightpoint, leftpoint, abovepoint); + sign = ori > 0.0 ? -1 : 1; - if (b->verbose > 1) { - pa = sorg(*splitsh); - pb = sdest(*splitsh); - pc = sapex(*splitsh); - printf(" Insert point %d (%d, %d, %d) loc %d\n", pointmark(insertpt), - pointmark(pa), pointmark(pb), pointmark(pc), (int) loc); - } - - // Does 'insertpt' lie on a segment? - if (splitseg != NULL) { - splitseg->shver = 0; - pa = sorg(*splitseg); - // Count the number of faces at segment [a, b]. - n = 0; - neighsh = *splitsh; - do { - spivotself(neighsh); - n++; - } while ((neighsh.sh != dummysh) && (neighsh.sh != splitsh->sh)); - // n is at least 1. - abfaces = new face[n]; - // Collect faces at seg [a, b]. - abfaces[0] = *splitsh; - if (sorg(abfaces[0]) != pa) sesymself(abfaces[0]); - for (i = 1; i < n; i++) { - spivot(abfaces[i - 1], abfaces[i]); - if (sorg(abfaces[i]) != pa) sesymself(abfaces[i]); - } - } - - // Initialize the cavity. - if (loc == ONEDGE) { - smarktest(*splitsh); - caveshlist->newindex((void **) &parysh); - *parysh = *splitsh; - if (splitseg != NULL) { - for (i = 1; i < n; i++) { - smarktest(abfaces[i]); - caveshlist->newindex((void **) &parysh); - *parysh = abfaces[i]; - } + // Is `tend' to the left? + ori = orient3d(tend, startpoint, abovepoint, leftpoint); + leftccw = ori * sign; + leftflag = leftccw > 0.0; + // Is `tend' to the right? + ori = orient3d(startpoint, tend, abovepoint, rightpoint); + rightccw = ori * sign; + rightflag = rightccw > 0.0; + if (leftflag && rightflag) { + // `searchsh' faces directly away from `tend'. We could go left or + // right. Ask whether it's a triangle or a boundary on the left. + senext2(*searchsh, checksh); + spivotself(checksh); + if (checksh.sh == dummysh) { + leftflag = 0; } else { - spivot(*splitsh, neighsh); - if (neighsh.sh != dummysh) { - smarktest(neighsh); - caveshlist->newindex((void **) &parysh); - *parysh = neighsh; - } - } - } else if (loc == ONFACE) { - smarktest(*splitsh); - caveshlist->newindex((void **) &parysh); - *parysh = *splitsh; - } else { // loc == OUTSIDE; - // This is only possible when T is convex. - assert(cflag); // SELF_CHECK - // Adjust 'abovepoint' to be above the 'splitsh'. 2009-07-21. - ori = orient3d(sorg(*splitsh), sdest(*splitsh), sapex(*splitsh), - abovepoint); - assert(ori != 0); - if (ori > 0) { - sesymself(*splitsh); - } - // Assume p is on top of the edge ('splitsh'). Find a right-most edge - // which is visible by p. - neighsh = *splitsh; - while (1) { - senext2self(neighsh); - spivot(neighsh, casout); - if (casout.sh == dummysh) { - // A convex hull edge. Is it visible by p. - pa = sorg(neighsh); - pb = sdest(neighsh); - ori = orient3d(pa, pb, abovepoint, insertpt); - if (ori < 0) { - *splitsh = neighsh; // Update 'splitsh'. - } else { - break; // 'splitsh' is the right-most visible edge. - } - } else { - if (sorg(casout) != sdest(neighsh)) sesymself(casout); - neighsh = casout; - } - } - // Create new triangles for all visible edges of p (from right to left). - casin.sh = dummysh; // No adjacent face at right. - pa = sorg(*splitsh); - pb = sdest(*splitsh); - while (1) { - // Create a new subface on top of the (visible) edge. - makeshellface(subfaces, &newsh); - // setshvertices(newsh, pb, pa, insertpt); - setsorg(newsh, pb); - setsdest(newsh, pa); - setsapex(newsh, insertpt); - setshellmark(newsh, shellmark(*splitsh)); - if (b->quality && varconstraint) { - area = areabound(*splitsh); - setareabound(newsh, area); - } - // Connect the new subface to the bottom subfaces. - sbond1(newsh, *splitsh); - sbond1(*splitsh, newsh); - // Connect the new subface to its right-adjacent subface. - if (casin.sh != dummysh) { - senext(newsh, casout); - sbond1(casout, casin); - sbond1(casin, casout); - } - // The left-adjacent subface has not been created yet. - senext2(newsh, casin); - // Add the new face into list. - smarktest(newsh); - caveshlist->newindex((void **) &parysh); - *parysh = newsh; - // Move to the convex hull edge at the left of 'splitsh'. - neighsh = *splitsh; - while (1) { - senextself(neighsh); - spivot(neighsh, casout); - if (casout.sh == dummysh) { - *splitsh = neighsh; - break; - } - if (sorg(casout) != sdest(neighsh)) sesymself(casout); - neighsh = casout; - } - // A convex hull edge. Is it visible by p. - pa = sorg(*splitsh); - pb = sdest(*splitsh); - ori = orient3d(pa, pb, abovepoint, insertpt); - if (ori >= 0) break; + rightflag = 0; } } - - // Form the Bowyer-Watson cavity. - for (i = 0; i < (int) caveshlist->objects; i++) { - parysh = (face *) fastlookup(caveshlist, i); - for (j = 0; j < 3; j++) { - sspivot(*parysh, checkseg); - if (checkseg.sh == dummysh) { - spivot(*parysh, neighsh); - if (neighsh.sh != dummysh) { - if (!smarktested(neighsh)) { - if (bwflag) { - pa = sorg(neighsh); - pb = sdest(neighsh); - pc = sapex(neighsh); - sign = incircle3d(pa, pb, pc, insertpt); - if (sign < 0) { - smarktest(neighsh); - caveshlist->newindex((void **) &pssub); - *pssub = neighsh; - } - } else { - sign = 1; // A boundary edge. - } - } else { - sign = -1; // Not a boundary edge. - } - } else { - if (loc == OUTSIDE) { - // It is a boundary edge if it does not contain insertp. - if ((sorg(*parysh)==insertpt) || (sdest(*parysh)==insertpt)) { - sign = -1; // Not a boundary edge. - } else { - sign = 1; // A boundary edge. - } - } else { - sign = 1; // A boundary edge. - } - } - } else { - sign = 1; // A segment! - } - if (sign >= 0) { - // Add a boundary edge. - caveshbdlist->newindex((void **) &pssub); - *pssub = *parysh; - } - senextself(*parysh); + while (leftflag) { + // Turn left until satisfied. + senext2self(*searchsh); + spivotself(*searchsh); + if (searchsh->sh == dummysh) { + printf("Internal error in finddirectionsub(): Unable to find a\n"); + printf(" subface leading from %d to %d.\n", pointmark(startpoint), + pointmark(tend)); + internalerror(); } + if (sorg(*searchsh) != startpoint) sesymself(*searchsh); + assert(sorg(*searchsh) == startpoint); + leftpoint = sapex(*searchsh); + rightccw = leftccw; + ori = orient3d(tend, startpoint, abovepoint, leftpoint); + leftccw = ori * sign; + leftflag = leftccw > 0.0; } - - // Creating new subfaces. - for (i = 0; i < (int) caveshbdlist->objects; i++) { - parysh = (face *) fastlookup(caveshbdlist, i); - sspivot(*parysh, checkseg); - if ((parysh->shver & 01) != 0) sesymself(*parysh); - pa = sorg(*parysh); - pb = sdest(*parysh); - // Create a new subface. - makeshellface(subfaces, &newsh); - // setshvertices(newsh, pa, pb, insertpt); - setsorg(newsh, pa); - setsdest(newsh, pb); - setsapex(newsh, insertpt); - setshellmark(newsh, shellmark(*parysh)); - if (b->quality && varconstraint) { - area = areabound(*parysh); - setareabound(newsh, area); - } - // Connect newsh to outer subfaces. - spivot(*parysh, casout); - if (casout.sh != dummysh) { - if (casout.sh != parysh->sh) { // It is not self-bonded. - casin = casout; - if (checkseg.sh != dummysh) { - spivot(casin, neighsh); - while (neighsh.sh != parysh->sh) { - casin = neighsh; - spivot(casin, neighsh); - } - } - sbond1(newsh, casout); - sbond1(casin, newsh); - } else { - // This side is empty. - } - } else { - // This is a hull side. Save it in dummysh[0] (it will be used by - // the routine locatesub()). 2009-07-20. - dummysh[0] = sencode(newsh); - } - if (checkseg.sh != dummysh) { - ssbond(newsh, checkseg); - } - // Connect oldsh <== newsh (for connecting adjacent new subfaces). - sbond1(*parysh, newsh); - } - - // Set a handle for searching. - // recentsh = newsh; - - // Connect adjacent new subfaces together. - for (i = 0; i < (int) caveshbdlist->objects; i++) { - // Get an old subface at edge [a, b]. - parysh = (face *) fastlookup(caveshbdlist, i); - sspivot(*parysh, checkseg); - spivot(*parysh, newsh); // The new subface [a, b, p]. - senextself(newsh); // At edge [b, p]. - spivot(newsh, neighsh); - if (neighsh.sh == dummysh) { - // Find the adjacent new subface at edge [b, p]. - pb = sdest(*parysh); - neighsh = *parysh; - while (1) { - senextself(neighsh); - spivotself(neighsh); - if (neighsh.sh == dummysh) break; - if (!smarktested(neighsh)) break; - if (sdest(neighsh) != pb) sesymself(neighsh); - } - if (neighsh.sh != dummysh) { - // 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]. - spivot(neighsh, casout); // SELF_CHECK - assert(casout.sh == dummysh); // SELF_CHECK - sbond(newsh, neighsh); - } else { - assert(loc == OUTSIDE); // SELF_CHECK - // It is a hull edge. 2009-07-21 - dummysh[0] = sencode(newsh); - } - } - spivot(*parysh, newsh); // The new subface [a, b, p]. - senext2self(newsh); // At edge [p, a]. - spivot(newsh, neighsh); - if (neighsh.sh == dummysh) { - // Find the adjacent new subface at edge [p, a]. - pa = sorg(*parysh); - neighsh = *parysh; - while (1) { - senext2self(neighsh); - spivotself(neighsh); - if (neighsh.sh == dummysh) break; - if (!smarktested(neighsh)) break; - if (sorg(neighsh) != pa) sesymself(neighsh); - } - if (neighsh.sh != dummysh) { - // 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]. - spivot(neighsh, casout); // SELF_CHECK - assert(casout.sh == dummysh); // SELF_CHECK - sbond(newsh, neighsh); - } else { - assert(loc == OUTSIDE); // SELF_CHECK - // It is a hull edge. 2009-07-21 - dummysh[0] = sencode(newsh); - } + while (rightflag) { + // Turn right until satisfied. + spivotself(*searchsh); + if (searchsh->sh == dummysh) { + printf("Internal error in finddirectionsub(): Unable to find a\n"); + printf(" subface leading from %d to %d.\n", pointmark(startpoint), + pointmark(tend)); + internalerror(); } + if (sdest(*searchsh) != startpoint) sesymself(*searchsh); + assert(sdest(*searchsh) == startpoint); + senextself(*searchsh); + rightpoint = sdest(*searchsh); + leftccw = rightccw; + ori = orient3d(startpoint, tend, abovepoint, rightpoint); + rightccw = ori * sign; + rightflag = rightccw > 0.0; } - - if (splitseg != NULL) { - // Split the segment [a, b]. - aseg = *splitseg; - pa = sorg(aseg); - pb = sdest(aseg); - if (b->verbose > 1) { - printf(" Split seg (%d, %d) by %d.\n", pointmark(pa), pointmark(pb), - pointmark(insertpt)); - } - // Insert the new point p. - makeshellface(subsegs, &bseg); - // setshvertices(bseg, insertpt, pb, NULL); - setsorg(bseg, insertpt); - setsdest(bseg, pb); - setsapex(bseg, NULL); - setsdest(aseg, insertpt); - setshellmark(bseg, shellmark(aseg)); - // This is done outside this routine (at where newpt was created). - // setpoint2sh(insertpt, sencode(aseg)); - if (b->quality && varconstraint) { - setareabound(bseg, areabound(aseg)); - } - // Update the point-to-seg map. - setpoint2seg(pb, sencode(bseg)); - setpoint2seg(insertpt, sencode(bseg)); - // Connect [p, b]<->[b, #]. - senext(aseg, aoutseg); - spivotself(aoutseg); - if (aoutseg.sh != dummysh) { - senext(bseg, boutseg); - sbond(boutseg, aoutseg); - } - // Connect [a, p] <-> [p, b]. - senext(aseg, aoutseg); - senext2(bseg, boutseg); - sbond(aoutseg, boutseg); - // Connect subsegs [a, p] and [p, b] to the true new subfaces. - for (i = 0; i < n; i++) { - spivot(abfaces[i], newsh); // The faked new subface. - if (sorg(newsh) != pa) sesymself(newsh); - senext2(newsh, neighsh); // The edge [p, a] in newsh - spivot(neighsh, casout); - ssbond(casout, aseg); - senext(newsh, neighsh); // The edge [b, p] in newsh - spivot(neighsh, casout); - ssbond(casout, bseg); - } - if (n > 1) { - // Create the two face rings at [a, p] and [p, b]. - for (i = 0; i < n; i++) { - spivot(abfaces[i], newsh); // The faked new subface. - if (sorg(newsh) != pa) sesymself(newsh); - spivot(abfaces[(i + 1) % n], neighsh); // The next faked new subface. - if (sorg(neighsh) != pa) sesymself(neighsh); - senext2(newsh, casout); // The edge [p, a] in newsh. - senext2(neighsh, casin); // The edge [p, a] in neighsh. - spivotself(casout); - spivotself(casin); - sbond1(casout, casin); // Let the i's face point to (i+1)'s face. - senext(newsh, casout); // The edge [b, p] in newsh. - senext(neighsh, casin); // The edge [b, p] in neighsh. - spivotself(casout); - spivotself(casin); - sbond1(casout, casin); - } - } else { - // Only one subface contains this segment. - // assert(n == 1); - spivot(abfaces[0], newsh); // The faked new subface. - if (sorg(newsh) != pa) sesymself(newsh); - senext2(newsh, casout); // The edge [p, a] in newsh. - spivotself(casout); - sdissolve(casout); // Disconnect to faked subface. - senext(newsh, casout); // The edge [b, p] in newsh. - spivotself(casout); - sdissolve(casout); // Disconnect to faked subface. - } - // Delete the faked new subfaces. - for (i = 0; i < n; i++) { - spivot(abfaces[i], newsh); // The faked new subface. - shellfacedealloc(subfaces, newsh.sh); - } - if (checksubsegs) { - // Add two subsegs into stack (for recovery). - if (!sinfected(aseg)) { - s = randomnation(subsegstack->objects + 1); - subsegstack->newindex((void **) &parysh); - *parysh = * (face *) fastlookup(subsegstack, s); - sinfect(aseg); - parysh = (face *) fastlookup(subsegstack, s); - *parysh = aseg; - } - assert(!sinfected(bseg)); // SELF_CHECK - s = randomnation(subsegstack->objects + 1); - subsegstack->newindex((void **) &parysh); - *parysh = * (face *) fastlookup(subsegstack, s); - sinfect(bseg); - parysh = (face *) fastlookup(subsegstack, s); - *parysh = bseg; - } - delete [] abfaces; + if (leftccw == 0.0) { + return LEFTCOLLINEAR; + } else if (rightccw == 0.0) { + return RIGHTCOLLINEAR; + } else { + return ACROSSEDGE; } +} - if (checksubfaces) { - // Add all new subfaces into list. - for (i = 0; i < (int) caveshbdlist->objects; i++) { - // Get an old subface at edge [a, b]. - parysh = (face *) fastlookup(caveshbdlist, i); - spivot(*parysh, newsh); // The new subface [a, b, p]. - // Some new subfaces may get deleted (when 'splitseg' is a segment). - if (!isdead(&newsh)) { - if (b->verbose > 1) { - printf(" Queue a new subface (%d, %d, %d).\n", - pointmark(sorg(newsh)), pointmark(sdest(newsh)), - pointmark(sapex(newsh))); - } - subfacstack->newindex((void **) &pssub); - *pssub = newsh; - } - } - } +/////////////////////////////////////////////////////////////////////////////// +// // +// insertsubseg() Create a subsegment and insert it between two subfaces. // +// // +// The new subsegment ab is inserted at the edge of subface 'tri'. If ab is // +// not a hull edge, it is inserted between two subfaces. If 'tri' is a hull // +// face, the initial face ring of ab will be set only one face which is self-// +// bonded. The final face ring will be constructed in 'unifysegments()'. // +// // +/////////////////////////////////////////////////////////////////////////////// - // Update the point-to-subface map. - for (i = 0; i < (int) caveshbdlist->objects; i++) { - // Get an old subface at edge [a, b]. - parysh = (face *) fastlookup(caveshbdlist, i); - spivot(*parysh, newsh); // The new subface [a, b, p]. - // Some new subfaces may get deleted (when 'splitseg' is a segment). - if (!isdead(&newsh)) { - ppt = (point *) &(newsh.sh[3]); - for (j = 0; j < 3; j++) { - setpoint2sh(ppt[j], sencode(newsh)); - } - } - } +void tetgenmesh::insertsubseg(face* tri) +{ + face oppotri; + face newsubseg; + point pa, pb; + REAL len; + int e1, e2; + int i; - // Delete the old subfaces. - for (i = 0; i < (int) caveshlist->objects; i++) { - parysh = (face *) fastlookup(caveshlist, i); - if (checksubfaces) { - // Disconnect in the neighbor tets. - for (j = 0; j < 2; j++) { - stpivot(*parysh, neightet); - if (neightet.tet != dummytet) { - tsdissolve(neightet); - // symself(neightet); - // tsdissolve(neightet); + // Check if there's already a subsegment here. + sspivot(*tri, newsubseg); + if (newsubseg.sh == dummysh) { + // Make new subsegment and initialize its vertices. + makeshellface(subsegs, &newsubseg); + pa = sorg(*tri); + pb = sdest(*tri); + setsorg(newsubseg, pa); + setsdest(newsubseg, pb); + // Are there length constraints? + 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(newsubseg, len); + break; } - sesymself(*parysh); } } - shellfacedealloc(subfaces, parysh->sh); + // Bond new subsegment to the two subfaces it is sandwiched between. + ssbond(*tri, newsubseg); + spivot(*tri, oppotri); + // 'oppotri' might be "out space". + if (oppotri.sh != dummysh) { + ssbond(oppotri, newsubseg); + } /* else { + // Outside! Bond '*tri' to itself. + sbond(*tri, *tri); + } */ } - - // Clean the working lists. - caveshlist->restart(); - caveshbdlist->restart(); - - return loc; } /////////////////////////////////////////////////////////////////////////////// // // -// formstarpolygon() Form the star polygon of a point in facet. // -// // -// The polygon P is formed by all coplanar subfaces having 'pt' as a vertex. // -// P is bounded by segments, e.g, if no segments, P is the full star of pt. // +// scoutsegmentsub() Scout the first triangle on the path from one point // +// to another, and check for completion (reaching the // +// second point), a collinear point,or the intersection // +// of two segments. // // // -// 'trilist' T returns the subfaces, it has one of such subfaces on input. // -// In addition, if f is in T, then sapex(f) = p. 'vertlist' V are verts of P.// -// Topologically, T is the star of p; V and the edges of T are the link of p.// +// Returns true if the entire segment is successfully inserted, and false if // +// the job must be finished by constrainededge(). // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::formstarpolygon(point pt, list* trilist, list* vertlist) +bool tetgenmesh::scoutsegmentsub(face* searchsh, point tend) { - face steinsh, lnextsh, rnextsh; - face checkseg; - point pa, pb, pc, pd; - int i; - - // Get a subface f containing p. - steinsh = * (face *)(* trilist)[0]; - steinsh.shver = 0; // CCW - // Let sapex(f) be p. - for (i = 0; i < 3; i++) { - if (sapex(steinsh) == pt) break; - senextself(steinsh); - } - assert(i < 3); - // Add the edge f into list. - * (face *)(* trilist)[0] = steinsh; - pa = sorg(steinsh); - pb = sdest(steinsh); - if (vertlist != (list *) NULL) { - // Add two verts a, b into V, - vertlist->append(&pa); - vertlist->append(&pb); - } + face newsubseg; + face crosssub, crosssubseg; + point leftpoint, rightpoint; + enum finddirectionresult collinear; - // Rotate edge pa to the left (CW) until meet pb or a segment. - lnextsh = steinsh; - pc = pa; - do { - senext2self(lnextsh); - assert(sorg(lnextsh) == pt); - sspivot(lnextsh, checkseg); - if (checkseg.sh != dummysh) break; // Do not cross a segment. - // Get neighbor subface n (must exist). - spivotself(lnextsh); - if (lnextsh.sh == dummysh) break; // It's a hull edge. - // Go to the edge ca opposite to p. - if (sdest(lnextsh) != pt) sesymself(lnextsh); - assert(sdest(lnextsh) == pt); - senext2self(lnextsh); - // Add n (at edge ca) to T. - trilist->append(&lnextsh); - // Add edge ca to E. - pc = sorg(lnextsh); - if (pc == pb) break; // Rotate back. - if (vertlist != (list *) NULL) { - // Add vert c into V. - vertlist->append(&pc); + collinear = finddirectionsub(searchsh, tend); + rightpoint = sdest(*searchsh); + leftpoint = sapex(*searchsh); + if (rightpoint == tend || leftpoint == tend) { + // The segment is already an edge. + if (leftpoint == tend) { + senext2self(*searchsh); } - } while (true); - - if (pc != pb) { - // Rotate edge bp to the right (CCW) until meet a segment. - rnextsh = steinsh; - do { - senextself(rnextsh); - assert(sdest(rnextsh) == pt); - sspivot(rnextsh, checkseg); - if (checkseg.sh != dummysh) break; // Do not cross a segment. - // Get neighbor subface n (must exist). - spivotself(rnextsh); - if (rnextsh.sh == dummysh) break; // It's a hull edge. - // Go to the edge bd opposite to p. - if (sorg(rnextsh) != pt) sesymself(rnextsh); - assert(sorg(rnextsh) == pt); - senextself(rnextsh); - // Add n (at edge bd) to T. - trilist->append(&rnextsh); - // Add edge bd to E. - pd = sdest(rnextsh); - if (pd == pa) break; // Rotate back. - if (vertlist != (list *) NULL) { - // Add vert d into V. - vertlist->append(&pd); - } - } while (true); + // Insert a subsegment. + insertsubseg(searchsh); + return true; + } else if (collinear == LEFTCOLLINEAR) { + // We've collided with a vertex between the segment's endpoints. + // Make the collinear vertex be the triangle's origin. + senextself(*searchsh); // lprevself(*searchtri); + // Insert a subsegment. + insertsubseg(searchsh); + // Insert the remainder of the segment. + return scoutsegmentsub(searchsh, tend); + } else if (collinear == RIGHTCOLLINEAR) { + // We've collided with a vertex between the segment's endpoints. + // Insert a subsegment. + insertsubseg(searchsh); + // Make the collinear vertex be the triangle's origin. + senextself(*searchsh); // lnextself(*searchtri); + // Insert the remainder of the segment. + return scoutsegmentsub(searchsh, tend); + } else { + senext(*searchsh, crosssub); // lnext(*searchtri, crosstri); + // Check for a crossing segment. + sspivot(crosssub, crosssubseg); +#ifdef SELF_CHECK + assert(crosssubseg.sh == dummysh); +#endif + return false; } } /////////////////////////////////////////////////////////////////////////////// // // -// About the 'abovepoint' // +// flipedgerecursive() Flip an edge. // // // -// The 'abovepoint' of a facet is a point which is exactly non-coplanar with // -// the plane containing that facet. With such an point, the 3D predicates: // -// orient3d(), and insphere() can be used to substitute the corresponding 2D // -// siblings, e.g. orient2d(), and incircle(). Its location is not critical, // -// but floating-point accuracy is improved if it is nicely placed over the // -// facet, not too close or too far away. // +// This is a support routine for inserting segments into a CDT. // // // -// We take the convention that the abovepoint of a facet always lies above // -// the facet. By this convention, given three points a, b, and c in a facet, // -// we say c has the counterclockwise order with ab is corresponding to say // -// that c is below the plane abp, where p is the lift point. // +// Let 'flipedge' be ab, and two triangles abc, abd share at it. ab may not // +// flipable if the four vertices a, b, c, and d are non-convex. If it is the // +// case, recursively flip ad or bd. Return when ab is flipped. // // // /////////////////////////////////////////////////////////////////////////////// +void tetgenmesh::flipedgerecursive(face* flipedge, queue* flipqueue) +{ + face fixupsh; + point pa, pb, pc, pd; + REAL oria, orib; + bool doflip; + + pa = sorg(*flipedge); + pb = sdest(*flipedge); + pc = sapex(*flipedge); + do { + spivot(*flipedge, fixupsh); + pd = sapex(fixupsh); + oria = orient3d(pc, pd, abovepoint, pa); + orib = orient3d(pc, pd, abovepoint, pb); + doflip = (oria * orib < 0.0); + if (doflip) { + // Flip the edge (a, b) away. + flip22sub(flipedge, flipqueue); + // Fix flipedge on edge e (c, d). + findedge(flipedge, pc, pd); + } else { + // ab is unflipable. Get the next edge (bd, or da) to flip. + if (sorg(fixupsh) != pb) sesymself(fixupsh); + assert(sdest(fixupsh) == pa); + if (fabs(oria) > fabs(orib)) { + // acd has larger area. Choose da. + senextself(fixupsh); + } else { + // bcd has larger area. Choose bd. + senext2self(fixupsh); + } + // Flip the edge. + flipedgerecursive(&fixupsh, flipqueue); + } + } while (!doflip); +} + /////////////////////////////////////////////////////////////////////////////// // // -// getfacetabovepoint() Get a point above a plane pass through a facet. // +// constrainededge() Force a segment into a CDT. // // // -// The calculcated point is saved in 'facetabovepointarray'. The 'abovepoint'// -// is set on return. // +// The segment s is recovered by flipping away the edges it intersects, and // +// triangulating the polygons that form on each side of it. // +// // +// Generates a single subsegment connecting `tstart' to `tend'. The triangle // +// `startsh' has `tstart' as its origin. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::getfacetabovepoint(face* facetsh) +void tetgenmesh::constrainededge(face* startsh, point tend, queue* flipqueue) { - list *verlist, *trilist, *tetlist; - triface adjtet; - face symsh; - point p1, p2, p3, pa; - // enum locateresult loc; - REAL smallcos, cosa; - REAL largevol, volume; - REAL v1[3], v2[3], len; - int smallidx, largeidx; - int shmark; - int i, j; - - abovecount++; - // Initialize working lists. - verlist = new list(sizeof(point *), NULL); - trilist = new list(sizeof(face), NULL); - tetlist = new list(sizeof(triface), NULL); - - // Get three pivotal points p1, p2, and p3 in the facet as a base triangle - // which is non-trivil and has good base angle (close to 90 degree). - - // p1 is chosen as the one which has the smallest index in pa, pb, pc. - p1 = sorg(*facetsh); - pa = sdest(*facetsh); - if (pointmark(pa) < pointmark(p1)) p1 = pa; - pa = sapex(*facetsh); - if (pointmark(pa) < pointmark(p1)) p1 = pa; - // Form the star polygon of p1. - trilist->append(facetsh); - formstarpolygon(p1, trilist, verlist); - - // Get the second pivotal point p2. - p2 = * (point *)(* verlist)[0]; - // Get vector v1 = p1->p2. - for (i = 0; i < 3; i++) v1[i] = p2[i] - p1[i]; - len = sqrt(dot(v1, v1)); - assert(len > 0.0); // p2 != p1. - for (i = 0; i < 3; i++) v1[i] /= len; - - // Get the third pivotal point p3. p3 is chosen as the one in 'verlist' - // which forms an angle with v1 closer to 90 degree than others do. - smallcos = 1.0; // The cosine value of 0 degree. - smallidx = 1; // Default value. - for (i = 1; i < verlist->len(); i++) { - p3 = * (point *)(* verlist)[i]; - for (j = 0; j < 3; j++) v2[j] = p3[j] - p1[j]; - len = sqrt(dot(v2, v2)); - if (len > 0.0) { // v2 is not too small. - cosa = fabs(dot(v1, v2)) / len; - if (cosa < smallcos) { - smallidx = i; - smallcos = cosa; + point tstart, tright, tleft; + REAL rori, lori; + bool collision; + + tstart = sorg(*startsh); + do { + // Loop edges oppo to tstart until find one crosses the segment. + do { + tright = sdest(*startsh); + tleft = sapex(*startsh); + // Is edge (tright, tleft) corss the segment. + rori = orient3d(tstart, tright, abovepoint, tend); + collision = (rori == 0.0); + if (collision) break; // tright is on the segment. + lori = orient3d(tstart, tleft, abovepoint, tend); + collision = (lori == 0.0); + if (collision) { // tleft is on the segment. + senext2self(*startsh); + break; } - } - } - assert(smallcos < 1.0); // p1->p3 != p1->p2. - p3 = * (point *)(* verlist)[smallidx]; - verlist->clear(); + if (rori * lori < 0.0) break; // Find the crossing edge. + // Both points are at one side of the segment. + finddirectionsub(startsh, tend); + } while (true); + if (collision) break; + // Get the neighbor face at edge e (tright, tleft). + senextself(*startsh); + // Flip the crossing edge. + flipedgerecursive(startsh, flipqueue); + // After flip, sorg(*startsh) == tstart. + assert(sorg(*startsh) == tstart); + } while (sdest(*startsh) != tend); - if (tetrahedrons->items > 0l) { - // Get a tet having p1 as a vertex. - point2tetorg(p1, adjtet); - assert(org(adjtet) == p1); - if (adjtet.tet != dummytet) { - // Get the star polyhedron of p1. - tetlist->append(&adjtet); - formstarpolyhedron(p1, tetlist, verlist, false); + // Insert a subsegment to make the segment permanent. + insertsubseg(startsh); + // If there was a collision with an interceding vertex, install another + // segment connecting that vertex with endpoint2. + if (collision) { + // Insert the remainder of the segment. + if (!scoutsegmentsub(startsh, tend)) { + constrainededge(startsh, tend, flipqueue); } } +} - // Get the abovepoint in 'verlist'. It is the one form the largest valid - // volumw with the base triangle over other points in 'verlist. - largevol = 0.0; - largeidx = 0; - for (i = 0; i < verlist->len(); i++) { - pa = * (point *)(* verlist)[i]; - volume = orient3d(p1, p2, p3, pa); - if (!iscoplanar(p1, p2, p3, pa, volume, b->epsilon * 1e+2)) { - if (fabs(volume) > largevol) { - largevol = fabs(volume); - largeidx = i; - } - } +/////////////////////////////////////////////////////////////////////////////// +// // +// recoversegment() Recover a segment in the surface triangulation. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::recoversegment(point tstart, point tend, queue* flipqueue) +{ + face searchsh; + + if (b->verbose > 2) { + printf(" Insert seg (%d, %d).\n", pointmark(tstart), pointmark(tend)); } - // Do we have the abovepoint? - if (largevol > 0.0) { - abovepoint = * (point *)(* verlist)[largeidx]; - if (b->verbose > 1) { - printf(" Chosen abovepoint %d for facet %d.\n", pointmark(abovepoint), - shellmark(*facetsh)); + // Find a triangle whose origin is the segment's first endpoint. + searchsh.sh = dummysh; + // Search for the segment's first endpoint by point location. + if (locatesub(tstart, &searchsh, 0, 0.0) != ONVERTEX) { + // Possibly caused by a degenerate subface. Do a brute-force search. + list *newshlist; + int i, j; + newshlist = new list(sizeof(face), NULL, 256); + // Get new subfaces, do not remove protected segments. + retrievenewsubs(newshlist, false); + // Search for a sub contain tstart. + for (i = 0; i < newshlist->len(); i++) { + searchsh = * (face *)(* newshlist)[i]; + for (j = 0; j < 3; j++) { + if (sorg(searchsh) == tstart) break; + senextself(searchsh); + } + if (j < 3) break; } - } else { - // Calculate an abovepoint for this facet. - facenormal(p1, p2, p3, v1, &len); - if (len != 0.0) for (i = 0; i < 3; i++) v1[i] /= len; - // Take the average edge length of the bounding box. - len = (0.5*(xmax - xmin) + 0.5*(ymax - ymin) + 0.5*(zmax - zmin)) / 3.0; - // Temporarily create a point. It will be removed by jettison(); - makepoint(&abovepoint); - setpointtype(abovepoint, UNUSEDVERTEX); - unuverts++; - for (i = 0; i < 3; i++) abovepoint[i] = p1[i] + len * v1[i]; - if (b->verbose > 1) { - printf(" Calculated abovepoint %d for facet %d.\n", - pointmark(abovepoint), shellmark(*facetsh)); + delete newshlist; + if (sorg(searchsh) != tstart) { + printf("Internal error in recoversegment(): Vertex location failed.\n"); + internalerror(); } } - // Save the abovepoint in 'facetabovepointarray'. - shmark = shellmark(*facetsh); - facetabovepointarray[shmark] = abovepoint; - - delete trilist; - delete tetlist; - delete verlist; + // Scout the segment and insert it if it is found. + if (scoutsegmentsub(&searchsh, tend)) { + // The segment was easily inserted. + return; + } + // Insert the segment into the triangulation by flips. + constrainededge(&searchsh, tend, flipqueue); + // Some edges may need flipping. + flipsub(flipqueue); } /////////////////////////////////////////////////////////////////////////////// // // -// incrflipdelaunaysub() Create a DT from a 3D coplanar point set using // -// the incremental flip algorithm. // -// // -// Let T be the current Delaunay triangulation (of vertices of a facet F). // -// 'shmark', the index of F in 'in->facetlist' (starts from 1). // +// infecthullsub() Virally infect all of the triangles of the convex hull // +// that are not protected by subsegments. // // // /////////////////////////////////////////////////////////////////////////////// -bool tetgenmesh::incrflipdelaunaysub(int shmark, REAL eps, list* ptlist, - int holes, REAL* holelist, queue* flipque) +void tetgenmesh::infecthullsub(memorypool* viri) { - face newsh, startsh; - point *insertarray; - point swappt; - pbcdata *pd; - enum locateresult loc; - REAL det, area; - bool aboveflag; - int arraysize; - int epscount; - int fmarker; - int idx, i, j, k; + face hulltri, nexttri, starttri; + face hullsubseg; + shellface **deadshellface; - // Get the point array (saved in 'ptlist'). - insertarray = (point *) ptlist->base; - arraysize = ptlist->len(); - if (arraysize < 3) return false; + // Find a triangle handle on the hull. + hulltri.sh = dummysh; + hulltri.shver = 0; + spivotself(hulltri); + adjustedgering(hulltri, CCW); + // Remember where we started so we know when to stop. + starttri = hulltri; + // Go once counterclockwise around the convex hull. + do { + // Ignore triangles that are already infected. + if (!sinfected(hulltri)) { + // Is the triangle protected by a subsegment? + sspivot(hulltri, hullsubseg); + if (hullsubseg.sh == dummysh) { + // The triangle is not protected; infect it. + if (!sinfected(hulltri)) { + sinfect(hulltri); + deadshellface = (shellface **) viri->alloc(); + *deadshellface = hulltri.sh; + } + } + } + // To find the next hull edge, go clockwise around the next vertex. + senextself(hulltri); // lnextself(hulltri); + spivot(hulltri, nexttri); // oprev(hulltri, nexttri); + if (nexttri.sh == hulltri.sh) { + nexttri.sh = dummysh; // 'hulltri' is self-bonded. + } else { + adjustedgering(nexttri, CCW); + senextself(nexttri); + } + while (nexttri.sh != dummysh) { + hulltri = nexttri; + spivot(hulltri, nexttri); // oprev(hulltri, nexttri); + if (nexttri.sh == hulltri.sh) { + nexttri.sh = dummysh; // 'hulltri' is self-bonded. + } else { + adjustedgering(nexttri, CCW); + senextself(nexttri); + } + } + } while (hulltri != starttri); +} - // Do calculation of 'abovepoint' if number of points > 3. - aboveflag = (arraysize > 3); +/////////////////////////////////////////////////////////////////////////////// +// // +// plaguesub() Spread the virus from all infected triangles to any // +// neighbors not protected by subsegments. Delete all // +// infected triangles. // +// // +// This is the procedure that actually creates holes and concavities. // +// // +/////////////////////////////////////////////////////////////////////////////// - // The initial triangulation T only has one triangle formed by 3 not - // cillinear points of the set V = 'insertarray'. The first point: - // a = insertarray[0]. +void tetgenmesh::plaguesub(memorypool* viri) +{ + face testtri, neighbor, ghostsh; + face neighborsubseg; + shellface **virusloop; + shellface **deadshellface; + int i; - epscount = 0; - while (true) { - for (i = 1; i < arraysize; i++) { - det = distance(insertarray[0], insertarray[i]); - if (det > (longest * eps)) break; - } - if (i < arraysize) { - // Swap to move b from index i to index 1. - swappt = insertarray[i]; - insertarray[i] = insertarray[1]; - insertarray[1] = swappt; - } - // Get the third point c, that is not collinear with a and b. - for (i++; i < arraysize; i++) { - if (!iscollinear(insertarray[0], insertarray[1], insertarray[i], eps)) - break; - } - if (i < arraysize) { - // Swap to move c from index i to index 2. - swappt = insertarray[i]; - insertarray[i] = insertarray[2]; - insertarray[2] = swappt; - i = 3; // The next inserting point. - } else { - // The set of vertices is not good (or nearly degenerate). - if ((eps == 0.0) || (epscount > 3)) { - printf("Warning: Discard an invalid facet.\n"); - printf(" #%d (%d, %d, %d", shmark, pointmark(insertarray[0]), - pointmark(insertarray[1]), pointmark(insertarray[2])); - if (ptlist->len() > 3) { - printf(", ..."); + // Loop through all the infected triangles, spreading the virus to + // their neighbors, then to their neighbors' neighbors. + viri->traversalinit(); + virusloop = (shellface **) viri->traverse(); + while (virusloop != (shellface **) NULL) { + testtri.sh = *virusloop; + // Check each of the triangle's three neighbors. + for (i = 0; i < 3; i++) { + // Find the neighbor. + spivot(testtri, neighbor); + // Check for a subsegment between the triangle and its neighbor. + sspivot(testtri, neighborsubseg); + // Check if the neighbor is nonexistent or already infected. + if ((neighbor.sh == dummysh) || sinfected(neighbor)) { + if (neighborsubseg.sh != dummysh) { + // There is a subsegment separating the triangle from its + // neighbor, but both triangles are dying, so the subsegment + // dies too. + shellfacedealloc(subsegs, neighborsubseg.sh); + if (neighbor.sh != dummysh) { + // Make sure the subsegment doesn't get deallocated again + // later when the infected neighbor is visited. + ssdissolve(neighbor); + } + } + } else { // The neighbor exists and is not infected. + if (neighborsubseg.sh == dummysh) { + // There is no subsegment protecting the neighbor, so the + // neighbor becomes infected. + sinfect(neighbor); + // Ensure that the neighbor's neighbors will be infected. + deadshellface = (shellface **) viri->alloc(); + *deadshellface = neighbor.sh; + } else { // The neighbor is protected by a subsegment. + // Remove this triangle from the subsegment. + ssbond(neighbor, neighborsubseg); + } } - printf(") looks like a line.\n"); - // terminatetetgen(1); - return false; + senextself(testtri); } - // Decrease the eps, and continue to try. - eps *= 1e-2; - epscount++; - continue; - } - break; - } // while (true); - - // Create the initial triangle. - makeshellface(subfaces, &newsh); - setsorg(newsh, insertarray[0]); - setsdest(newsh, insertarray[1]); - setsapex(newsh, insertarray[2]); - // Remeber the facet it belongs to. - setshellmark(newsh, shmark); - // Set vertex type be FREESUBVERTEX if it has no type yet. - if (pointtype(insertarray[0]) == FREEVOLVERTEX) { - setpointtype(insertarray[0], FREESUBVERTEX); - } - if (pointtype(insertarray[1]) == FREEVOLVERTEX) { - setpointtype(insertarray[1], FREESUBVERTEX); - } - if (pointtype(insertarray[2]) == FREEVOLVERTEX) { - setpointtype(insertarray[2], FREESUBVERTEX); - } - // Let 'dummysh' point to it (for point location). - dummysh[0] = sencode(newsh); - - // Update the point-to-subface map. - for (i = 0; i < 3; i++) { - setpoint2sh(insertarray[i], sencode(newsh)); + virusloop = (shellface **) viri->traverse(); } - // Are there area constraints? - if (b->quality && (in->facetconstraintlist != (REAL *) NULL)) { - idx = in->facetmarkerlist[shmark - 1]; // The actual facet marker. - for (k = 0; k < in->numberoffacetconstraints; k++) { - fmarker = (int) in->facetconstraintlist[k * 2]; - if (fmarker == idx) { - area = in->facetconstraintlist[k * 2 + 1]; - setareabound(newsh, area); - break; + ghostsh.sh = dummysh; // A handle of outer space. + viri->traversalinit(); + virusloop = (shellface **) viri->traverse(); + while (virusloop != (shellface **) NULL) { + testtri.sh = *virusloop; + // Record changes in the number of boundary edges, and disconnect + // dead triangles from their neighbors. + for (i = 0; i < 3; i++) { + spivot(testtri, neighbor); + if (neighbor.sh != dummysh) { + // Disconnect the triangle from its neighbor. + // sdissolve(neighbor); + sbond(neighbor, ghostsh); } + senextself(testtri); } + // Return the dead triangle to the pool of triangles. + shellfacedealloc(subfaces, testtri.sh); + virusloop = (shellface **) viri->traverse(); } + // Empty the virus pool. + viri->restart(); +} - // Are there pbc conditions? - if (checkpbcs) { - idx = in->facetmarkerlist[shmark - 1]; // The actual facet marker. - for (k = 0; k < in->numberofpbcgroups; k++) { - pd = &subpbcgrouptable[k]; - for (j = 0; j < 2; j++) { - if (pd->fmark[j] == idx) { - setshellpbcgroup(newsh, k); - pd->ss[j] = newsh; - } - } - } - } +/////////////////////////////////////////////////////////////////////////////// +// // +// carveholessub() Find the holes and infect them. Find the area // +// constraints and infect them. Infect the convex hull. // +// Spread the infection and kill triangles. Spread the // +// area constraints. // +// // +// This routine mainly calls other routines to carry out all these functions.// +// // +/////////////////////////////////////////////////////////////////////////////// - if (aboveflag) { - // Compute the 'abovepoint' for orient3d(). - abovepoint = facetabovepointarray[shmark]; - if (abovepoint == (point) NULL) { - getfacetabovepoint(&newsh); - } - } +void tetgenmesh::carveholessub(int holes, REAL* holelist, memorypool *viri) +{ + face searchtri, triangleloop; + shellface **holetri; + enum locateresult intersect; + int i; + + // Mark as infected any unprotected triangles on the boundary. + // This is one way by which concavities are created. + infecthullsub(viri); if (holes > 0) { - // Project hole points onto the plane containing the facet. - REAL prj[3]; - for (k = 0; k < holes; k++) { - projpt2face(&(holelist[k * 3]), insertarray[0], insertarray[1], - insertarray[2], prj); - for (j = 0; j < 3; j++) holelist[k * 3 + j] = prj[j]; + // Infect each triangle in which a hole lies. + for (i = 0; i < 3 * holes; i += 3) { + // Ignore holes that aren't within the bounds of the mesh. + if ((holelist[i] >= xmin) && (holelist[i] <= xmax) + && (holelist[i + 1] >= ymin) && (holelist[i + 1] <= ymax) + && (holelist[i + 2] >= zmin) && (holelist[i + 2] <= zmax)) { + // Start searching from some triangle on the outer boundary. + searchtri.sh = dummysh; + // Find a triangle that contains the hole. + intersect = locatesub(&holelist[i], &searchtri, 0, 0.0); + if ((intersect != OUTSIDE) && (!sinfected(searchtri))) { + // Infect the triangle. This is done by marking the triangle + // as infected and including the triangle in the virus pool. + sinfect(searchtri); + holetri = (shellface **) viri->alloc(); + *holetri = searchtri.sh; + } + } } } - // Incrementally insert the rest of points into T. - for (; i < arraysize; i++) { - // Insert p_i. - startsh.sh = dummysh; - loc = sinsertvertex(insertarray[i], &startsh, NULL, true, true); - // The point-to-subface map has been updated. - // Set p_i's type FREESUBVERTEX if it has no type yet. - if (pointtype(insertarray[i]) == FREEVOLVERTEX) { - setpointtype(insertarray[i], FREESUBVERTEX); - } + if (viri->items > 0) { + // Carve the holes and concavities. + plaguesub(viri); } - - return true; + // The virus pool should be empty now. } /////////////////////////////////////////////////////////////////////////////// // // -// finddirectionsub() Find the first subface in a facet on the path from // -// one point to another. // +// triangulate() Triangulate a PSLG into a CDT. // // // -// Finds the subface in the facet that intersects a line segment drawn from // -// the origin of `searchsh' to the point `tend', and returns the result in // -// `searchsh'. The origin of `searchsh' does not change, even though the // -// subface returned may differ from the one passed in. // +// A Planar Straight Line Graph (PSLG) P is actually a 2D polygonal region, // +// possibly contains holes, segments and vertices in its interior. P is tri- // +// angulated into a set of _subfaces_ forming a CDT of P. // // // -// The return value notes whether the destination or apex of the found face // -// is collinear with the two points in question. // +// The vertices and segments of P are found in 'ptlist' and 'conlist', resp- // +// ectively. 'holelist' contains a list of hole points. 'shmark' will be set // +// to all subfaces of P. // +// // +// The CDT is created directly in the pools 'subfaces' and 'subsegs'. It can // +// be retrived by a broadth-first searching starting from 'dummysh[0]'(debug // +// function 'outsurfmesh()' does it). // // // /////////////////////////////////////////////////////////////////////////////// -enum tetgenmesh::finddirectionresult tetgenmesh::finddirectionsub( - face* searchsh, point tend) +void tetgenmesh::triangulate(int shmark, REAL eps, list* ptlist, list* conlist, + int holes, REAL* holelist, memorypool* viri, queue* flipqueue) { - face checksh; - point startpoint, leftpoint, rightpoint; - REAL leftccw, rightccw; - REAL ori, sign; - int leftflag, rightflag; - - startpoint = sorg(*searchsh); - // Find the sign to simulate that abovepoint is 'above' the facet. - adjustedgering(*searchsh, CCW); - // Make sure 'startpoint' is the origin. - if (sorg(*searchsh) != startpoint) senextself(*searchsh); - rightpoint = sdest(*searchsh); - leftpoint = sapex(*searchsh); - ori = orient3d(startpoint, rightpoint, leftpoint, abovepoint); - sign = ori > 0.0 ? -1 : 1; + face newsh; + point *cons; + int i; - // Is `tend' to the left? - ori = orient3d(tend, startpoint, abovepoint, leftpoint); - leftccw = ori * sign; - leftflag = leftccw > 0.0; - // Is `tend' to the right? - ori = orient3d(startpoint, tend, abovepoint, rightpoint); - rightccw = ori * sign; - rightflag = rightccw > 0.0; - if (leftflag && rightflag) { - // `searchsh' faces directly away from `tend'. We could go left or - // right. Ask whether it's a triangle or a boundary on the left. - senext2(*searchsh, checksh); - spivotself(checksh); - if (checksh.sh == dummysh) { - leftflag = 0; - } else { - rightflag = 0; - } - } - while (leftflag) { - // Turn left until satisfied. - senext2self(*searchsh); - spivotself(*searchsh); - if (searchsh->sh == dummysh) { - printf("Internal error in finddirectionsub(): Unable to find a\n"); - printf(" subface leading from %d to %d.\n", pointmark(startpoint), - pointmark(tend)); - terminatetetgen(2); + if (b->verbose > 1) { + printf(" %d vertices, %d segments", ptlist->len(), conlist->len()); + if (holes > 0) { + printf(", %d holes", holes); } - if (sorg(*searchsh) != startpoint) sesymself(*searchsh); - assert(sorg(*searchsh) == startpoint); - leftpoint = sapex(*searchsh); - rightccw = leftccw; - ori = orient3d(tend, startpoint, abovepoint, leftpoint); - leftccw = ori * sign; - leftflag = leftccw > 0.0; + printf(", shmark: %d.\n", shmark); } - while (rightflag) { - // Turn right until satisfied. - spivotself(*searchsh); - if (searchsh->sh == dummysh) { - printf("Internal error in finddirectionsub(): Unable to find a\n"); - printf(" subface leading from %d to %d.\n", pointmark(startpoint), - pointmark(tend)); - terminatetetgen(2); + + // Create the DT of V by the 2D incremental flip algorithm. + incrflipdelaunaysub(shmark, eps, ptlist, holes, holelist, flipqueue); + // Recover boundary edges. + if (ptlist->len() > 3) { + // Insert segments into the DT. + for (i = 0; i < conlist->len(); i++) { + cons = (point *)(* conlist)[i]; + recoversegment(cons[0], cons[1], flipqueue); + } + // Carve holes and concavities. + carveholessub(holes, holelist, viri); + } else if (ptlist->len() == 3) { + // Insert 3 segments directly. + newsh.sh = dummysh; + newsh.shver = 0; + spivotself(newsh); + for (i = 0; i < 3; i++) { + insertsubseg(&newsh); + senextself(newsh); } - if (sdest(*searchsh) != startpoint) sesymself(*searchsh); - assert(sdest(*searchsh) == startpoint); - senextself(*searchsh); - rightpoint = sdest(*searchsh); - leftccw = rightccw; - ori = orient3d(startpoint, tend, abovepoint, rightpoint); - rightccw = ori * sign; - rightflag = rightccw > 0.0; - } - if (leftccw == 0.0) { - return LEFTCOLLINEAR; - } else if (rightccw == 0.0) { - return RIGHTCOLLINEAR; - } else { - return ACROSSEDGE; + } else if (ptlist->len() == 2) { + // This facet is actually a segment. It is not support by the mesh data + // strcuture. Hence the segment will not be maintained in the mesh. + // However, during segment recovery, the segment can be processed. + cons = (point *)(* conlist)[0]; + makeshellface(subsegs, &newsh); + setsorg(newsh, cons[0]); + setsdest(newsh, cons[1]); } } /////////////////////////////////////////////////////////////////////////////// // // -// insertsubseg() Create a subsegment and insert it between two subfaces. // +// retrievenewsubs() Retrieve newly created subfaces. // // // -// The new subsegment ab is inserted at the edge of subface 'tri'. If ab is // -// not a hull edge, it is inserted between two subfaces. If 'tri' is a hull // -// face, the initial face ring of ab will be set only one face which is self-// -// bonded. The final face ring will be constructed in 'unifysegments()'. // +// The new subfaces created by triangulate() can be found by a broadth-first // +// searching starting from 'dummysh[0]'. // +// // +// 'newshlist' (empty on input) returns the retrieved subfaces. Each edge on // +// the hull is bound to 'dummysh' and protected by a segment. If 'removeseg' // +// is TRUE, the segment is removed. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::insertsubseg(face* tri) +void tetgenmesh::retrievenewsubs(list* newshlist, bool removeseg) { - face oppotri; - face newsubseg; - point pa, pb; - REAL len; - int e1, e2; - int i; + face startsh, neighsh; + face deadseg; + int i, j; - // Check if there's already a subsegment here. - sspivot(*tri, newsubseg); - if (newsubseg.sh == dummysh) { - // Make new subsegment and initialize its vertices. - makeshellface(subsegs, &newsubseg); - pa = sorg(*tri); - pb = sdest(*tri); - setsorg(newsubseg, pa); - setsdest(newsubseg, pb); - // Are there length constraints? - 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(newsubseg, len); - break; + // The first new subface is found at dummysh[0]. + startsh.sh = dummysh; + startsh.shver = 0; + spivotself(startsh); + assert(startsh.sh != dummysh); + sinfect(startsh); + newshlist->append(&startsh); + + // Find the rest of new subfaces by a broadth-first searching. + for (i = 0; i < newshlist->len(); i++) { + // Get a new subface s. + startsh = * (face *)(* newshlist)[i]; + for (j = 0; j < 3; j++) { + spivot(startsh, neighsh); + if (neighsh.sh != dummysh) { + if (!sinfected(neighsh)) { + // Discovered a new subface. + sinfect(neighsh); + newshlist->append(&neighsh); + } + } else { + // Found a boundary edge. + if (removeseg) { + // This side of s may be protected by a segment. + sspivot(startsh, deadseg); + if (deadseg.sh != dummysh) { + // Detach it from s. + ssdissolve(startsh); + // Delete the segment. + shellfacedealloc(subsegs, deadseg.sh); + } } } + senextself(startsh); } - // Bond new subsegment to the two subfaces it is sandwiched between. - ssbond(*tri, newsubseg); - spivot(*tri, oppotri); - // 'oppotri' might be "out space". - if (oppotri.sh != dummysh) { - ssbond(oppotri, newsubseg); - } /* else { - // Outside! Bond '*tri' to itself. - sbond(*tri, *tri); - } */ + } + for (i = 0; i < newshlist->len(); i++) { + startsh = * (face *)(* newshlist)[i]; + suninfect(startsh); } } /////////////////////////////////////////////////////////////////////////////// // // -// scoutsegmentsub() Scout the first triangle on the path from one point // -// to another, and check for completion (reaching the // -// second point), a collinear point,or the intersection // -// of two segments. // +// unifysegments() Unify identical segments and build facet connections. // // // -// Returns true if the entire segment is successfully inserted, and false if // -// the job must be finished by constrainededge(). // +// After creating the surface mesh. Each facet has its own segments. There // +// are duplicated segments between adjacent facets. This routine has three // +// purposes: // +// (1) identify the set of segments which have the same endpoints and // +// unify them into one segment, remove redundant ones; // +// (2) create the face rings of the unified segments, hence setup the // +// connections between facets; and // +// (3) set a unique marker (1-based) for each segment. // +// On finish, each segment is unique and the face ring around it (right-hand // +// rule) is constructed. The connections between facets-facets are setup. // // // /////////////////////////////////////////////////////////////////////////////// -bool tetgenmesh::scoutsegmentsub(face* searchsh, point tend) +void tetgenmesh::unifysegments() { - face newsubseg; - face crosssub, crosssubseg; - point leftpoint, rightpoint; - enum finddirectionresult collinear; + list *sfacelist; + shellface **facesperverlist; + face subsegloop, testseg; + face sface, sface1, sface2; + point torg, tdest; + REAL da1, da2; + int *idx2facelist; + int segmarker; + int idx, k, m; - collinear = finddirectionsub(searchsh, tend); - rightpoint = sdest(*searchsh); - leftpoint = sapex(*searchsh); - if (rightpoint == tend || leftpoint == tend) { - // The segment is already an edge. - if (leftpoint == tend) { - senext2self(*searchsh); + if (b->verbose > 0) { + printf(" Unifying segments.\n"); + } + + // Compute a mapping from indices of vertices to subfaces. + makesubfacemap(idx2facelist, facesperverlist); + // Initialize 'sfacelist' for constructing the face link of each segment. + sfacelist = new list(sizeof(face), NULL); + + segmarker = 1; + subsegs->traversalinit(); + subsegloop.sh = shellfacetraverse(subsegs); + while (subsegloop.sh != (shellface *) NULL) { + subsegloop.shver = 0; // For sure. + torg = sorg(subsegloop); + tdest = sdest(subsegloop); + idx = pointmark(torg) - in->firstnumber; + // Loop through the set of subfaces containing 'torg'. Get all the + // subfaces containing the edge (torg, tdest). Save and order them + // in 'sfacelist', the ordering is defined by the right-hand rule + // with thumb points from torg to tdest. + for (k = idx2facelist[idx]; k < idx2facelist[idx + 1]; k++) { + sface.sh = facesperverlist[k]; + sface.shver = 0; + // sface may be died due to the removing of duplicated subfaces. + if (!isdead(&sface) && isfacehasedge(&sface, torg, tdest)) { + // 'sface' contains this segment. + findedge(&sface, torg, tdest); + // Save it in 'sfacelist'. + if (sfacelist->len() < 2) { + sfacelist->append(&sface); + } else { + for (m = 0; m < sfacelist->len() - 1; m++) { + sface1 = * (face *)(* sfacelist)[m]; + sface2 = * (face *)(* sfacelist)[m + 1]; + da1 = facedihedral(torg, tdest, sapex(sface1), sapex(sface)); + da2 = facedihedral(torg, tdest, sapex(sface1), sapex(sface2)); + if (da1 < da2) { + break; // Insert it after m. + } + } + sfacelist->insert(m + 1, &sface); + } + } } - // Insert a subsegment. - insertsubseg(searchsh); - return true; - } else if (collinear == LEFTCOLLINEAR) { - // We've collided with a vertex between the segment's endpoints. - // Make the collinear vertex be the triangle's origin. - senextself(*searchsh); // lprevself(*searchtri); - // Insert a subsegment. - insertsubseg(searchsh); - // Insert the remainder of the segment. - return scoutsegmentsub(searchsh, tend); - } else if (collinear == RIGHTCOLLINEAR) { - // We've collided with a vertex between the segment's endpoints. - // Insert a subsegment. - insertsubseg(searchsh); - // Make the collinear vertex be the triangle's origin. - senextself(*searchsh); // lnextself(*searchtri); - // Insert the remainder of the segment. - return scoutsegmentsub(searchsh, tend); - } else { - senext(*searchsh, crosssub); // lnext(*searchtri, crosstri); - // Check for a crossing segment. - sspivot(crosssub, crosssubseg); -#ifdef SELF_CHECK - assert(crosssubseg.sh == dummysh); -#endif - return false; + if (b->verbose > 1) { + printf(" Identifying %d segments of (%d %d).\n", sfacelist->len(), + pointmark(torg), pointmark(tdest)); + } + // Set the connection between this segment and faces containing it, + // at the same time, remove redundant segments. + for (k = 0; k < sfacelist->len(); k++) { + sface = *(face *)(* sfacelist)[k]; + sspivot(sface, testseg); + // If 'testseg' is not 'subsegloop', it is a redundant segment that + // needs be removed. BE CAREFUL it may already be removed. Do not + // remove it twice, i.e., do test 'isdead()' together. + if ((testseg.sh != subsegloop.sh) && !isdead(&testseg)) { + shellfacedealloc(subsegs, testseg.sh); + } + // 'ssbond' bonds the subface and the segment together, and dissloves + // the old bond as well. + ssbond(sface, subsegloop); + } + // Set connection between these faces. + sface = *(face *)(* sfacelist)[0]; + for (k = 1; k <= sfacelist->len(); k++) { + if (k < sfacelist->len()) { + sface1 = *(face *)(* sfacelist)[k]; + } else { + sface1 = *(face *)(* sfacelist)[0]; // Form a face loop. + } + /* + // Check if these two subfaces are the same. It is possible when user + // defines one facet (or polygon) two or more times. If they are, + // they should not be bonded together, instead of that, one of them + // should be delete from the surface mesh. + if ((sfacelist->len() > 1) && sapex(sface) == sapex(sface1)) { + // They are duplicated faces. + if (b->verbose > 0) { + printf(" A duplicated subface (%d, %d, %d) is removed.\n", + pointmark(torg), pointmark(tdest), pointmark(sapex(sface))); + } + if (k == sfacelist->len()) { + // 'sface' is the last face, however, it is same as the first one. + // In order to form the ring, we have to let the second last + // face bond to the first one 'sface1'. + shellfacedealloc(subfaces, sface.sh); + assert(sfacelist->len() >= 2); + assert(k == sfacelist->len()); + sface = *(face *)(* sfacelist)[k - 2]; + } else { + // 'sface1' is in the middle and may be the last one. + shellfacedealloc(subfaces, sface1.sh); + // Skip this face and go to the next one. + continue; + } + } + */ + if (b->verbose > 2) { + printf(" Bond subfaces (%d, %d, %d) and (%d, %d, %d).\n", + pointmark(torg), pointmark(tdest), pointmark(sapex(sface)), + pointmark(torg), pointmark(tdest), pointmark(sapex(sface1))); + } + sbond1(sface, sface1); + sface = sface1; + } + // Set the unique segment marker into the unified segment. + setshellmark(subsegloop, segmarker); + // Increase the marker. + segmarker++; + // Clear the working list. + sfacelist->clear(); + subsegloop.sh = shellfacetraverse(subsegs); } + + delete [] idx2facelist; + delete [] facesperverlist; + delete sfacelist; } /////////////////////////////////////////////////////////////////////////////// // // -// flipedgerecursive() Flip an edge. // -// // -// This is a support routine for inserting segments into a CDT. // +// mergefacets() Merge adjacent facets to be one facet if they are // +// coplanar and have the same boundary marker. // // // -// Let 'flipedge' be ab, and two triangles abc, abd share at it. ab may not // -// flipable if the four vertices a, b, c, and d are non-convex. If it is the // -// case, recursively flip ad or bd. Return when ab is flipped. // +// Segments between two merged facets will be removed from the mesh. If all // +// segments around a vertex have been removed, change its vertex type to be // +// FREESUBVERTEX. Edge flips will be performed to ensure the Delaunayness of // +// the triangulation of merged facets. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::flipedgerecursive(face* flipedge, queue* flipqueue) -{ - face fixupsh; - point pa, pb, pc, pd; - REAL oria, orib; - bool doflip; +void tetgenmesh::mergefacets(queue* flipqueue) +{ + face parentsh, neighsh, neineighsh; + face segloop; + point eorg, edest; + REAL ori; + bool mergeflag, pbcflag; + int* segspernodelist; + int fidx1, fidx2; + int i, j; + + if (b->verbose > 0) { + printf(" Merging coplanar facets.\n"); + } + // Create and initialize 'segspernodelist'. + segspernodelist = new int[points->items + 1]; + for (i = 0; i < points->items + 1; i++) segspernodelist[i] = 0; + + // Loop the segments, counter the number of segments sharing each vertex. + subsegs->traversalinit(); + segloop.sh = shellfacetraverse(subsegs); + while (segloop.sh != (shellface *) NULL) { + // Increment the number of sharing segments for each endpoint. + for (i = 0; i < 2; i++) { + j = pointmark((point) segloop.sh[3 + i]); + segspernodelist[j]++; + } + segloop.sh = shellfacetraverse(subsegs); + } - pa = sorg(*flipedge); - pb = sdest(*flipedge); - pc = sapex(*flipedge); - do { - spivot(*flipedge, fixupsh); - pd = sapex(fixupsh); - oria = orient3d(pc, pd, abovepoint, pa); - orib = orient3d(pc, pd, abovepoint, pb); - doflip = (oria * orib < 0.0); - if (doflip) { - // Flip the edge (a, b) away. - flip22sub(flipedge, flipqueue); - // Fix flipedge on edge e (c, d). - findedge(flipedge, pc, pd); - } else { - // ab is unflipable. Get the next edge (bd, or da) to flip. - if (sorg(fixupsh) != pb) sesymself(fixupsh); - assert(sdest(fixupsh) == pa); - if (fabs(oria) > fabs(orib)) { - // acd has larger area. Choose da. - senextself(fixupsh); - } else { - // bcd has larger area. Choose bd. - senext2self(fixupsh); + // Loop the segments, find out dead segments. + subsegs->traversalinit(); + segloop.sh = shellfacetraverse(subsegs); + while (segloop.sh != (shellface *) NULL) { + eorg = sorg(segloop); + edest = sdest(segloop); + spivot(segloop, parentsh); + spivot(parentsh, neighsh); + spivot(neighsh, neineighsh); + if (parentsh.sh != neighsh.sh && parentsh.sh == neineighsh.sh) { + // Exactly two subfaces at this segment. + fidx1 = shellmark(parentsh) - 1; + fidx2 = shellmark(neighsh) - 1; + pbcflag = false; + if (checkpbcs) { + pbcflag = (shellpbcgroup(parentsh) >= 0) + || (shellpbcgroup(neighsh) >= 0); + } + // Possibly merge them if they are not in the same facet. + if ((fidx1 != fidx2) && !pbcflag) { + // Test if they are coplanar. + ori = orient3d(eorg, edest, sapex(parentsh), sapex(neighsh)); + if (ori != 0.0) { + if (iscoplanar(eorg, edest, sapex(parentsh), sapex(neighsh), ori, + b->epsilon)) { + ori = 0.0; // They are assumed as coplanar. + } + } + if (ori == 0.0) { + mergeflag = (in->facetmarkerlist == (int *) NULL || + in->facetmarkerlist[fidx1] == in->facetmarkerlist[fidx2]); + if (mergeflag) { + // This segment becomes dead. + if (b->verbose > 1) { + printf(" Removing segment (%d, %d).\n", pointmark(eorg), + pointmark(edest)); + } + ssdissolve(parentsh); + ssdissolve(neighsh); + shellfacedealloc(subsegs, segloop.sh); + j = pointmark(eorg); + segspernodelist[j]--; + if (segspernodelist[j] == 0) { + setpointtype(eorg, FREESUBVERTEX); + } + j = pointmark(edest); + segspernodelist[j]--; + if (segspernodelist[j] == 0) { + setpointtype(edest, FREESUBVERTEX); + } + // Add 'parentsh' to queue checking for flip. + enqueueflipedge(parentsh, flipqueue); + } + } } - // Flip the edge. - flipedgerecursive(&fixupsh, flipqueue); } - } while (!doflip); + segloop.sh = shellfacetraverse(subsegs); + } + + if (!flipqueue->empty()) { + // Restore the Delaunay property in the facet triangulation. + flipsub(flipqueue); + } + + delete [] segspernodelist; } /////////////////////////////////////////////////////////////////////////////// // // -// constrainededge() Force a segment into a CDT. // +// meshsurface() Create the surface mesh of a PLC. // // // -// The segment s is recovered by flipping away the edges it intersects, and // -// triangulating the polygons that form on each side of it. // +// Let X be the PLC, the surface mesh S of X consists of triangulated facets.// +// S is created mainly in the following steps: // // // -// Generates a single subsegment connecting `tstart' to `tend'. The triangle // -// `startsh' has `tstart' as its origin. // +// (1) Form the CDT of each facet of X separately (by routine triangulate()).// +// After it is done, the subfaces of each facet are connected to each other, // +// however there is no connection between facets yet. Notice each facet has // +// its own segments, some of them are duplicated. // +// // +// (2) Remove the redundant segments created in step (1) (by routine unify- // +// segment()). The subface ring of each segment is created, the connection // +// between facets are established as well. // +// // +// The return value indicates the number of segments of X. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::constrainededge(face* startsh, point tend, queue* flipqueue) +long tetgenmesh::meshsurface() { - point tstart, tright, tleft; - REAL rori, lori; - bool collision; + list *ptlist, *conlist; + queue *flipqueue; + tetgenio::facet *f; + tetgenio::polygon *p; + memorypool *viri; + point *idx2verlist; + point tstart, tend, *cons; + int *worklist; + int end1, end2; + int shmark, i, j; - tstart = sorg(*startsh); - do { - // Loop edges oppo to tstart until find one crosses the segment. - do { - tright = sdest(*startsh); - tleft = sapex(*startsh); - // Is edge (tright, tleft) corss the segment. - rori = orient3d(tstart, tright, abovepoint, tend); - collision = (rori == 0.0); - if (collision) break; // tright is on the segment. - lori = orient3d(tstart, tleft, abovepoint, tend); - collision = (lori == 0.0); - if (collision) { // tleft is on the segment. - senext2self(*startsh); - break; + if (!b->quiet) { + printf("Creating surface mesh.\n"); + } + + // Compute a mapping from indices to points. + makeindex2pointmap(idx2verlist); + // Compute a mapping from points to tets for computing abovepoints. + makepoint2tetmap(); + // Initialize 'facetabovepointarray'. + facetabovepointarray = new point[in->numberoffacets + 1]; + for (i = 0; i < in->numberoffacets + 1; i++) { + facetabovepointarray[i] = (point) NULL; + } + if (checkpbcs) { + // Initialize the global array 'subpbcgrouptable'. + createsubpbcgrouptable(); + } + + // Initialize working lists. + viri = new memorypool(sizeof(shellface *), 1024, POINTER, 0); + flipqueue = new queue(sizeof(badface)); + ptlist = new list(sizeof(point *), NULL, 256); + conlist = new list(sizeof(point *) * 2, NULL, 256); + worklist = new int[points->items + 1]; + for (i = 0; i < points->items + 1; i++) worklist[i] = 0; + + // Loop the facet list, triangulate each facet. On finish, all subfaces + // are in 'subfaces', all segments are in 'subsegs'. Notice: there're + // redundant segments. Remember: All facet indices count from 1. + for (shmark = 1; shmark <= in->numberoffacets; shmark++) { + // Get a facet F. + f = &in->facetlist[shmark - 1]; + + // Process the duplicated points first, they are marked with type + // DUPLICATEDVERTEX by incrflipdelaunay(). Let p and q are dup. + // and the index of p is larger than q's, p is substituted by q. + // In a STL mesh, duplicated points are implicitly included. + if ((b->object == tetgenbehavior::STL) || dupverts) { + // Loop all polygons of this facet. + for (i = 0; i < f->numberofpolygons; i++) { + p = &(f->polygonlist[i]); + // Loop other vertices of this polygon. + for (j = 0; j < p->numberofvertices; j++) { + end1 = p->vertexlist[j]; + tstart = idx2verlist[end1 - in->firstnumber]; + if (pointtype(tstart) == DUPLICATEDVERTEX) { + // Reset the index of vertex-j. + tend = point2ppt(tstart); + end2 = pointmark(tend); + p->vertexlist[j] = end2; + } + } } - if (rori * lori < 0.0) break; // Find the crossing edge. - // Both points are at one side of the segment. - finddirectionsub(startsh, tend); - } while (true); - if (collision) break; - // Get the neighbor face at edge e (tright, tleft). - senextself(*startsh); - // Flip the crossing edge. - flipedgerecursive(startsh, flipqueue); - // After flip, sorg(*startsh) == tstart. - assert(sorg(*startsh) == tstart); - } while (sdest(*startsh) != tend); + } - // Insert a subsegment to make the segment permanent. - insertsubseg(startsh); - // If there was a collision with an interceding vertex, install another - // segment connecting that vertex with endpoint2. - if (collision) { - // Insert the remainder of the segment. - if (!scoutsegmentsub(startsh, tend)) { - constrainededge(startsh, tend, flipqueue); + // Loop polygons of F, get the set V of vertices and S of segments. + for (i = 0; i < f->numberofpolygons; i++) { + // Get a polygon. + p = &(f->polygonlist[i]); + // Get the first vertex. + end1 = p->vertexlist[0]; + if ((end1 < in->firstnumber) || + (end1 >= in->firstnumber + in->numberofpoints)) { + if (!b->quiet) { + printf("Warning: Invalid the 1st vertex %d of polygon", end1); + printf(" %d in facet %d.\n", i + 1, shmark); + } + continue; // Skip this polygon. + } + tstart = idx2verlist[end1 - in->firstnumber]; + // Add tstart to V if it haven't been added yet. + if (worklist[end1] == 0) { + ptlist->append(&tstart); + worklist[end1] = 1; + } + // Loop other vertices of this polygon. + for (j = 1; j <= p->numberofvertices; j++) { + // get a vertex. + if (j < p->numberofvertices) { + end2 = p->vertexlist[j]; + } else { + end2 = p->vertexlist[0]; // Form a loop from last to first. + } + if ((end2 < in->firstnumber) || + (end2 >= in->firstnumber + in->numberofpoints)) { + if (!b->quiet) { + printf("Warning: Invalid vertex %d in polygon %d", end2, i + 1); + printf(" in facet %d.\n", shmark); + } + } else { + if (end1 != end2) { + // 'end1' and 'end2' form a segment. + tend = idx2verlist[end2 - in->firstnumber]; + // Add tstart to V if it haven't been added yet. + if (worklist[end2] == 0) { + ptlist->append(&tend); + worklist[end2] = 1; + } + // Save the segment in S (conlist). + cons = (point *) conlist->append(NULL); + cons[0] = tstart; + cons[1] = tend; + // Set the start for next continuous segment. + end1 = end2; + tstart = tend; + } else { + // Two identical vertices represent an isolated vertex of F. + if (p->numberofvertices > 2) { + // This may be an error in the input, anyway, we can continue + // by simply skipping this segment. + if (!b->quiet) { + printf("Warning: Polygon %d has two identical verts", i + 1); + printf(" in facet %d.\n", shmark); + } + } + // Ignore this vertex. + } + } + // Is the polygon degenerate (a segment or a vertex)? + if (p->numberofvertices == 2) break; + } + } + // Unmark vertices. + for (i = 0; i < ptlist->len(); i++) { + tstart = * (point *)(* ptlist)[i]; + end1 = pointmark(tstart); + assert(worklist[end1] == 1); + worklist[end1] = 0; } + + // Create a CDT of F. + triangulate(shmark, b->epsilon * 1e+2, ptlist, conlist, f->numberofholes, + f->holelist, viri, flipqueue); + // Clear working lists. + ptlist->clear(); + conlist->clear(); + viri->restart(); } -} -/////////////////////////////////////////////////////////////////////////////// -// // -// recoversegment() Recover a segment in the surface triangulation. // -// // -/////////////////////////////////////////////////////////////////////////////// + // Unify segments in 'subsegs', remove redundant segments. Face links + // of segments are also built. + unifysegments(); + // Remember the number of input segments (for output). + insegments = subsegs->items; -void tetgenmesh::recoversegment(point tstart, point tend, queue* flipqueue) -{ - face searchsh; + if (checkpbcs) { + // Create the global array 'segpbcgrouptable'. + createsegpbcgrouptable(); + } - if (b->verbose > 2) { - printf(" Insert seg (%d, %d).\n", pointmark(tstart), pointmark(tend)); + if (b->object == tetgenbehavior::STL) { + // Remove redundant vertices (for .stl input mesh). + jettisonnodes(); } - // Find a triangle whose origin is the segment's first endpoint. - point2shorg(tstart, searchsh); - // Scout the segment and insert it if it is found. - if (scoutsegmentsub(&searchsh, tend)) { - // The segment was easily inserted. - return; - } - // Insert the segment into the triangulation by flips. - constrainededge(&searchsh, tend, flipqueue); - // Some edges may need flipping. - lawson(flipqueue); + if (!b->nomerge && !b->nobisect && !checkpbcs) { + // No '-M' switch - merge adjacent facets if they are coplanar. + mergefacets(flipqueue); + } + + delete [] idx2verlist; + delete [] worklist; + delete ptlist; + delete conlist; + delete flipqueue; + delete viri; + + return subsegs->items; } +// +// End of surface triangulation routines +// + /////////////////////////////////////////////////////////////////////////////// // // -// infecthullsub() Virally infect all of the triangles of the convex hull // -// that are not protected by subsegments. // +// interecursive() Recursively do intersection test on a set of triangles.// +// // +// Recursively split the set 'subfacearray' of subfaces into two sets using // +// a cut plane parallel to x-, or, y-, or z-axies. The split criteria are // +// follows. Assume the cut plane is H, and H+ denotes the left halfspace of // +// H, and H- denotes the right halfspace of H; and s be a subface: // +// // +// (1) If all points of s lie at H+, put it into left array; // +// (2) If all points of s lie at H-, put it into right array; // +// (3) If some points of s lie at H+ and some of lie at H-, or some // +// points lie on H, put it into both arraies. // +// // +// Partitions by x-axis if axis == '0'; by y-axis if axis == '1'; by z-axis // +// if axis == '2'. If current cut plane is parallel to the x-axis, the next // +// one will be parallel to y-axis, and the next one after the next is z-axis,// +// and then alternately return back to x-axis. // +// // +// Stop splitting when the number of triangles of the input array is not // +// decreased anymore. Do tests on the current set. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::infecthullsub(memorypool* viri) +void tetgenmesh:: +interecursive(shellface** subfacearray, int arraysize, int axis, REAL bxmin, + REAL bxmax, REAL bymin, REAL bymax, REAL bzmin, REAL bzmax, + int* internum) { - face hulltri, nexttri, starttri; - face hullsubseg; - shellface **deadshellface; + shellface **leftarray, **rightarray; + face sface1, sface2; + point p1, p2, p3; + point p4, p5, p6; + enum interresult intersect; + REAL split; + bool toleft, toright; + int leftsize, rightsize; + int i, j; - // Find a triangle handle on the hull. - hulltri.sh = dummysh; - hulltri.shver = 0; - spivotself(hulltri); - adjustedgering(hulltri, CCW); - // Remember where we started so we know when to stop. - starttri = hulltri; - // Go once counterclockwise around the convex hull. - do { - // Ignore triangles that are already infected. - if (!sinfected(hulltri)) { - // Is the triangle protected by a subsegment? - sspivot(hulltri, hullsubseg); - if (hullsubseg.sh == dummysh) { - // The triangle is not protected; infect it. - if (!sinfected(hulltri)) { - sinfect(hulltri); - deadshellface = (shellface **) viri->alloc(); - *deadshellface = hulltri.sh; - } + if (b->verbose > 1) { + printf(" Recur %d faces. Bbox (%g, %g, %g),(%g, %g, %g). %s-axis\n", + arraysize, bxmin, bymin, bzmin, bxmax, bymax, bzmax, + axis == 0 ? "x" : (axis == 1 ? "y" : "z")); + } + + leftarray = new shellface*[arraysize]; + if (leftarray == NULL) { + printf("Error in interecursive(): Insufficient memory.\n"); + terminatetetgen(1); + } + rightarray = new shellface*[arraysize]; + if (rightarray == NULL) { + printf("Error in interecursive(): Insufficient memory.\n"); + terminatetetgen(1); + } + leftsize = rightsize = 0; + + 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. + split = 0.5 * (bzmin + bzmax); + } + + for (i = 0; i < arraysize; i++) { + sface1.sh = subfacearray[i]; + p1 = (point) sface1.sh[3]; + p2 = (point) sface1.sh[4]; + p3 = (point) sface1.sh[5]; + toleft = toright = false; + if (p1[axis] < split) { + toleft = true; + if (p2[axis] >= split || p3[axis] >= split) { + toright = true; + } + } else if (p1[axis] > split) { + toright = true; + if (p2[axis] <= split || p3[axis] <= split) { + toleft = true; } + } else { + // p1[axis] == split; + toleft = true; + toright = true; } - // To find the next hull edge, go clockwise around the next vertex. - senextself(hulltri); - spivot(hulltri, nexttri); - while (nexttri.sh != dummysh) { - if (sorg(nexttri) != sdest(hulltri)) { - sesymself(nexttri); + // At least one is true; +#ifdef SELF_CHECK + assert(!(toleft == false && toright == false)); +#endif + if (toleft) { + leftarray[leftsize] = sface1.sh; + leftsize++; + } + if (toright) { + rightarray[rightsize] = sface1.sh; + rightsize++; + } + } + + if (leftsize < arraysize && rightsize < arraysize) { + // Continue to partition the input set. Now 'subfacearray' has been + // split into two sets, it's memory can be freed. 'leftarray' and + // 'rightarray' will be freed in the next recursive (after they're + // partitioned again or performing tests). + delete [] subfacearray; + // Continue to split these two sets. + if (axis == 0) { + interecursive(leftarray, leftsize, 1, bxmin, split, bymin, bymax, + bzmin, bzmax, internum); + interecursive(rightarray, rightsize, 1, split, bxmax, bymin, bymax, + bzmin, bzmax, internum); + } else if (axis == 1) { + interecursive(leftarray, leftsize, 2, bxmin, bxmax, bymin, split, + bzmin, bzmax, internum); + interecursive(rightarray, rightsize, 2, bxmin, bxmax, split, bymax, + bzmin, bzmax, internum); + } else { + interecursive(leftarray, leftsize, 0, bxmin, bxmax, bymin, bymax, + bzmin, split, internum); + interecursive(rightarray, rightsize, 0, bxmin, bxmax, bymin, bymax, + split, bzmax, internum); + } + } else { + if (b->verbose > 1) { + printf(" Checking intersecting faces.\n"); + } + // Perform a brute-force compare on the set. + for (i = 0; i < arraysize; i++) { + sface1.sh = subfacearray[i]; + p1 = (point) sface1.sh[3]; + p2 = (point) sface1.sh[4]; + p3 = (point) sface1.sh[5]; + for (j = i + 1; j < arraysize; j++) { + sface2.sh = subfacearray[j]; + p4 = (point) sface2.sh[3]; + p5 = (point) sface2.sh[4]; + p6 = (point) sface2.sh[5]; + intersect = tri_tri_inter(p1, p2, p3, p4, p5, p6); + if (intersect == INTERSECT || intersect == SHAREFACE) { + if (!b->quiet) { + if (intersect == INTERSECT) { + printf(" Facet #%d intersects facet #%d at triangles:\n", + shellmark(sface1), shellmark(sface2)); + printf(" (%4d, %4d, %4d) and (%4d, %4d, %4d)\n", + pointmark(p1), pointmark(p2), pointmark(p3), + pointmark(p4), pointmark(p5), pointmark(p6)); + } else { + printf(" Facet #%d duplicates facet #%d at triangle:\n", + shellmark(sface1), shellmark(sface2)); + printf(" (%4d, %4d, %4d)\n", pointmark(p1), pointmark(p2), + pointmark(p3)); + } + } + // Increase the number of intersecting pairs. + (*internum)++; + // Infect these two faces (although they may already be infected). + sinfect(sface1); + sinfect(sface2); + } } - senext(nexttri, hulltri); - spivot(hulltri, nexttri); } - } while (hulltri != starttri); + // Don't forget to free all three arrays. No further partition. + delete [] leftarray; + delete [] rightarray; + delete [] subfacearray; + } } /////////////////////////////////////////////////////////////////////////////// // // -// plaguesub() Spread the virus from all infected triangles to any // -// neighbors not protected by subsegments. Delete all // -// infected triangles. // +// detectinterfaces() Detect intersecting triangles. // +// // +// Given a set of triangles, find the pairs of intersecting triangles from // +// them. Here the set of triangles is in 'subfaces' which is a surface mesh // +// of a PLC (.poly or .smesh). // +// // +// To detect whether two triangles are intersecting is done by the routine // +// 'tri_tri_inter()'. The algorithm for the test is very simple and stable. // +// It is based on geometric orientation test which uses exact arithmetics. // +// // +// Use divide-and-conquer algorithm for reducing the number of intersection // +// tests. Start from the bounding box of the input point set, recursively // +// partition the box into smaller boxes, until the number of triangles in a // +// box is not decreased anymore. Then perform triangle-triangle tests on the // +// remaining set of triangles. The memory allocated in the input set is // +// freed immediately after it has been partitioned into two arrays. So it // +// can be re-used for the consequent partitions. // // // -// This is the procedure that actually creates holes and concavities. // +// On return, the pool 'subfaces' will be cleared, and only the intersecting // +// triangles remain for output (to a .face file). // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::plaguesub(memorypool* viri) +void tetgenmesh::detectinterfaces() { - face testtri, neighbor, ghostsh; - face neighborsubseg; - shellface **virusloop; - shellface **deadshellface; - point *ppt; - int i, j; + shellface **subfacearray; + face shloop; + int internum; + int i; - // Loop through all the infected triangles, spreading the virus to - // their neighbors, then to their neighbors' neighbors. - viri->traversalinit(); - virusloop = (shellface **) viri->traverse(); - while (virusloop != (shellface **) NULL) { - testtri.sh = *virusloop; - // Check each of the triangle's three neighbors. - for (i = 0; i < 3; i++) { - // Find the neighbor. - spivot(testtri, neighbor); - // Check for a subsegment between the triangle and its neighbor. - sspivot(testtri, neighborsubseg); - // Check if the neighbor is nonexistent or already infected. - if ((neighbor.sh == dummysh) || sinfected(neighbor)) { - if (neighborsubseg.sh != dummysh) { - // There is a subsegment separating the triangle from its - // neighbor, but both triangles are dying, so the subsegment - // dies too. - shellfacedealloc(subsegs, neighborsubseg.sh); - if (neighbor.sh != dummysh) { - // Make sure the subsegment doesn't get deallocated again - // later when the infected neighbor is visited. - ssdissolve(neighbor); - } - } - } else { // The neighbor exists and is not infected. - if (neighborsubseg.sh == dummysh) { - // There is no subsegment protecting the neighbor, so the - // neighbor becomes infected. - sinfect(neighbor); - // Ensure that the neighbor's neighbors will be infected. - deadshellface = (shellface **) viri->alloc(); - *deadshellface = neighbor.sh; - } else { // The neighbor is protected by a subsegment. - // Remove this triangle from the subsegment. - ssbond(neighbor, neighborsubseg); - // Update the point-to-subface map. 2009-07-21. - ppt = (point *) &(neighbor.sh[3]); - for (j = 0; j < 3; j++) { - setpoint2sh(ppt[j], sencode(neighbor)); - } - } - } - senextself(testtri); + if (!b->quiet) { + printf("Detecting intersecting facets.\n"); + } + + // Construct a map from indices to subfaces; + subfacearray = new shellface*[subfaces->items]; + subfaces->traversalinit(); + shloop.sh = shellfacetraverse(subfaces); + i = 0; + while (shloop.sh != (shellface *) NULL) { + subfacearray[i] = shloop.sh; + shloop.sh = shellfacetraverse(subfaces); + i++; + } + + internum = 0; + // Recursively split the set of triangles into two sets using a cut plane + // parallel to x-, or, y-, or z-axies. Stop splitting when the number + // of subfaces is not decreasing anymore. Do tests on the current set. + interecursive(subfacearray, subfaces->items, 0, xmin, xmax, ymin, ymax, + zmin, zmax, &internum); + + if (!b->quiet) { + if (internum > 0) { + printf("\n!! Found %d pairs of faces are intersecting.\n\n", internum); + } else { + printf("\nNo faces are intersecting.\n\n"); } - virusloop = (shellface **) viri->traverse(); } - ghostsh.sh = dummysh; // A handle of outer space. - viri->traversalinit(); - virusloop = (shellface **) viri->traverse(); - while (virusloop != (shellface **) NULL) { - testtri.sh = *virusloop; - // Record changes in the number of boundary edges, and disconnect - // dead triangles from their neighbors. - for (i = 0; i < 3; i++) { - spivot(testtri, neighbor); - if (neighbor.sh != dummysh) { - // Disconnect the triangle from its neighbor. - // sdissolve(neighbor); - sbond(neighbor, ghostsh); + if (internum > 0) { + // Traverse all subfaces, deallocate those have not been infected (they + // are not intersecting faces). Uninfect those have been infected. + // After this loop, only intersecting faces remain. + subfaces->traversalinit(); + shloop.sh = shellfacetraverse(subfaces); + while (shloop.sh != (shellface *) NULL) { + if (sinfected(shloop)) { + suninfect(shloop); + } else { + shellfacedealloc(subfaces, shloop.sh); } - senextself(testtri); + shloop.sh = shellfacetraverse(subfaces); } - // Return the dead triangle to the pool of triangles. - shellfacedealloc(subfaces, testtri.sh); - virusloop = (shellface **) viri->traverse(); + } else { + // Deallocate all subfaces. + subfaces->restart(); } - // Empty the virus pool. - viri->restart(); } +// +// Begin of periodic boundary condition routines +// + /////////////////////////////////////////////////////////////////////////////// // // -// carveholessub() Find the holes and infect them. Find the area // -// constraints and infect them. Infect the convex hull. // -// Spread the infection and kill triangles. Spread the // -// area constraints. // +// createsubpbcgrouptable() Create the 'subpbcgrouptable'. // // // -// This routine mainly calls other routines to carry out all these functions.// +// Allocate the memory for 'subpbcgrouptable'. Each entry i (a pbcdata) of // +// the table represents a pbcgroup. Most of the fields of a group-i are set // +// in this routine. 'fmark[0]', 'fmark[1]', and 'transmat[0]' are directly // +// copied from the corresponding data of 'in->numberofpbcgroups'. 'transmat // +// [1]' is calculated as the inverse matrix of 'transmat[0]'. 'ss[0]' and // +// 'ss[1]' are initilized be 'dummysh'. They are set in 'trangulatefacet()' // +// (when -p is in use) or 'reconstructmesh()' (when -r is in use). // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::carveholessub(int holes, REAL* holelist, memorypool *viri) +void tetgenmesh::createsubpbcgrouptable() { - face searchtri, triangleloop; - shellface **holetri; - enum locateresult intersect; - int i; - - // Mark as infected any unprotected triangles on the boundary. - // This is one way by which concavities are created. - infecthullsub(viri); + tetgenio::pbcgroup *pg; + pbcdata *pd; + REAL A[4][4], rhs[4], D; + int indx[4]; + int i, j, k; - if (holes > 0) { - // Infect each triangle in which a hole lies. - for (i = 0; i < 3 * holes; i += 3) { - // Ignore holes that aren't within the bounds of the mesh. - if ((holelist[i] >= xmin) && (holelist[i] <= xmax) - && (holelist[i + 1] >= ymin) && (holelist[i + 1] <= ymax) - && (holelist[i + 2] >= zmin) && (holelist[i + 2] <= zmax)) { - // Start searching from some triangle on the outer boundary. - searchtri.sh = dummysh; - // Find a triangle that contains the hole. - intersect = locatesub(&holelist[i], &searchtri, 0, 0.0); - if ((intersect != OUTSIDE) && (!sinfected(searchtri))) { - // Infect the triangle. This is done by marking the triangle - // as infected and including the triangle in the virus pool. - sinfect(searchtri); - holetri = (shellface **) viri->alloc(); - *holetri = searchtri.sh; - } + subpbcgrouptable = new pbcdata[in->numberofpbcgroups]; + for (i = 0; i < in->numberofpbcgroups; i++) { + pg = &(in->pbcgrouplist[i]); + pd = &(subpbcgrouptable[i]); + // Copy data from pg to pd. + pd->fmark[0] = pg->fmark1; + pd->fmark[1] = pg->fmark2; + // Initialize array 'pd->ss'. + pd->ss[0].sh = dummysh; + pd->ss[1].sh = dummysh; + // Copy the transform matrix from pg to pd->transmat[0]. + for (j = 0; j < 4; j++) { + for (k = 0; k < 4; k++) { + pd->transmat[0][j][k] = pg->transmat[j][k]; + // Prepare for inverting the matrix. + A[j][k] = pg->transmat[j][k]; } } + // Calculate the inverse matrix (pd->transmat[1]) of pd->transmat[0]. + lu_decmp(A, 4, indx, &D, 0); + for (j = 0; j < 4; j++) { + for (k = 0; k < 4; k++) rhs[k] = 0.0; + rhs[j] = 1.0; + lu_solve(A, 4, indx, rhs, 0); + for (k = 0; k < 4; k++) pd->transmat[1][k][j] = rhs[k]; + } } - - if (viri->items > 0) { - // Carve the holes and concavities. - plaguesub(viri); - } - // The virus pool should be empty now. } /////////////////////////////////////////////////////////////////////////////// // // -// triangulate() Triangulate a PSLG into a CDT. // -// // -// A Planar Straight Line Graph (PSLG) P is actually a 2D polygonal region, // -// possibly contains holes, segments and vertices in its interior. P is tri- // -// angulated into a set of _subfaces_ forming a CDT of P. // -// // -// The vertices and segments of P are found in 'ptlist' and 'conlist', resp- // -// ectively. 'holelist' contains a list of hole points. 'shmark' will be set // -// to all subfaces of P. // +// getsubpbcgroup() Get the pbcgroup of a subface. // // // -// The CDT is created directly in the pools 'subfaces' and 'subsegs'. It can // -// be retrived by a broadth-first searching starting from 'dummysh[0]'(debug // -// function 'outsurfmesh()' does it). // +// 'pbcsub' has pbc defined. Its pbcgroup is returned in 'pd'. In addition, // +// 'f1' (0 or 1) indicates the position of 'pbcsub' in 'pd'; 'f2' (= 1 - f1) // +// is the position where the symmetric subface of 'pbcsub' is found. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::triangulate(int shmark, REAL eps, list* ptlist, list* conlist, - int holes, REAL* holelist, memorypool* viri, queue* flipqueue) +void tetgenmesh::getsubpbcgroup(face* pbcsub, pbcdata** pd, int *f1, int *f2) { - face newsh; - point *cons; - int i; - - if (b->verbose > 1) { - printf(" %d vertices, %d segments", ptlist->len(), conlist->len()); - if (holes > 0) { - printf(", %d holes", holes); - } - printf(", shmark: %d.\n", shmark); - } + int groupid, fmark, idx; - // Create the DT of V by the 2D incremental flip algorithm. - if (incrflipdelaunaysub(shmark, eps, ptlist, holes, holelist, flipqueue)) { - // Recover boundary edges. - if (ptlist->len() > 3) { - // Insert segments into the DT. - for (i = 0; i < conlist->len(); i++) { - cons = (point *)(* conlist)[i]; - recoversegment(cons[0], cons[1], flipqueue); - } - // Carve holes and concavities. - carveholessub(holes, holelist, viri); - } else if (ptlist->len() == 3) { - // Insert 3 segments directly. - newsh.sh = dummysh; - newsh.shver = 0; - spivotself(newsh); - for (i = 0; i < 3; i++) { - insertsubseg(&newsh); - senextself(newsh); - } - } else if (ptlist->len() == 2) { - // This facet is actually a segment. It is not support by the mesh data - // strcuture. Hence the segment will not be maintained in the mesh. - // However, during segment recovery, the segment can be processed. - cons = (point *)(* conlist)[0]; - makeshellface(subsegs, &newsh); - setsorg(newsh, cons[0]); - setsdest(newsh, cons[1]); - } + groupid = shellpbcgroup(*pbcsub); + *pd = &subpbcgrouptable[groupid]; + + // Get the facet index (1 - based). + idx = shellmark(*pbcsub); + // Get the facet marker from array (0 - based). + fmark = in->facetmarkerlist[idx - 1]; + if ((*pd)->fmark[0] == fmark) { + *f1 = 0; + } else { +#ifdef SELF_CHECK + assert((*pd)->fmark[1] == fmark); +#endif + *f1 = 1; } + *f2 = 1 - (*f1); } /////////////////////////////////////////////////////////////////////////////// // // -// retrievenewsubs() Retrieve newly created subfaces. // -// // -// The new subfaces created by triangulate() can be found by a broadth-first // -// searching starting from 'dummysh[0]'. // +// getsubpbcsympoint() Compute the symmetric point for a subface point. // // // -// 'newshlist' (empty on input) returns the retrieved subfaces. Each edge on // -// the hull is bound to 'dummysh' and protected by a segment. If 'removeseg' // -// is TRUE, the segment is removed. // +// 'newpoint' lies on 'splitsub'. This routine calculates a 'sympoint' which // +// locates on 'symsplitsub' and symmtric to 'newpoint'. Return the location // +// of sympoint wrt. symsplitsub. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::retrievenewsubs(list* newshlist, bool removeseg) +enum tetgenmesh::locateresult tetgenmesh:: getsubpbcsympoint(point newpoint, + face* splitsub, point sympoint, face* symsplitsub) { - face startsh, neighsh; - face deadseg; - int i, j; - - // The first new subface is found at dummysh[0]. - startsh.sh = dummysh; - startsh.shver = 0; - spivotself(startsh); - assert(startsh.sh != dummysh); - sinfect(startsh); - newshlist->append(&startsh); + pbcdata *pd; + face subloop; + point pa, pb, pc; + enum locateresult symloc; + REAL ori; + int f1, f2, i; - // Find the rest of new subfaces by a broadth-first searching. - for (i = 0; i < newshlist->len(); i++) { - // Get a new subface s. - startsh = * (face *)(* newshlist)[i]; - for (j = 0; j < 3; j++) { - spivot(startsh, neighsh); - if (neighsh.sh != dummysh) { - if (!sinfected(neighsh)) { - // Discovered a new subface. - sinfect(neighsh); - newshlist->append(&neighsh); - } - } else { - // Found a boundary edge. - if (removeseg) { - // This side of s may be protected by a segment. - sspivot(startsh, deadseg); - if (deadseg.sh != dummysh) { - // Detach it from s. - ssdissolve(startsh); - // Delete the segment. - shellfacedealloc(subsegs, deadseg.sh); + // Get the pbcgroup of 'splitsub'. + getsubpbcgroup(splitsub, &pd, &f1, &f2); + + // Transform newpoint from f1 -> f2. + for (i = 0; i < 3; i++) { + sympoint[i] = pd->transmat[f1][i][0] * newpoint[0] + + pd->transmat[f1][i][1] * newpoint[1] + + pd->transmat[f1][i][2] * newpoint[2] + + pd->transmat[f1][i][3] * 1.0; + } + // Locate sympoint in f2. + symloc = OUTSIDE; + *symsplitsub = pd->ss[f2]; + // Is the stored subface valid? Hole removal may delete the subface. + if ((symsplitsub->sh != dummysh) && !isdead(symsplitsub)) { + // 'symsplitsub' should lie on the symmetric facet. Check it. + i = shellmark(*symsplitsub); + if (in->facetmarkerlist[i - 1] == pd->fmark[f2]) { + // 'symsplitsub' has the symmetric boundary marker. + pa = sorg(*symsplitsub); + pb = sdest(*symsplitsub); + pc = sapex(*symsplitsub); + // Test if they are (nearly) coplanar. Some facets may have the + // same boundary marker but not coplanar with this point. + ori = orient3d(pa, pb, pc, sympoint); + if (iscoplanar(pa, pb, pc, sympoint, ori, b->epsilon * 1e+2)) { + // Locate sympoint in facet. Don't stop at subsegment. + abovepoint = facetabovepointarray[shellmark(*symsplitsub)]; + if (abovepoint == (point) NULL) { + getfacetabovepoint(symsplitsub); + } + symloc = locatesub(sympoint, symsplitsub, 0, b->epsilon * 1e+2); + } + } + } + if (symloc == OUTSIDE) { + // Do a brute-force searching for the symmetric subface. + REAL epspp = b->epsilon * 1e+2; + int lcount = 0; + do { + // Locate sympoint in the pool of subfaces (with fmark pd->fmark[f2]). + subfaces->traversalinit(); + subloop.sh = shellfacetraverse(subfaces); + while (subloop.sh != (shellface *) NULL) { + i = shellmark(subloop); + if (in->facetmarkerlist[i - 1] == pd->fmark[f2]) { + // Found a facet have the symmetric boundary marker. + pa = sorg(subloop); + pb = sdest(subloop); + pc = sapex(subloop); + // Test if they are (nearly) coplanar. Some facets may have the + // same boundary marker but not coplanar with this point. + ori = orient3d(pa, pb, pc, sympoint); + if (iscoplanar(pa, pb, pc, sympoint, ori, epspp)) { + // Test if sympoint is (nearly) inside this facet. + // Get the abovepoint of the facet. + abovepoint = facetabovepointarray[shellmark(subloop)]; + // Do we need to calculate the abovepoint? + if (abovepoint == (point) NULL) { + getfacetabovepoint(&subloop); + } + // subloop is on the facet, search sympoint. + symloc = locatesub(sympoint, &subloop, 0, epspp); + if (symloc != OUTSIDE) break; } } + subloop.sh = shellfacetraverse(subfaces); } - senextself(startsh); - } - } - for (i = 0; i < newshlist->len(); i++) { - startsh = * (face *)(* newshlist)[i]; - suninfect(startsh); + lcount++; + epspp *= 10.0; + } while ((symloc == OUTSIDE) && (lcount < 3)); +#ifdef SELF_CHECK + // sympoint should be inside the facet. + assert(symloc != OUTSIDE); +#endif + // Set the returning subface. + *symsplitsub = subloop; + // Update the stored subface for next searching. + pd->ss[f2] = *symsplitsub; } + + return adjustlocatesub(sympoint, symsplitsub, symloc, b->epsilon); } /////////////////////////////////////////////////////////////////////////////// // // -// unifysegments() Unify identical segments and build facet connections. // +// createsegpbcgrouptable() Create the 'segpbcgrouptable'. // // // -// After creating the surface mesh. Each facet has its own segments. There // -// are duplicated segments between adjacent facets. This routine has three // -// purposes: // -// (1) identify the set of segments which have the same endpoints and // -// unify them into one segment, remove redundant ones; // -// (2) create the face rings of the unified segments, hence setup the // -// connections between facets; and // -// (3) set a unique marker (1-based) for each segment. // -// On finish, each segment is unique and the face ring around it (right-hand // -// rule) is constructed. The connections between facets-facets are setup. // +// Each segment may belong to more than one pbcgroups. For example, segment // +// ab may need to be symmteric to both segments cd, and ef, then ab and cd, // +// cd and ef, ef and ab form three pbcgroups. // +// // +// 'segpbcgrouptable' is implemented as a list of pbcdatas. Each item i is // +// a pbcgroup. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::unifysegments() +void tetgenmesh::createsegpbcgrouptable() { - list *sfacelist; - shellface **facesperverlist; - face subsegloop, testseg; - face sface, sface1, sface2; - point torg, tdest; - REAL da1, da2; - int *idx2facelist; - int segmarker; - int idx, k, m; + shellface** segsperverlist; + pbcdata *pd, *ppd, pd1, pd2; + face segloop, symseg; + face startsh, spinsh, symsh; + point pa, pb, syma, symb; + enum locateresult symloc; + REAL testpt[3], sympt[3]; + bool inflag; + int *idx2seglist; + int segid1, segid2; + int f1, f2; + int i, j, k, l; - if (b->verbose > 0) { - printf(" Unifying segments.\n"); + // Allocate memory for 'subpbcgrouptable'. + segpbcgrouptable = new list(sizeof(pbcdata), NULL, 256); + + if (b->refine) { + // Create a point-to-seg map for quickly finding PBC seg pairs. + makesegmentmap(idx2seglist, segsperverlist); } - // Compute a mapping from indices of vertices to subfaces. - makesubfacemap(idx2facelist, facesperverlist); - // Initialize 'sfacelist' for constructing the face link of each segment. - sfacelist = new list(sizeof(face), NULL); - - segmarker = 1; + // Loop through the segment list. subsegs->traversalinit(); - subsegloop.sh = shellfacetraverse(subsegs); - while (subsegloop.sh != (shellface *) NULL) { - subsegloop.shver = 0; // For sure. - torg = sorg(subsegloop); - tdest = sdest(subsegloop); - idx = pointmark(torg) - in->firstnumber; - // Loop through the set of subfaces containing 'torg'. Get all the - // subfaces containing the edge (torg, tdest). Save and order them - // in 'sfacelist', the ordering is defined by the right-hand rule - // with thumb points from torg to tdest. - for (k = idx2facelist[idx]; k < idx2facelist[idx + 1]; k++) { - sface.sh = facesperverlist[k]; - sface.shver = 0; - // sface may be died due to the removing of duplicated subfaces. - if (!isdead(&sface) && isfacehasedge(&sface, torg, tdest)) { - // 'sface' contains this segment. - findedge(&sface, torg, tdest); - // Save it in 'sfacelist'. - if (sfacelist->len() < 2) { - sfacelist->append(&sface); + segloop.sh = shellfacetraverse(subsegs); + while (segloop.sh != (shellface *) NULL) { + // Loop the subface ring of segloop ab. + pa = sorg(segloop); + pb = sdest(segloop); + segid1 = shellmark(segloop); + spivot(segloop, startsh); + spinsh = startsh; + do { + // Adjust spinsh be edge ab. + if (sorg(spinsh) != pa) { + sesymself(spinsh); + } + // Does spinsh belong to a pbcgroup? + if (shellpbcgroup(spinsh) != -1) { + // Yes! There exists a segment cd. ab and cd form a pbcgroup. + if (b->refine) { + getsubpbcgroup(&spinsh, &pd, &f1, &f2); + // Transform pa from f1 -> f2. + for (i = 0; i < 3; i++) { + sympt[i] = pd->transmat[f1][i][0] * pa[0] + + pd->transmat[f1][i][1] * pa[1] + + pd->transmat[f1][i][2] * pa[2] + + pd->transmat[f1][i][3] * 1.0; + } + syma = point2pbcpt(pa); + // Is 'sympt == syma'? + if (distance(sympt, syma) > (longest * b->epsilon)) { + // No. Search the symmetric vertex of pa. + symloc = getsubpbcsympoint(pa, &spinsh, sympt, &symsh); + syma = sorg(symsh); + if (symloc != ONVERTEX) { + // Do a brute force search. Not done yet. + assert(0); + } + } + // Transform pb from f1 -> f2. + for (i = 0; i < 3; i++) { + sympt[i] = pd->transmat[f1][i][0] * pb[0] + + pd->transmat[f1][i][1] * pb[1] + + pd->transmat[f1][i][2] * pb[2] + + pd->transmat[f1][i][3] * 1.0; + } + // Search sym subface from the point-to-subface map. + symseg.shver = 0; + j = pointmark(syma) - in->firstnumber; + for (i = idx2seglist[j]; i < idx2seglist[j + 1]; i++) { + symseg.sh = segsperverlist[i]; + if (sorg(symseg) == syma) symb = sdest(symseg); + else symb = sorg(symseg); + if (distance(sympt, symb) <= (longest * b->epsilon)) break; + } + assert(i < idx2seglist[j + 1]); } else { - for (m = 0; m < sfacelist->len() - 1; m++) { - sface1 = * (face *)(* sfacelist)[m]; - sface2 = * (face *)(* sfacelist)[m + 1]; - da1 = facedihedral(torg, tdest, sapex(sface1), sapex(sface)); - da2 = facedihedral(torg, tdest, sapex(sface1), sapex(sface2)); - if (da1 < da2) { - break; // Insert it after m. + // 'testpt' is the midpoint of ab used to find cd. + for (i = 0; i < 3; i++) testpt[i] = 0.5 * (pa[i] + pb[i]); + symloc = getsubpbcsympoint(testpt, &spinsh, sympt, &symsh); +#ifdef SELF_CHECK + assert(symloc == ONEDGE); +#endif + sspivot(symsh, symseg); + } +#ifdef SELF_CHECK + assert(symseg.sh != dummysh); +#endif + // Check whether this group has already been created in list. + segid2 = shellmark(symseg); + inflag = false; + for (i = 0; i < segpbcgrouptable->len() && !inflag; i++) { + pd = (pbcdata *)(* segpbcgrouptable)[i]; + if (pd->segid[0] == segid1) { + if (pd->segid[1] == segid2) inflag = true; + } else if (pd->segid[0] == segid2) { + if (pd->segid[1] == segid1) inflag = true; + } + } + if (!inflag) { + // Create a segment pbcgroup in list for ab and cd. + pd = (pbcdata *) segpbcgrouptable->append(NULL); + // Save the markers of ab and cd. + pd->segid[0] = segid1; + pd->segid[1] = segid2; + // Save the handles of ab and cd. + pd->ss[0] = segloop; + pd->ss[1] = symseg; + // Find the map from ab to cd. + getsubpbcgroup(&spinsh, &ppd, &f1, &f2); + pd->fmark[0] = ppd->fmark[f1]; + pd->fmark[1] = ppd->fmark[f2]; + // Set the map from ab to cd. + for (i = 0; i < 4; i++) { + for (j = 0; j < 4; j++) { + pd->transmat[0][i][j] = ppd->transmat[f1][i][j]; + } + } + // Set the map from cd to ab. + for (i = 0; i < 4; i++) { + for (j = 0; j < 4; j++) { + pd->transmat[1][i][j] = ppd->transmat[f2][i][j]; } } - sfacelist->insert(m + 1, &sface); } } - } - if (b->verbose > 1) { - printf(" Identifying %d segments of (%d %d).\n", sfacelist->len(), - pointmark(torg), pointmark(tdest)); - } - // Set the connection between this segment and faces containing it, - // at the same time, remove redundant segments. - for (k = 0; k < sfacelist->len(); k++) { - sface = *(face *)(* sfacelist)[k]; - sspivot(sface, testseg); - // If 'testseg' is not 'subsegloop', it is a redundant segment that - // needs be removed. BE CAREFUL it may already be removed. Do not - // remove it twice, i.e., do test 'isdead()' together. - if ((testseg.sh != subsegloop.sh) && !isdead(&testseg)) { - shellfacedealloc(subsegs, testseg.sh); - } - // 'ssbond' bonds the subface and the segment together, and dissloves - // the old bond as well. - ssbond(sface, subsegloop); - } - // Set connection between these faces. - sface = *(face *)(* sfacelist)[0]; - if (sfacelist->len() > 1) { - for (k = 1; k <= sfacelist->len(); k++) { - if (k < sfacelist->len()) { - sface1 = *(face *)(* sfacelist)[k]; - } else { - sface1 = *(face *)(* sfacelist)[0]; // Form a face loop. - } - // Comment: For detecting invalid PLC, here we could check if the - // two subfaces "sface" and "sface1" are identical (skipped). - if (b->verbose > 2) { - printf(" Bond subfaces (%d, %d, %d) and (%d, %d, %d).\n", - pointmark(torg), pointmark(tdest), pointmark(sapex(sface)), - pointmark(torg), pointmark(tdest), pointmark(sapex(sface1))); + // Go to the next subface in the ring of ab. + spivotself(spinsh); + } while (spinsh.sh != startsh.sh); + segloop.sh = shellfacetraverse(subsegs); + } + + if (b->refine) { + delete [] segsperverlist; + delete [] idx2seglist; + } + + // Create the indirect segment pbcgroups. + // Bug-fixed (08 Sept. 2006). The total size of 'segpbcgrouptable' may get + // increased. Do not use pointers for 'pd1' and 'pd2'. The addresses may + // be invaild after realloc(). + for (i = 0; i < segpbcgrouptable->len(); i++) { + pd1 = * (pbcdata *)(* segpbcgrouptable)[i]; + for (f1 = 0; f1 < 2; f1++) { + // Search for a group (except i) contains pd1.segid[f1]. + for (j = 0; j < segpbcgrouptable->len(); j++) { + if (j == i) continue; + pd2 = * (pbcdata *)(* segpbcgrouptable)[j]; + f2 = -1; + if (pd1.segid[f1] == pd2.segid[0]) { + f2 = 0; + } else if (pd1.segid[f1] == pd2.segid[1]) { + f2 = 1; + } + if (f2 != -1) { +#ifdef SELF_CHECK + assert(pd1.segid[f1] == pd2.segid[f2]); +#endif + segid1 = pd1.segid[1 - f1]; + segid2 = pd2.segid[1 - f2]; + // Search for the existence of segment pbcgroup (segid1, segid2). + inflag = false; + for (k = 0; k < segpbcgrouptable->len() && !inflag; k++) { + pd = (pbcdata *)(* segpbcgrouptable)[k]; + if (pd->segid[0] == segid1) { + if (pd->segid[1] == segid2) inflag = true; + } else if (pd->segid[0] == segid2) { + if (pd->segid[1] == segid1) inflag = true; + } + } + if (!inflag) { + pd = (pbcdata *) segpbcgrouptable->append(NULL); + pd->segid[0] = pd1.segid[1 - f1]; + pd->segid[1] = pd2.segid[1 - f2]; + pd->ss[0] = pd1.ss[1 - f1]; + pd->ss[1] = pd2.ss[1 - f2]; + // Invalid the fmark[0] == fmark[1]. + pd->fmark[0] = pd->fmark[1] = 0; + // Translate matrix pd->transmat[0] = m2 * m1, where m1 = + // pd1.transmat[1 - f1], m2 = pd2.transmat[f2]. + for (k = 0; k < 4; k++) { + for (l = 0; l < 4; l++) { + pd->transmat[0][k][l] = pd2.transmat[f2][k][l]; + } + } + m4xm4(pd->transmat[0], pd1.transmat[1 - f1]); + // Translate matrix pd->transmat[1] = m4 * m3, where m3 = + // pd2.transmat[1 - f2], m4 = pd1.transmat[f1]. + for (k = 0; k < 4; k++) { + for (l = 0; l < 4; l++) { + pd->transmat[1][k][l] = pd1.transmat[f1][k][l]; + } + } + m4xm4(pd->transmat[1], pd2.transmat[1 - f2]); + } } - sbond1(sface, sface1); - sface = sface1; } - } else { - // This segment belongs to only on subface. - sdissolve(sface); } - // Set the unique segment marker into the unified segment. - setshellmark(subsegloop, segmarker); - // Increase the marker. - segmarker++; - // Clear the working list. - sfacelist->clear(); - subsegloop.sh = shellfacetraverse(subsegs); } - delete [] idx2facelist; - delete [] facesperverlist; - delete sfacelist; + // Form a map from segment index to pbcgroup list of this segment. + idx2segpglist = new int[subsegs->items + 1]; + for (i = 0; i < subsegs->items + 1; i++) idx2segpglist[i] = 0; + // Loop through 'segpbcgrouptable', counter the number of pbcgroups of + // each segment. + for (i = 0; i < segpbcgrouptable->len(); i++) { + pd = (pbcdata *)(* segpbcgrouptable)[i]; + for (j = 0; j < 2; j++) { + k = pd->segid[j] - 1; + idx2segpglist[k]++; + } + } + // Calculate the total length of array 'segpglist'. + j = idx2segpglist[0]; + idx2segpglist[0] = 0; // Array starts from 0 element. + for (i = 0; i < subsegs->items; i++) { + k = idx2segpglist[i + 1]; + idx2segpglist[i + 1] = idx2segpglist[i] + j; + j = k; + } + // The total length is in the last unit of idx2segpglist. + segpglist = new int[idx2segpglist[i]]; + // Loop the set of pbcgroups again, set the data into segpglist. + for (i = 0; i < segpbcgrouptable->len(); i++) { + pd = (pbcdata *)(* segpbcgrouptable)[i]; + for (j = 0; j < 2; j++) { + k = pd->segid[j] - 1; + segpglist[idx2segpglist[k]] = i; + idx2segpglist[k]++; + } + } + // Contents in 'idx2segpglist' are shifted, now shift them back. + for (i = subsegs->items - 1; i >= 0; i--) { + idx2segpglist[i + 1] = idx2segpglist[i]; + } + idx2segpglist[0] = 0; } /////////////////////////////////////////////////////////////////////////////// // // -// assignsegmentmarkers() Assign markers given in "in->edgemarkerlist". // +// getsegpbcsympoint() Compute the symmetric point for a segment point. // +// // +// 'newpoint' lies on 'splitseg'. This routine calculates a 'sympoint' which // +// locates on 'symsplitseg' and symmtric to 'newpoint'. Return the location // +// of sympoint wrt. symsplitseg. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::assignsegmentmarkers() +enum tetgenmesh::locateresult tetgenmesh:: +getsegpbcsympoint(point newpoint, face* splitseg, point sympoint, + face* symsplitseg, int groupid) { - shellface **segsperverlist; - face sseg; - bool isseg; - int *idx2seglist; - int end1, end2, tend1, tend2; - int index, i, j; + pbcdata *pd; + enum locateresult symloc; + int segid, f1, f2, i; - if (b->verbose > 0) { - printf(" Assigning segment markers.\n"); + pd = (pbcdata *)(* segpbcgrouptable)[groupid]; + segid = shellmark(*splitseg); + if (pd->segid[0] == segid) { + f1 = 0; + } else { +#ifdef SELF_CHECK + assert(pd->segid[1] == segid); +#endif + f1 = 1; } + f2 = 1 - f1; - assert(in->edgemarkerlist != NULL); - makesegmentmap(idx2seglist, segsperverlist); - - for (i = 0; i < in->numberofedges; i++) { - end1 = in->edgelist[i * 2]; - end2 = in->edgelist[i * 2 + 1]; - index = end1 - in->firstnumber; - for (j = idx2seglist[index]; j < idx2seglist[index + 1]; j++) { - sseg.sh = segsperverlist[j]; - sseg.shver = 0; - isseg = false; - tend1 = pointmark(sorg(sseg)); - tend2 = pointmark(sdest(sseg)); - if (tend1 == end1) { - if (tend2 == end2) isseg = true; - } else if (tend1 == end2) { - if (tend2 == end1) isseg = true; - } - if (isseg) { - setshellmark(sseg, in->edgemarkerlist[i]); - break; - } - } + // Transform newpoint from f1 -> f2. + for (i = 0; i < 3; i++) { + sympoint[i] = pd->transmat[f1][i][0] * newpoint[0] + + pd->transmat[f1][i][1] * newpoint[1] + + pd->transmat[f1][i][2] * newpoint[2] + + pd->transmat[f1][i][3] * 1.0; } - - delete [] idx2seglist; - delete [] segsperverlist; + // Locate sympoint in f2. + *symsplitseg = pd->ss[f2]; +#ifdef SELF_CHECK + assert(symsplitseg->sh != dummysh); +#endif + // Locate sympoint in facet. Stop at subsegment. + symloc = locateseg(sympoint, symsplitseg); + symloc = adjustlocateseg(sympoint, symsplitseg, symloc, b->epsilon * 1e+2); + return symloc; } +// +// End of periodic boundary condition routines +// + +// +// Begin of vertex perturbation routines +// + /////////////////////////////////////////////////////////////////////////////// // // -// mergefacets() Merge adjacent facets to be one facet if they are // -// coplanar and have the same boundary marker. // -// // -// Segments between two merged facets will be removed from the mesh. If all // -// segments around a vertex have been removed, change its vertex type to be // -// FREESUBVERTEX. Edge flips will be performed to ensure the Delaunayness of // -// the triangulation of merged facets. // +// randgenerator() Generate a random REAL number between (0, |range|). // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::mergefacets(queue* flipqueue) +REAL tetgenmesh::randgenerator(REAL range) { - face parentsh, neighsh, neineighsh; - face segloop; - point eorg, edest; - REAL ori; - bool mergeflag, pbcflag; - int* segspernodelist; - int fidx1, fidx2; - int i, j; + REAL worknumber, result; + int expo; - if (b->verbose > 0) { - printf(" Merging coplanar facets.\n"); + if (range == 0.0) return 0.0; + + expo = 0; + worknumber = fabs(range); + // Normalize worknumber (i.e., 1.xxxExx) + if (worknumber > 10.0) { + while (worknumber > 10.0) { + worknumber /= 10.0; + expo++; + } + } else if (worknumber < 1.0) { + while (worknumber < 1.0) { + worknumber *= 10.0; + expo--; + } } - // Create and initialize 'segspernodelist'. - segspernodelist = new int[points->items + 1]; - for (i = 0; i < points->items + 1; i++) segspernodelist[i] = 0; +#ifdef SELF_CHECK + assert(worknumber >= 1.0 && worknumber <= 10.0); +#endif - // Loop the segments, counter the number of segments sharing each vertex. - subsegs->traversalinit(); - segloop.sh = shellfacetraverse(subsegs); - while (segloop.sh != (shellface *) NULL) { - // Increment the number of sharing segments for each endpoint. - for (i = 0; i < 2; i++) { - j = pointmark((point) segloop.sh[3 + i]); - segspernodelist[j]++; + // Enlarge worknumber 1000 times. + worknumber *= 1e+3; + expo -= 3; + // Generate a randome number between (0, worknumber). + result = (double) randomnation((int) worknumber); + + // Scale result back into the original size. + if (expo > 0) { + while (expo != 0) { + result *= 10.0; + expo--; + } + } else if (expo < 0) { + while (expo != 0) { + result /= 10.0; + expo++; } - segloop.sh = shellfacetraverse(subsegs); } +#ifdef SELF_CHECK + assert((result >= 0.0) && (result <= fabs(range))); +#endif - // Loop the segments, find out dead segments. - subsegs->traversalinit(); - segloop.sh = shellfacetraverse(subsegs); - while (segloop.sh != (shellface *) NULL) { - eorg = sorg(segloop); - edest = sdest(segloop); - spivot(segloop, parentsh); - if (parentsh.sh != dummysh) { - // This segment is not dangling. - spivot(parentsh, neighsh); - if (neighsh.sh != dummysh) { - // This segment belongs to at least two facets. - spivot(neighsh, neineighsh); - if ((parentsh.sh != neighsh.sh) && (parentsh.sh == neineighsh.sh)) { - // Exactly two subfaces at this segment. - fidx1 = shellmark(parentsh) - 1; - fidx2 = shellmark(neighsh) - 1; - pbcflag = false; - if (checkpbcs) { - pbcflag = (shellpbcgroup(parentsh) >= 0) - || (shellpbcgroup(neighsh) >= 0); + return result; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// checksub4cocir() Test a subface to find co-circular pair of subfaces. // +// // +// 'eps' is a relative tolerance for testing approximately cospherical case. // +// Set it to zero if only exact test is desired. // +// // +// An edge(not a segment) of 'testsub' is locally degenerate if the opposite // +// vertex of the adjacent subface is cocircular with the vertices of testsub.// +// If 'once' is TRUE, operate on the edge only if the pointer 'testsub->sh' // +// is smaller than its neighbor (for each edge is considered only once). // +// // +// Return TRUE if find an edge of testsub is locally degenerate. // +// // +/////////////////////////////////////////////////////////////////////////////// + +bool tetgenmesh::checksub4cocir(face* testsub, REAL eps, bool once, + bool enqflag) +{ + badface *cocirsub; + face subloop, neighsub; + face checkseg; + point pa, pb, pc, pd; + REAL sign; + int i; + + subloop = *testsub; + subloop.shver = 0; // Keep the CCW orientation. + // Get the abovepoint of the facet. + abovepoint = facetabovepointarray[shellmark(subloop)]; + // Do we need to calculate the abovepoint? + if (abovepoint == (point) NULL) { + getfacetabovepoint(&subloop); + } + // Check the three edges of subloop. + for (i = 0; i < 3; i++) { + sspivot(subloop, checkseg); + if (checkseg.sh == dummysh) { + // It is not a segment, get the adjacent subface. + spivot(subloop, neighsub); + // assert(neighsub.sh != dummysh); + if (!once || (once && (neighsub.sh > subloop.sh))) { + pa = sorg(subloop); + pb = sdest(subloop); + pc = sapex(subloop); + pd = sapex(neighsub); + sign = insphere(pa, pb, pc, abovepoint, pd); + if ((sign != 0.0) && (eps > 0.0)) { + if (iscospheric(pa, pb, pc, abovepoint, pd, sign, eps)) sign = 0.0; + } + if (sign == 0.0) { + // It's locally degenerate! + if (enqflag && badsubfaces != (memorypool *) NULL) { + // Save it. + cocirsub = (badface *) badsubfaces->alloc(); + cocirsub->ss = subloop; + cocirsub->forg = pa; + cocirsub->fdest = pb; + cocirsub->fapex = pc; + cocirsub->foppo = pd; + setshell2badface(cocirsub->ss, cocirsub); } - // Possibly merge them if they are not in the same facet. - if ((fidx1 != fidx2) && !pbcflag) { - // Test if they are coplanar. - ori = orient3d(eorg, edest, sapex(parentsh), sapex(neighsh)); - if (ori != 0.0) { - if (iscoplanar(eorg, edest, sapex(parentsh), sapex(neighsh), ori, - b->epsilon)) { - ori = 0.0; // They are assumed as coplanar. - } - } - if (ori == 0.0) { - mergeflag = (in->facetmarkerlist == (int *) NULL || - in->facetmarkerlist[fidx1] == in->facetmarkerlist[fidx2]); - if (mergeflag) { - // This segment becomes dead. - if (b->verbose > 1) { - printf(" Removing segment (%d, %d).\n", pointmark(eorg), - pointmark(edest)); - } - ssdissolve(parentsh); - ssdissolve(neighsh); - shellfacedealloc(subsegs, segloop.sh); - j = pointmark(eorg); - segspernodelist[j]--; - if (segspernodelist[j] == 0) { - setpointtype(eorg, FREESUBVERTEX); - } - j = pointmark(edest); - segspernodelist[j]--; - if (segspernodelist[j] == 0) { - setpointtype(edest, FREESUBVERTEX); - } - // Add 'parentsh' to queue checking for flip. - enqueueflipedge(parentsh, flipqueue); - } - } + if (b->verbose > 1) { + printf(" Found set (%d, %d, %d, %d).\n", pointmark(pa), + pointmark(pb), pointmark(pc), pointmark(pd)); } + return true; } - } // neighsh.sh != dummysh - } // parentsh.sh != dummysh - segloop.sh = shellfacetraverse(subsegs); - } - - if (!flipqueue->empty()) { - // Restore the Delaunay property in the facet triangulation. - lawson(flipqueue); + } + } + senextself(subloop); } - delete [] segspernodelist; + return false; } /////////////////////////////////////////////////////////////////////////////// // // -// meshsurface() Create the surface mesh of a PLC. // +// tallcocirsubs() Find all co-circular subfaces and save them in list. // // // -// Let X be the PLC, the surface mesh S of X consists of triangulated facets.// -// S is created mainly in the following steps: // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::tallcocirsubs(REAL eps, bool enqflag) +{ + face subloop; + + // Loop over all subfaces. + subfaces->traversalinit(); + subloop.sh = shellfacetraverse(subfaces); + while (subloop.sh != (shellface *) NULL) { + checksub4cocir(&subloop, eps, true, enqflag); + subloop.sh = shellfacetraverse(subfaces); + } +} + +/////////////////////////////////////////////////////////////////////////////// // // -// (1) Form the CDT of each facet of X separately (by routine triangulate()).// -// After it is done, the subfaces of each facet are connected to each other, // -// however there is no connection between facets yet. Notice each facet has // -// its own segments, some of them are duplicated. // +// tallencsegsfsubs() Check for encroached segs from a list of subfaces. // // // -// (2) Remove the redundant segments created in step (1) (by routine unify- // -// segment()). The subface ring of each segment is created, the connection // -// between facets are established as well. // +/////////////////////////////////////////////////////////////////////////////// + +bool tetgenmesh::tallencsegsfsubs(point testpt, list* cavsublist) +{ + face startsub, checkseg; + long oldencnum; + int i, j; + + // Remember the current number of encroached segments. + oldencnum = badsubsegs->items; + + // Check segments in the list of subfaces. + for (i = 0; i < cavsublist->len(); i++) { + startsub = * (face *)(* cavsublist)[i]; + // Test all three edges of startsub. + for (j = 0; j < 3; j++) { + sspivot(startsub, checkseg); + if (checkseg.sh != dummysh) { + if (!shell2badface(checkseg)) { + checkseg4encroach(&checkseg, testpt, NULL, true); + } + } + senextself(startsub); + } + } + + return (badsubsegs->items > oldencnum); +} + +/////////////////////////////////////////////////////////////////////////////// // // -// The return value indicates the number of segments of X. // +// collectflipedges() Collect edges of split subfaces for flip checking. // +// // +// 'inspoint' is a newly inserted segment point (inserted by insertsite()). // +// 'splitseg' is one of the two split subsegments. Some subfaces may be non- // +// Delaunay since they're still not bonded to CDT. This routine collect all // +// such possible subfaces in 'flipqueue'. // // // /////////////////////////////////////////////////////////////////////////////// -long tetgenmesh::meshsurface() +void tetgenmesh:: +collectflipedges(point inspoint, face* splitseg, queue* flipqueue) { - list *ptlist, *conlist; - queue *flipqueue; - tetgenio::facet *f; - tetgenio::polygon *p; - memorypool *viri; - point *idx2verlist; - point tstart, tend, *cons; - int *worklist; - int end1, end2; - int shmark, i, j; + face startsh, spinsh, checksh; + face nextseg; + point pa, pb; - if (!b->quiet) { - printf("Creating surface mesh.\n"); + // Let the dest of splitseg be inspoint. + splitseg->shver = 0; + if (sdest(*splitseg) != inspoint) { + sesymself(*splitseg); } +#ifdef SELF_CHECK + assert(sdest(*splitseg) == inspoint); +#endif + pa = sorg(*splitseg); + spivot(*splitseg, startsh); + spinsh = startsh; + do { + findedge(&spinsh, pa, inspoint); + senext2(spinsh, checksh); + enqueueflipedge(checksh, flipqueue); + spivotself(spinsh); + } while (spinsh.sh != startsh.sh); - // Compute a mapping from indices to points. - makeindex2pointmap(idx2verlist); - // // Compute a mapping from points to tets for computing abovepoints. - // makepoint2tetmap(); - // Initialize 'facetabovepointarray'. - facetabovepointarray = new point[in->numberoffacets + 1]; - for (i = 0; i < in->numberoffacets + 1; i++) { - facetabovepointarray[i] = (point) NULL; - } - if (checkpbcs) { - // Initialize the global array 'subpbcgrouptable'. - // createsubpbcgrouptable(); + // Get the next subsegment. + senext(*splitseg, nextseg); + spivotself(nextseg); +#ifdef SELF_CHECK + assert(nextseg.sh != (shellface *) NULL); +#endif + + // Let the org of nextseg be inspoint. + nextseg.shver = 0; + if (sorg(nextseg) != inspoint) { + sesymself(nextseg); } +#ifdef SELF_CHECK + assert(sorg(nextseg) == inspoint); +#endif + pb = sdest(nextseg); + spivot(nextseg, startsh); + spinsh = startsh; + do { + findedge(&spinsh, inspoint, pb); + senext(spinsh, checksh); + enqueueflipedge(checksh, flipqueue); + spivotself(spinsh); + } while (spinsh.sh != startsh.sh); +} - // Initialize working lists. - viri = new memorypool(sizeof(shellface *), 1024, POINTER, 0); - flipqueue = new queue(sizeof(badface)); - ptlist = new list(sizeof(point *), NULL, 256); - conlist = new list(sizeof(point *) * 2, NULL, 256); - worklist = new int[points->items + 1]; - for (i = 0; i < points->items + 1; i++) worklist[i] = 0; - - caveshlist = new arraypool(sizeof(face), 10); - caveshbdlist = new arraypool(sizeof(face), 10); +/////////////////////////////////////////////////////////////////////////////// +// // +// perturbrepairencsegs() Repair all encroached segments. // +// // +// All encroached segments are stored in 'badsubsegs'. Each segment will be // +// split by adding a perturbed point near its circumcenter. // +// // +/////////////////////////////////////////////////////////////////////////////// - // Loop the facet list, triangulate each facet. On finish, all subfaces - // are in 'subfaces', all segments are in 'subsegs'. Notice: there're - // redundant segments. Remember: All facet indices count from 1. - for (shmark = 1; shmark <= in->numberoffacets; shmark++) { - // Get a facet F. - f = &in->facetlist[shmark - 1]; +void tetgenmesh::perturbrepairencsegs(queue* flipqueue) +{ + badface *encloop; + tetrahedron encodedtet; + triface splittet; + face splitsub, symsplitsub; + face splitseg, symsplitseg; + point newpoint, sympoint; + point pa, pb, pc; + enum insertsiteresult success; + enum locateresult loc, symloc; + REAL cent[3], d1, ps, rs; + int i, j; - // Process the duplicated points first, they are marked with type - // DUPLICATEDVERTEX by incrflipdelaunay(). Let p and q are dup. - // and the index of p is larger than q's, p is substituted by q. - // In a STL mesh, duplicated points are implicitly included. - if ((b->object == tetgenbehavior::STL) || dupverts) { - // Loop all polygons of this facet. - for (i = 0; i < f->numberofpolygons; i++) { - p = &(f->polygonlist[i]); - // Loop other vertices of this polygon. - for (j = 0; j < p->numberofvertices; j++) { - end1 = p->vertexlist[j]; - tstart = idx2verlist[end1 - in->firstnumber]; - if (pointtype(tstart) == DUPLICATEDVERTEX) { - // Reset the index of vertex-j. - tend = point2ppt(tstart); - end2 = pointmark(tend); - p->vertexlist[j] = end2; + // Note that steinerleft == -1 if an unlimited number of Steiner points + // is allowed. Loop until 'badsubsegs' is empty. + badsubsegs->traversalinit(); + encloop = badfacetraverse(badsubsegs); + while ((encloop != (badface *) NULL) && (steinerleft != 0)) { + splitseg = encloop->ss; +#ifdef SELF_CHECK + assert(shell2badface(splitseg) == encloop); +#endif + setshell2badface(splitseg, NULL); + pa = sorg(splitseg); + pb = sdest(splitseg); + if ((pa == encloop->forg) && (pb == encloop->fdest)) { + if (b->verbose > 1) { + printf(" Get seg (%d, %d).\n", pointmark(pa), pointmark(pb)); + } + // Create the newpoint. + makepoint(&newpoint); + // Get the circumcenter and radius of ab. + for (i = 0; i < 3; i++) cent[i] = 0.5 * (pa[i] + pb[i]); + d1 = 0.5 * distance(pa, pb); + // Add a random perturbation to newpoint along the vector ab. + ps = randgenerator(d1 * 1.0e-3); + rs = ps / d1; + // Set newpoint (be at the perturbed circumcenter of ab). + for (i = 0; i < 3; i++) newpoint[i] = cent[i] + rs * (cent[i] - pa[i]); + setpointtype(newpoint, FREESEGVERTEX); + // Set splitseg into the newpoint. + setpoint2sh(newpoint, sencode(splitseg)); + + // Is there periodic boundary condition? + if (checkpbcs) { + // Insert points on other segments of incident pbcgroups. + i = shellmark(splitseg) - 1; + for (j = idx2segpglist[i]; j < idx2segpglist[i + 1]; j++) { + makepoint(&sympoint); + symloc = getsegpbcsympoint(newpoint, &splitseg, sympoint, + &symsplitseg, segpglist[j]); +#ifdef SELF_CHECK + assert(symloc != OUTSIDE); +#endif + // Note: the symsplitseg and splitseg may be identical, in case + // when the the splitseg is the axis of the rotational sym. + if ((symloc == ONEDGE) && (symsplitseg.sh != splitseg.sh)) { + setpointtype(sympoint, FREESEGVERTEX); + setpoint2sh(sympoint, sencode(symsplitseg)); + // Insert sympoint into DT. + pc = sorg(symsplitseg); + splittet.tet = dummytet; + // Find a good start point to search. + encodedtet = point2tet(pc); + if (encodedtet != (tetrahedron) NULL) { + decode(encodedtet, splittet); + if (isdead(&splittet)) { + splittet.tet = dummytet; + } + } + // Locate sympoint in DT. Do exact location. + success = insertsite(sympoint, &splittet, false, flipqueue); +#ifdef SELF_CHECK + assert(success != DUPLICATEPOINT); +#endif + if (success == OUTSIDEPOINT) { + inserthullsite(sympoint, &splittet, flipqueue); + } + if (steinerleft > 0) steinerleft--; + // Let sympoint remember splittet. + setpoint2tet(sympoint, encode(splittet)); + // Do flip in DT. + flip(flipqueue, NULL); + // Insert sympoint into F. + symloc = locateseg(sympoint, &symsplitseg); + if (symloc == ONEDGE) { + symsplitseg.shver = 0; + spivot(symsplitseg, symsplitsub); + // sympoint should on the edge of symsplitsub. + splitsubedge(sympoint, &symsplitsub, flipqueue); + } else { + // insertsite() has done the whole job. +#ifdef SELF_CHECK + assert(symloc == ONVERTEX); + assert(checksubfaces); +#endif + // Some edges may need to be flipped. + collectflipedges(sympoint, &symsplitseg, flipqueue); + } + // Do flip in facet. + flipsub(flipqueue); + } else { // if (symloc == ONVERTEX) { + // The symmtric point already exists. It is possible when two + // pbc group are idebtical. Omit sympoint. + pointdealloc(sympoint); } } } - } - // Loop polygons of F, get the set V of vertices and S of segments. - for (i = 0; i < f->numberofpolygons; i++) { - // Get a polygon. - p = &(f->polygonlist[i]); - // Get the first vertex. - end1 = p->vertexlist[0]; - if ((end1 < in->firstnumber) || - (end1 >= in->firstnumber + in->numberofpoints)) { - if (!b->quiet) { - printf("Warning: Invalid the 1st vertex %d of polygon", end1); - printf(" %d in facet %d.\n", i + 1, shmark); + // Insert newpoint into DT. + splittet.tet = dummytet; + // Find a good start point to search. + encodedtet = point2tet(pa); + if (encodedtet != (tetrahedron) NULL) { + decode(encodedtet, splittet); + if (isdead(&splittet)) { + splittet.tet = dummytet; } - continue; // Skip this polygon. - } - tstart = idx2verlist[end1 - in->firstnumber]; - // Add tstart to V if it haven't been added yet. - if (worklist[end1] == 0) { - ptlist->append(&tstart); - worklist[end1] = 1; } - // Loop other vertices of this polygon. - for (j = 1; j <= p->numberofvertices; j++) { - // get a vertex. - if (j < p->numberofvertices) { - end2 = p->vertexlist[j]; - } else { - end2 = p->vertexlist[0]; // Form a loop from last to first. - } - if ((end2 < in->firstnumber) || - (end2 >= in->firstnumber + in->numberofpoints)) { - if (!b->quiet) { - printf("Warning: Invalid vertex %d in polygon %d", end2, i + 1); - printf(" in facet %d.\n", shmark); + if (splittet.tet == dummytet) { // Try pb. + encodedtet = point2tet(pb); + if (encodedtet != (tetrahedron) NULL) { + decode(encodedtet, splittet); + if (isdead(&splittet)) { + splittet.tet = dummytet; } - } else { - if (end1 != end2) { - // 'end1' and 'end2' form a segment. - tend = idx2verlist[end2 - in->firstnumber]; - // Add tstart to V if it haven't been added yet. - if (worklist[end2] == 0) { - ptlist->append(&tend); - worklist[end2] = 1; - } - // Save the segment in S (conlist). - cons = (point *) conlist->append(NULL); - cons[0] = tstart; - cons[1] = tend; - // Set the start for next continuous segment. - end1 = end2; - tstart = tend; - } else { - // Two identical vertices represent an isolated vertex of F. - if (p->numberofvertices > 2) { - // This may be an error in the input, anyway, we can continue - // by simply skipping this segment. - if (!b->quiet) { - printf("Warning: Polygon %d has two identical verts", i + 1); - printf(" in facet %d.\n", shmark); - } - } - // Ignore this vertex. - } } - // Is the polygon degenerate (a segment or a vertex)? - if (p->numberofvertices == 2) break; - } - } - // Unmark vertices. - for (i = 0; i < ptlist->len(); i++) { - tstart = * (point *)(* ptlist)[i]; - end1 = pointmark(tstart); - assert(worklist[end1] == 1); - worklist[end1] = 0; - } - - // Create a CDT of F. - triangulate(shmark, b->epsilon * 1e+2, ptlist, conlist, f->numberofholes, - f->holelist, viri, flipqueue); - // Clear working lists. - ptlist->clear(); - conlist->clear(); - viri->restart(); - } - - delete caveshlist; - delete caveshbdlist; - caveshlist = NULL; - caveshbdlist = NULL; - - // Unify segments in 'subsegs', remove redundant segments. Face links - // of segments are also built. - unifysegments(); - /*if (in->numberofedges > 0) { - if (in->edgemarkerlist != NULL) { - assignsegmentmarkers(); + } + // Locate the newpoint in DT. Do exact location. + success = insertsite(newpoint, &splittet, false, flipqueue); +#ifdef SELF_CHECK + assert(success != DUPLICATEPOINT); +#endif + if (success == OUTSIDEPOINT) { + // A convex hull edge is mssing, and the inserting point lies + // (slightly) outside the convex hull due to the significant + // digits lost in the calculation. Enlarge the convex hull. + inserthullsite(newpoint, &splittet, flipqueue); + } + if (steinerleft > 0) steinerleft--; + // Let newpoint remember splittet. + setpoint2tet(newpoint, encode(splittet)); + // Do flip in DT. + flip(flipqueue, NULL); + // Insert newpoint into F. + loc = locateseg(newpoint, &splitseg); + if (loc == ONEDGE) { + splitseg.shver = 0; + spivot(splitseg, splitsub); + // newpoint should on the edge of splitsub. + splitsubedge(newpoint, &splitsub, flipqueue); + } else { + // insertsite() has done the whole job. +#ifdef SELF_CHECK + assert(loc == ONVERTEX); + assert(checksubfaces); +#endif + // Some edges may need to be flipped. + collectflipedges(newpoint, &splitseg, flipqueue); + } + // Do flip in facet. + flipsub(flipqueue); } - }*/ - - // Remember the number of input segments (for output). - insegments = subsegs->items; - - if (checkpbcs) { - // Create the global array 'segpbcgrouptable'. - // createsegpbcgrouptable(); - } - - if (b->object == tetgenbehavior::STL) { - // Remove redundant vertices (for .stl input mesh). - jettisonnodes(); - } - - if (!b->nomerge && !b->nobisect && !checkpbcs) { - // No '-M' switch - merge adjacent facets if they are coplanar. - mergefacets(flipqueue); + // Remove this entry from list. + badfacedealloc(badsubsegs, encloop); + // Get the next encroached segments. + encloop = badfacetraverse(badsubsegs); } - - // Create the point-to-segment map. - makepoint2segmap(); - - delete [] idx2verlist; - delete [] worklist; - delete ptlist; - delete conlist; - delete flipqueue; - delete viri; - - return subsegs->items; } /////////////////////////////////////////////////////////////////////////////// // // -// interecursive() Recursively do intersection test on a set of triangles.// -// // -// Recursively split the set 'subfacearray' of subfaces into two sets using // -// a cut plane parallel to x-, or, y-, or z-axies. The split criteria are // -// follows. Assume the cut plane is H, and H+ denotes the left halfspace of // -// H, and H- denotes the right halfspace of H; and s be a subface: // -// // -// (1) If all points of s lie at H+, put it into left array; // -// (2) If all points of s lie at H-, put it into right array; // -// (3) If some points of s lie at H+ and some of lie at H-, or some // -// points lie on H, put it into both arraies. // -// // -// Partitions by x-axis if axis == '0'; by y-axis if axis == '1'; by z-axis // -// if axis == '2'. If current cut plane is parallel to the x-axis, the next // -// one will be parallel to y-axis, and the next one after the next is z-axis,// -// and then alternately return back to x-axis. // +// perturbrepairencsubs() Repair all encroached subfaces. // // // -// Stop splitting when the number of triangles of the input array is not // -// decreased anymore. Do tests on the current set. // +// All encroached subfaces are stored in 'badsubfaces'. Each subface will be // +// split by adding a perturbed point near its circumcenter. However, if the // +// point encroaches some segments, it will not be inserted. Instead, the // +// encroached segments are split. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh:: -interecursive(shellface** subfacearray, int arraysize, int axis, REAL bxmin, - REAL bxmax, REAL bymin, REAL bymax, REAL bzmin, REAL bzmax, - int* internum) +void tetgenmesh::perturbrepairencsubs(list* cavsublist, queue* flipqueue) { - shellface **leftarray, **rightarray; - face sface1, sface2; - point p1, p2, p3; - point p4, p5, p6; - enum interresult intersect; - REAL split; - bool toleft, toright; - int leftsize, rightsize; - int i, j; - - if (b->verbose > 1) { - printf(" Recur %d faces. Bbox (%g, %g, %g),(%g, %g, %g). %s-axis\n", - arraysize, bxmin, bymin, bzmin, bxmax, bymax, bzmax, - axis == 0 ? "x" : (axis == 1 ? "y" : "z")); - } - - leftarray = new shellface*[arraysize]; - if (leftarray == NULL) { - terminatetetgen(1); - } - rightarray = new shellface*[arraysize]; - if (rightarray == NULL) { - terminatetetgen(1); - } - leftsize = rightsize = 0; + badface *encloop, *encsubseg; + tetrahedron encodedtet; + triface splittet; + face splitsub, symsplitsub; + face checkseg, symsplitseg; + point newpoint, sympoint; + point pa, pb, pc, pd; + enum insertsiteresult success; + enum locateresult loc, symloc; + REAL cent[3], d1, ps, rs; + bool reject; + int i; - 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. - split = 0.5 * (bzmin + bzmax); - } + // Note that steinerleft == -1 if an unlimited number of Steiner points + // is allowed. Loop until the list 'badsubfaces' is empty. + while ((badsubfaces->items > 0) && (steinerleft != 0)) { + badsubfaces->traversalinit(); + encloop = badfacetraverse(badsubfaces); + while ((encloop != (badface *) NULL) && (steinerleft != 0)) { + splitsub = encloop->ss; +#ifdef SELF_CHECK + assert(shell2badface(splitsub) == encloop); +#endif + setshell2badface(splitsub, NULL); + pa = sorg(splitsub); + pb = sdest(splitsub); + pc = sapex(splitsub); + // The subface may be not the same one when it was determined to be + // encroached. If its adjacent encroached subface was split, the + // consequent flips may change it into another subface. + if ((pa == encloop->forg) && (pb == encloop->fdest) && + (pc == encloop->fapex)) { + if (b->verbose > 1) { + printf(" Get subface (%d, %d, %d).\n", pointmark(pa), + pointmark(pb), pointmark(pc)); + } + // Create the newpoint. + makepoint(&newpoint); + // Get the circumcenter of abc. + circumsphere(pa, pb, pc, NULL, cent, &d1); +#ifdef SELF_CHECK + assert(d1 > 0.0); +#endif + // Add a random perturbation to newpoint along the vector a->cent. + // This way, the perturbed point still lies in the plane of abc. + ps = randgenerator(d1 * 1.0e-3); + rs = ps / d1; + // Set newpoint (be at the perturbed circumcenter of abc). + for (i = 0; i < 3; i++) newpoint[i] = cent[i] + rs * (cent[i] - pa[i]); + // Get the abovepoint of the facet. + abovepoint = facetabovepointarray[shellmark(splitsub)]; + // Do we need to calculate the abovepoint? + if (abovepoint == (point) NULL) { + getfacetabovepoint(&splitsub); + } + loc = locatesub(newpoint, &splitsub, 1, 0.0); +#ifdef SELF_CHECK + assert(loc != ONVERTEX); +#endif + if (loc != OUTSIDE) { + // Add 'splitsub' into 'cavsublist'. + cavsublist->append(&splitsub); + // Collect all subfaces that encroached by newpoint. + collectcavsubs(newpoint, cavsublist); + // Find if there are encroached segments. + reject = tallencsegsfsubs(newpoint, cavsublist); + // Clear cavsublist for the next use. + cavsublist->clear(); + } else { + // newpoint lies outside. splitsub contains the boundary segment. + sspivot(splitsub, checkseg); +#ifdef SELF_CHECK + assert(checkseg.sh != dummysh); +#endif + // Add this segment into list for splitting. + if (b->verbose > 2) { + printf(" Queuing boundary segment (%d, %d).\n", + pointmark(sorg(checkseg)), pointmark(sdest(checkseg))); + } + encsubseg = (badface *) badsubsegs->alloc(); + encsubseg->ss = checkseg; + encsubseg->forg = sorg(checkseg); + encsubseg->fdest = sdest(checkseg); + encsubseg->foppo = (point) NULL; + setshell2badface(encsubseg->ss, encsubseg); + // Reject newpoint. + reject = true; + } - for (i = 0; i < arraysize; i++) { - sface1.sh = subfacearray[i]; - p1 = (point) sface1.sh[3]; - p2 = (point) sface1.sh[4]; - p3 = (point) sface1.sh[5]; - toleft = toright = false; - if (p1[axis] < split) { - toleft = true; - if (p2[axis] >= split || p3[axis] >= split) { - toright = true; - } - } else if (p1[axis] > split) { - toright = true; - if (p2[axis] <= split || p3[axis] <= split) { - toleft = true; - } - } else { - // p1[axis] == split; - toleft = true; - toright = true; - } - // At least one is true; + if (!reject) { + // newpoint is going to be inserted. + + // Is there periodic boundary condition? + if (checkpbcs) { + if (shellpbcgroup(splitsub) >= 0) { + // Insert a point on another facet of the pbcgroup. + makepoint(&sympoint); + // Note: 'abovepoint' will be changed. + symloc = getsubpbcsympoint(newpoint, &splitsub, sympoint, + &symsplitsub); #ifdef SELF_CHECK - assert(!(toleft == false && toright == false)); + assert(symloc != ONVERTEX); #endif - if (toleft) { - leftarray[leftsize] = sface1.sh; - leftsize++; - } - if (toright) { - rightarray[rightsize] = sface1.sh; - rightsize++; - } - } + setpoint2pbcpt(newpoint, sympoint); + setpoint2pbcpt(sympoint, newpoint); + setpointtype(sympoint, FREESUBVERTEX); + // setpoint2sh(sympoint, sencode(symsplitsub)); + // Insert sympoint into DT. + pd = sorg(symsplitsub); + splittet.tet = dummytet; + // Find a good start point to search. + encodedtet = point2tet(pd); + if (encodedtet != (tetrahedron) NULL) { + decode(encodedtet, splittet); + if (isdead(&splittet)) { + splittet.tet = dummytet; + } + } + // Locate sympoint in DT. Do exact location. + success = insertsite(sympoint, &splittet, false, flipqueue); +#ifdef SELF_CHECK + assert(success != DUPLICATEPOINT); +#endif + if (success == OUTSIDEPOINT) { + inserthullsite(sympoint, &splittet, flipqueue); + } + if (steinerleft > 0) steinerleft--; + // Let sympoint remember splittet. + setpoint2tet(sympoint, encode(splittet)); + // Do flip in DT. + flip(flipqueue, NULL); + // Insert sympoint into F. + // getabovepoint(&symsplitsub); + // symloc = locatesub(sympoint, &symsplitsub, 1, 0.0); + if (symloc == ONFACE) { + splitsubface(sympoint, &symsplitsub, flipqueue); + } else if (symloc == ONEDGE) { + splitsubedge(sympoint, &symsplitsub, flipqueue); + } else { + // 'insertsite()' has done the whole job. +#ifdef SELF_CHECK + assert(symloc == ONVERTEX); + assert(checksubfaces); +#endif + // Split subfaces have been flipped. + flipqueue->clear(); + } + // Do flip in facet. + flipsub(flipqueue); + } + } - if (leftsize < arraysize && rightsize < arraysize) { - // Continue to partition the input set. Now 'subfacearray' has been - // split into two sets, it's memory can be freed. 'leftarray' and - // 'rightarray' will be freed in the next recursive (after they're - // partitioned again or performing tests). - delete [] subfacearray; - // Continue to split these two sets. - if (axis == 0) { - interecursive(leftarray, leftsize, 1, bxmin, split, bymin, bymax, - bzmin, bzmax, internum); - interecursive(rightarray, rightsize, 1, split, bxmax, bymin, bymax, - bzmin, bzmax, internum); - } else if (axis == 1) { - interecursive(leftarray, leftsize, 2, bxmin, bxmax, bymin, split, - bzmin, bzmax, internum); - interecursive(rightarray, rightsize, 2, bxmin, bxmax, split, bymax, - bzmin, bzmax, internum); - } else { - interecursive(leftarray, leftsize, 0, bxmin, bxmax, bymin, bymax, - bzmin, split, internum); - interecursive(rightarray, rightsize, 0, bxmin, bxmax, bymin, bymax, - split, bzmax, internum); - } - } else { - if (b->verbose > 1) { - printf(" Checking intersecting faces.\n"); - } - // Perform a brute-force compare on the set. - for (i = 0; i < arraysize; i++) { - sface1.sh = subfacearray[i]; - p1 = (point) sface1.sh[3]; - p2 = (point) sface1.sh[4]; - p3 = (point) sface1.sh[5]; - for (j = i + 1; j < arraysize; j++) { - sface2.sh = subfacearray[j]; - p4 = (point) sface2.sh[3]; - p5 = (point) sface2.sh[4]; - p6 = (point) sface2.sh[5]; - intersect = tri_tri_inter(p1, p2, p3, p4, p5, p6); - if (intersect == INTERSECT || intersect == SHAREFACE) { - if (!b->quiet) { - if (intersect == INTERSECT) { - printf(" Facet #%d intersects facet #%d at triangles:\n", - shellmark(sface1), shellmark(sface2)); - printf(" (%4d, %4d, %4d) and (%4d, %4d, %4d)\n", - pointmark(p1), pointmark(p2), pointmark(p3), - pointmark(p4), pointmark(p5), pointmark(p6)); - } else { - printf(" Facet #%d duplicates facet #%d at triangle:\n", - shellmark(sface1), shellmark(sface2)); - printf(" (%4d, %4d, %4d)\n", pointmark(p1), pointmark(p2), - pointmark(p3)); + // Insert newpoint into DT. + splittet.tet = dummytet; + // Find a good start point to search. + encodedtet = point2tet(pa); + if (encodedtet != (tetrahedron) NULL) { + decode(encodedtet, splittet); + if (isdead(&splittet)) { + splittet.tet = dummytet; } } - // Increase the number of intersecting pairs. - (*internum)++; - // Infect these two faces (although they may already be infected). - sinfect(sface1); - sinfect(sface2); + if (splittet.tet == dummytet) { // Try pb. + encodedtet = point2tet(pb); + if (encodedtet != (tetrahedron) NULL) { + decode(encodedtet, splittet); + if (isdead(&splittet)) { + splittet.tet = dummytet; + } + } + } + // Locate the newpoint in DT. Do exact location. + success = insertsite(newpoint, &splittet, false, flipqueue); +#ifdef SELF_CHECK + assert(success != DUPLICATEPOINT); +#endif + if (success == OUTSIDEPOINT) { + inserthullsite(newpoint, &splittet, flipqueue); + } + if (steinerleft > 0) steinerleft--; + // Let newpoint remember splittet. + setpoint2tet(newpoint, encode(splittet)); + // Do flip in DT. + flip(flipqueue, NULL); + // Insert newpoint into F. + // if (checkpbcs) { + // 'abovepoint' has been changed. + // getabovepoint(&splitsub); + // loc = locatesub(newpoint, &splitsub, 1, 0.0); + // } + if (loc == ONFACE) { + // Insert the newpoint in facet. + splitsubface(newpoint, &splitsub, flipqueue); + } else if (loc == ONEDGE) { + // Insert the newpoint in facet. + splitsubedge(newpoint, &splitsub, flipqueue); + } else { + // 'insertsite()' has done the whole job. +#ifdef SELF_CHECK + assert(loc == ONVERTEX); + assert(checksubfaces); +#endif + // Split subfaces have been flipped. + flipqueue->clear(); + } + // Set the type of the newpoint. + setpointtype(newpoint, FREESUBVERTEX); + // Set splitsub into the newpoint. + // setpoint2sh(newpoint, sencode(splitsub)); + // Do flip in the facet. + flipsub(flipqueue); + + // Remove this entry from list. + badfacedealloc(badsubfaces, encloop); + } else { + // newpoint is rejected. Remove it from points. + pointdealloc(newpoint); + // Repair all encroached segments. + perturbrepairencsegs(flipqueue); + // Do not remove 'encloop'. Later it will be tested again. + setshell2badface(encloop->ss, encloop); } + } else { + // This subface has been changed. Remove this entry from list. + badfacedealloc(badsubfaces, encloop); + // It may be co-circular with its neighbors. + // checksub4cocir(&splitsub, eps, false, true); } + // Get the next encroached subfaces. + encloop = badfacetraverse(badsubfaces); } - // Don't forget to free all three arrays. No further partition. - delete [] leftarray; - delete [] rightarray; - delete [] subfacearray; } } /////////////////////////////////////////////////////////////////////////////// // // -// detectinterfaces() Detect intersecting triangles. // -// // -// Given a set of triangles, find the pairs of intersecting triangles from // -// them. Here the set of triangles is in 'subfaces' which is a surface mesh // -// of a PLC (.poly or .smesh). // -// // -// To detect whether two triangles are intersecting is done by the routine // -// 'tri_tri_inter()'. The algorithm for the test is very simple and stable. // -// It is based on geometric orientation test which uses exact arithmetics. // +// incrperturbvertices() Remove the local degeneracies in DT. // // // -// Use divide-and-conquer algorithm for reducing the number of intersection // -// tests. Start from the bounding box of the input point set, recursively // -// partition the box into smaller boxes, until the number of triangles in a // -// box is not decreased anymore. Then perform triangle-triangle tests on the // -// remaining set of triangles. The memory allocated in the input set is // -// freed immediately after it has been partitioned into two arrays. So it // -// can be re-used for the consequent partitions. // +// A local degeneracy of a DT D is a set of 5 or more vertices which share a // +// common sphere S and no other vertex of D in S. D is not unique if it has // +// local degeneracies. This routine removes the local degeneracies from D by // +// inserting break points (as described in reference [2]). // // // -// On return, the pool 'subfaces' will be cleared, and only the intersecting // -// triangles remain for output (to a .face file). // +// 'eps' is a user-provided error tolerance. It is used to detect whether or // +// not five points are approximate cospherical (evaluated in iscospheric()). // +// Set it to 0.0 to disable it, i.e., only test pure degenerate point set. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::detectinterfaces() +void tetgenmesh::incrperturbvertices(REAL eps) { - shellface **subfacearray; - face shloop; - int internum; - int i; - - if (!b->quiet) { - printf("Detecting intersecting facets.\n"); - } - - // Construct a map from indices to subfaces; - subfacearray = new shellface*[subfaces->items]; - subfaces->traversalinit(); - shloop.sh = shellfacetraverse(subfaces); - i = 0; - while (shloop.sh != (shellface *) NULL) { - subfacearray[i] = shloop.sh; - shloop.sh = shellfacetraverse(subfaces); - i++; - } - - internum = 0; - // Recursively split the set of triangles into two sets using a cut plane - // parallel to x-, or, y-, or z-axies. Stop splitting when the number - // of subfaces is not decreasing anymore. Do tests on the current set. - interecursive(subfacearray, subfaces->items, 0, xmin, xmax, ymin, ymax, - zmin, zmax, &internum); + queue *flipqueue; + list *cavsublist; + long vertcount; if (!b->quiet) { - if (internum > 0) { - printf("\n!! Found %d pairs of faces are intersecting.\n\n", internum); - } else { - printf("\nNo faces are intersecting.\n\n"); - } + printf("Perturbing vertices.\n"); } - if (internum > 0) { - // Traverse all subfaces, deallocate those have not been infected (they - // are not intersecting faces). Uninfect those have been infected. - // After this loop, only intersecting faces remain. - subfaces->traversalinit(); - shloop.sh = shellfacetraverse(subfaces); - while (shloop.sh != (shellface *) NULL) { - if (sinfected(shloop)) { - suninfect(shloop); - } else { - shellfacedealloc(subfaces, shloop.sh); - } - shloop.sh = shellfacetraverse(subfaces); - } - } else { - // Deallocate all subfaces. - subfaces->restart(); + vertcount = points->items; + // Create a map from points to tets for fastening search. + // makepoint2tetmap(); // This has been done in meshsurface(). + + // Initialize working queues, lists. + flipqueue = new queue(sizeof(badface)); + cavsublist = new list(sizeof(face), NULL, 256); + // Initialize the pool of encroached subfaces and subsegments. + badsubsegs = new memorypool(sizeof(badface), SUBPERBLOCK, POINTER, 0); + badsubfaces = new memorypool(sizeof(badface), SUBPERBLOCK, POINTER, 0); + // Find all pairs of co-circular subfaces. + tallcocirsubs(eps, true); + if (b->verbose && badsubfaces->items > 0) { + printf(" Removing degenerate subfaces.\n"); + } + perturbrepairencsubs(cavsublist, flipqueue); + + if (b->verbose > 0) { + printf(" %ld break points.\n", points->items - vertcount); } + + delete cavsublist; + delete flipqueue; + delete badsubfaces; + delete badsubsegs; + badsubsegs = (memorypool *) NULL; + badsubfaces = (memorypool *) NULL; } -//// //// -//// //// -//// surface_cxx ////////////////////////////////////////////////////////////// +// +// End of vertex perturbation routines +// -//// constrained_cxx ////////////////////////////////////////////////////////// -//// //// -//// //// +// +// Begin of segment recovery routines +// /////////////////////////////////////////////////////////////////////////////// // // @@ -18906,5536 +20315,4304 @@ finddirection(triface *searchtet, point tend, long maxtetnumber) fnextself(*searchtet); esymself(*searchtet); enextself(*searchtet); // org(*searchtet) == tstart; - return ACROSSEDGE; - } else { // ori3 == 0.0; - // Collinear with edge (org, oppo) - return TOPCOLLINEAR; - } - } - } else { // ori1 == 0.0; - // Possible cases are: RIGHTCOLLINEAR, LEFTCOLLINEAR, ACROSSEDGE. - if (ori2 < 0.0) { - if (ori3 < 0.0) { - // Cross edge (tdest, tapex) - return ACROSSEDGE; - } else { // ori3 == 0.0 - // Collinear with edge (torg, tapex) - return LEFTCOLLINEAR; - } - } else { // ori2 == 0.0; -#ifdef SELF_CHECK - assert(ori3 != 0.0); -#endif - // Collinear with edge (torg, tdest) - return RIGHTCOLLINEAR; - } - } - } - // Loop breakout. It may happen when the mesh is non-Delaunay. - return BELOWHULL; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// finddirection() Find the tet on the path from one point to another. // -// // -// The path starts from 'searchtet''s origin and ends at 'endpt'. On finish, // -// 'searchtet' contains a tet on the path, its origin does not change. // -// // -// The return value indicates one of the following cases (let 'searchtet' be // -// abcd, a is the origin of the path): // -// - ACROSSVERT, edge ab is collinear with the path; // -// - ACROSSEDGE, edge bc intersects with the path; // -// - ACROSSFACE, face bcd intersects with the path. // -// // -// WARNING: This routine is designed for convex triangulations, and will not // -// generally work after the holes and concavities have been carved. // -// - BELOWHULL2, the mesh is non-convex and the searching for the path has // -// got stucked at a non-convex boundary face. // -// // -/////////////////////////////////////////////////////////////////////////////// - -enum tetgenmesh::interresult tetgenmesh::finddirection2(triface* searchtet, - point endpt) -{ - triface neightet; - point pa, pb, pc, pd, pn; - enum {HMOVE, RMOVE, LMOVE} nextmove; - enum {HCOPLANE, RCOPLANE, LCOPLANE, NCOPLANE} cop; - REAL hori, rori, lori; - REAL dmin, dist; - - assert((searchtet->tet != NULL) && (searchtet->tet != dummytet)); - - // The origin is fixed. - pa = org(*searchtet); - if (searchtet->ver & 01) { - // Switch to the 0th edge ring. - esymself(*searchtet); - enextself(*searchtet); - } - pb = dest(*searchtet); - if (pb == endpt) { - // pa->pb is the search edge. - return INTERVERT; - } - pc = apex(*searchtet); - if (pc == endpt) { - // pa->pc is the search edge. - enext2self(*searchtet); - esymself(*searchtet); - return INTERVERT; - } - - // Walk through tets at pa until the right one is found. - while (1) { - - pd = oppo(*searchtet); - - if (b->verbose > 2) { - 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. - fnextself(*searchtet); - enext2self(*searchtet); - esymself(*searchtet); - return INTERVERT; - } - - // Now assume that the base face abc coincides with the horizon plane, - // and d lies above the horizon. The search point 'endpt' may lie - // above or below the horizon. We test the orientations of 'endpt' - // with respect to three planes: abc (horizon), bad (right plane), - // and acd (left plane). - 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'. - if (hori > 0) { - if (rori > 0) { - if (lori > 0) { - // Any of the three neighbors is a viable move. - nextmove = HMOVE; - sym(*searchtet, neightet); - if (neightet.tet != dummytet) { - pn = oppo(neightet); - dmin = NORM2(endpt[0] - pn[0], endpt[1] - pn[1], endpt[2] - pn[2]); - } else { - dmin = NORM2(xmax - xmin, ymax - ymin, zmax - zmin); - } - fnext(*searchtet, neightet); - symself(neightet); - if (neightet.tet != dummytet) { - pn = oppo(neightet); - dist = NORM2(endpt[0] - pn[0], endpt[1] - pn[1], endpt[2] - pn[2]); - } else { - dist = dmin; - } - if (dist < dmin) { - nextmove = RMOVE; - dmin = dist; - } - enext2fnext(*searchtet, neightet); - symself(neightet); - if (neightet.tet != dummytet) { - pn = oppo(neightet); - dist = NORM2(endpt[0] - pn[0], endpt[1] - pn[1], endpt[2] - pn[2]); - } else { - dist = dmin; - } - if (dist < dmin) { - nextmove = LMOVE; - dmin = dist; - } - } else { - // Two tets, below horizon and below right, are viable. - nextmove = HMOVE; - sym(*searchtet, neightet); - if (neightet.tet != dummytet) { - pn = oppo(neightet); - dmin = NORM2(endpt[0] - pn[0], endpt[1] - pn[1], endpt[2] - pn[2]); - } else { - dmin = NORM2(xmax - xmin, ymax - ymin, zmax - zmin); - } - fnext(*searchtet, neightet); - symself(neightet); - if (neightet.tet != dummytet) { - pn = oppo(neightet); - dist = NORM2(endpt[0] - pn[0], endpt[1] - pn[1], endpt[2] - pn[2]); - } else { - dist = dmin; - } - if (dist < dmin) { - nextmove = RMOVE; - dmin = dist; - } - } - } else { - if (lori > 0) { - // Two tets, below horizon and below left, are viable. - nextmove = HMOVE; - sym(*searchtet, neightet); - if (neightet.tet != dummytet) { - pn = oppo(neightet); - dmin = NORM2(endpt[0] - pn[0], endpt[1] - pn[1], endpt[2] - pn[2]); - } else { - dmin = NORM2(xmax - xmin, ymax - ymin, zmax - zmin); - } - enext2fnext(*searchtet, neightet); - symself(neightet); - if (neightet.tet != dummytet) { - pn = oppo(neightet); - dist = NORM2(endpt[0] - pn[0], endpt[1] - pn[1], endpt[2] - pn[2]); - } else { - dist = dmin; - } - if (dist < dmin) { - nextmove = LMOVE; - dmin = dist; - } - } else { - // The tet below horizon is chosen. - nextmove = HMOVE; - } - } - } else { - if (rori > 0) { - if (lori > 0) { - // Two tets, below right and below left, are viable. - nextmove = RMOVE; - fnext(*searchtet, neightet); - symself(neightet); - if (neightet.tet != dummytet) { - pn = oppo(neightet); - dmin = NORM2(endpt[0] - pn[0], endpt[1] - pn[1], endpt[2] - pn[2]); - } else { - dmin = NORM2(xmax - xmin, ymax - ymin, zmax - zmin); - } - enext2fnext(*searchtet, neightet); - symself(neightet); - if (neightet.tet != dummytet) { - pn = oppo(neightet); - dist = NORM2(endpt[0] - pn[0], endpt[1] - pn[1], endpt[2] - pn[2]); - } else { - dist = dmin; - } - if (dist < dmin) { - nextmove = LMOVE; - dmin = dist; - } - } else { - // The tet below right is chosen. - nextmove = RMOVE; - } - } else { - if (lori > 0) { - // The tet below left is chosen. - nextmove = LMOVE; - } else { - // 'endpt' lies either on the plane(s) or across face bcd. - if (hori == 0) { - if (rori == 0) { - // pa->'endpt' is COLLINEAR with pa->pb. - return INTERVERT; - } - if (lori == 0) { - // pa->'endpt' is COLLINEAR with pa->pc. - enext2self(*searchtet); - esymself(*searchtet); - return INTERVERT; - } - // pa->'endpt' crosses the edge pb->pc. - // enextself(*searchtet); - // return INTEREDGE; - cop = HCOPLANE; - break; - } - if (rori == 0) { - if (lori == 0) { - // pa->'endpt' is COLLINEAR with pa->pd. - fnextself(*searchtet); // face abd. - enext2self(*searchtet); - esymself(*searchtet); - return INTERVERT; - } - // pa->'endpt' crosses the edge pb->pd. - // fnextself(*searchtet); // face abd. - // enextself(*searchtet); - // return INTEREDGE; - cop = RCOPLANE; - break; - } - if (lori == 0) { - // pa->'endpt' crosses the edge pc->pd. - // enext2fnextself(*searchtet); // face cad - // enext2self(*searchtet); - // return INTEREDGE; - cop = LCOPLANE; - break; - } - // pa->'endpt' crosses the face bcd. - // enextfnextself(*searchtet); - // return INTERFACE; - cop = NCOPLANE; - break; - } - } - } - - // Move to the next tet, fix pa as its origin. - if (nextmove == RMOVE) { - tfnextself(*searchtet); - } else if (nextmove == LMOVE) { - enext2self(*searchtet); - tfnextself(*searchtet); - enextself(*searchtet); - } else { // HMOVE - symedgeself(*searchtet); - enextself(*searchtet); - } - // Assume convex case, we should not move to outside. - if (searchtet->tet == dummytet) { - // This should only happen when the domain is non-convex. - return BELOWHULL2; - } - assert(org(*searchtet) == pa); // SELF_CHECK - pb = dest(*searchtet); - pc = apex(*searchtet); - - } // while (1) - - // Either case INTEREDGE or INTERFACE. - /*if (b->epsilon > 0) { - // Use tolerance to re-evaluate the orientations. - if (cop != HCOPLANE) { - if (iscoplanar(pa, pb, pc, endpt, hori)) hori = 0; - } - if (cop != RCOPLANE) { - if (iscoplanar(pb, pa, pd, endpt, rori)) rori = 0; - } - if (cop != LCOPLANE) { - if (iscoplanar(pa, pc, pd, endpt, lori)) lori = 0; - } - // It is not possible that all orientations are zero. - assert(!((hori == 0) && (rori == 0) && (lori == 0))); // SELF_CHECK - }*/ - - // Now decide the degenerate cases. - if (hori == 0) { - if (rori == 0) { - // pa->'endpt' is COLLINEAR with pa->pb. - return INTERVERT; - } - if (lori == 0) { - // pa->'endpt' is COLLINEAR with pa->pc. - enext2self(*searchtet); - esymself(*searchtet); - return INTERVERT; - } - // pa->'endpt' crosses the edge pb->pc. - return INTEREDGE; - } - if (rori == 0) { - if (lori == 0) { - // pa->'endpt' is COLLINEAR with pa->pd. - fnextself(*searchtet); // face abd. - enext2self(*searchtet); - esymself(*searchtet); - return INTERVERT; - } - // pa->'endpt' crosses the edge pb->pd. - fnextself(*searchtet); // face abd. - esymself(*searchtet); - enextself(*searchtet); - return INTEREDGE; - } - if (lori == 0) { - // pa->'endpt' crosses the edge pc->pd. - enext2fnextself(*searchtet); // face cad - esymself(*searchtet); - return INTEREDGE; - } - // pa->'endpt' crosses the face bcd. - return INTERFACE; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// finddirection3() Used when finddirection2() returns BELOWHULL2. // -// // -/////////////////////////////////////////////////////////////////////////////// - -enum tetgenmesh::interresult tetgenmesh::finddirection3(triface* searchtet, - point endpt) -{ - arraypool *startetlist; - triface *parytet, oppoface, neightet; - point startpt, pa, pb, pc; - enum interresult dir; - int types[2], poss[4]; - int pos, i, j; - - startetlist = new arraypool(sizeof(triface), 8); - startpt = org(*searchtet); - infect(*searchtet); - startetlist->newindex((void **) &parytet); - *parytet = *searchtet; - - if (b->verbose > 1) { - printf(" Search path (%d, %d) under non-convexity.\n", - pointmark(startpt), pointmark(endpt)); - } - - for (i = 0; i < (int) startetlist->objects; i++) { - parytet = (triface *) fastlookup(startetlist, i); - *searchtet = *parytet; - // assert(org(*searchtet) == startpt); - adjustedgering(*searchtet, CCW); - if (org(*searchtet) != startpt) { - enextself(*searchtet); - assert(org(*searchtet) == startpt); - } - // Go to the opposite face of startpt. - enextfnext(*searchtet, oppoface); - esymself(oppoface); - pa = org(oppoface); - pb = dest(oppoface); - pc = apex(oppoface); - // Check if face [a, b, c] intersects the searching path. - if (tri_edge_test(pa, pb, pc, startpt, endpt, NULL, 1, types, poss)) { - // They intersect. Get the type of intersection. - dir = (enum interresult) types[0]; - pos = poss[0]; - break; - } else { - dir = DISJOINT; - } - // Get the neighbor tets. - for (j = 0; j < 3; j++) { - if (j == 0) { - symedge(*searchtet, neightet); - } else if (j == 1) { - fnext(*searchtet, neightet); - symedgeself(neightet); - } else { - enext2fnext(*searchtet, neightet); - symedgeself(neightet); - } - if (neightet.tet != dummytet) { - if (!infected(neightet)) { - if (org(neightet) != startpt) esymself(neightet); - infect(neightet); - startetlist->newindex((void **) &parytet); - *parytet = neightet; - } - } - } - } - - for (i = 0; i < (int) startetlist->objects; i++) { - parytet = (triface *) fastlookup(startetlist, i); - uninfect(*parytet); - } - delete startetlist; - - if (dir == INTERVERT) { - // This path passing a vertex of the face [a, b, c]. - if (pos == 0) { - // The path acrosses pa. - enext2self(*searchtet); - esymself(*searchtet); - } else if (pos == 1) { - // The path acrosses pa. - } else { // pos == 2 - // The path acrosses pc. - fnextself(*searchtet); - enext2self(*searchtet); - esymself(*searchtet); - } - return INTERVERT; - } - if (dir == INTEREDGE) { - // This path passing an edge of the face [a, b, c]. - if (pos == 0) { - // The path intersects [pa, pb]. - } else if (pos == 1) { - // The path intersects [pb, pc]. - fnextself(*searchtet); - enext2self(*searchtet); - esymself(*searchtet); - } else { // pos == 2 - // The path intersects [pc, pa]. - enext2fnextself(*searchtet); - esymself(*searchtet); - } - return INTEREDGE; - } - if (dir == INTERFACE) { - return INTERFACE; - } - - // The path does not intersect any tet at pa. - return BELOWHULL2; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// scoutsegment() Look for a given segment in the tetrahedralization T. // -// // -// 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. // -// // -// The returned value indicates one of the following cases: // -// - SHAREEDGE, the segment exists and is inserted in T; // -// - INTERVERT, the segment intersects a vertex ('refpt'). // -// - INTEREDGE, the segment intersects an edge (in 'searchtet'). // -// - INTERFACE, the segment crosses a face (in 'searchtet'). // -// // -// If the returned value is INTEREDGE or INTERFACE, i.e., the segment is // -// missing, 'refpt' returns the reference point for splitting thus segment, // -// 'searchtet' returns a tet containing the 'refpt'. // -// // -/////////////////////////////////////////////////////////////////////////////// - -enum tetgenmesh::interresult tetgenmesh::scoutsegment2(face* sseg, - triface* searchtet, point* refpt) -{ - triface neightet, reftet; - face splitsh, checkseg; - point startpt, endpt; - point pa, pb, pc, pd; - enum interresult dir; - REAL angmax, ang; - long facecount; - int hitbdry; - int types[2], poss[4]; - int pos, i; - - // Is 'searchtet' a valid handle? - if ((searchtet->tet == NULL) || (searchtet->tet == dummytet)) { - startpt = sorg(*sseg); - point2tetorg(startpt, *searchtet); - } else { - startpt = sorg(*sseg); - } - assert(org(*searchtet) == startpt); // SELF_CHECK - endpt = sdest(*sseg); - - if (b->verbose > 1) { - printf(" Scout seg (%d, %d).\n", pointmark(startpt), pointmark(endpt)); - } - - dir = finddirection2(searchtet, endpt); - - if (dir == INTERVERT) { - pd = dest(*searchtet); - if (pd == endpt) { - // Found! Insert the segment. - tsspivot1(*searchtet, checkseg); // SELF_CHECK - if (checkseg.sh == dummysh) { - neightet = *searchtet; - hitbdry = 0; - do { - tssbond1(neightet, *sseg); - tfnextself(neightet); - if (neightet.tet == dummytet) { - hitbdry++; - if (hitbdry == 2) break; - esym(*searchtet, neightet); - tfnextself(neightet); - if (neightet.tet == dummytet) break; - } - } while (neightet.tet != searchtet->tet); - } else { - // Collision! This can happy during facet recovery. - // See fig/dump-cavity-case19, -case20. - assert(checkseg.sh == sseg->sh); // SELF_CHECK - } - // The job is done. - return SHAREEDGE; - } else { - // A point is on the path. - *refpt = pd; - return INTERVERT; - } - } - - if (b->verbose > 1) { - printf(" Scout ref point of seg (%d, %d).\n", pointmark(startpt), - pointmark(endpt)); - } - facecount = across_face_count; - - enextfnextself(*searchtet); // Go to the opposite face. - symedgeself(*searchtet); // Enter the adjacent tet. - - pa = org(*searchtet); - angmax = interiorangle(pa, startpt, endpt, NULL); - *refpt = pa; - pb = dest(*searchtet); - ang = interiorangle(pb, startpt, endpt, NULL); - if (ang > angmax) { - angmax = ang; - *refpt = pb; - } - - // Check whether two segments are intersecting. - if (dir == INTEREDGE) { - tsspivot1(*searchtet, checkseg); - if (checkseg.sh != dummysh) { - printf("Error: Invalid PLC. Two segments intersect.\n"); - startpt = getsubsegfarorg(sseg); - endpt = getsubsegfardest(sseg); - pa = getsubsegfarorg(&checkseg); - pb = getsubsegfardest(&checkseg); - printf(" 1st: (%d, %d), 2nd: (%d, %d).\n", pointmark(startpt), - pointmark(endpt), pointmark(pa), pointmark(pb)); - terminatetetgen(3); - } - across_edge_count++; - } - - pc = apex(*searchtet); - ang = interiorangle(pc, startpt, endpt, NULL); - if (ang > angmax) { - angmax = ang; - *refpt = pc; - } - reftet = *searchtet; // Save the tet containing the refpt. - - // Search intersecting faces along the segment. - while (1) { - - pd = oppo(*searchtet); - - if (b->verbose > 2) { - 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; - - ang = interiorangle(pd, startpt, endpt, NULL); - if (ang > angmax) { - angmax = ang; - *refpt = pd; - reftet = *searchtet; - } - - // Find a face intersecting the segment. - if (dir == INTERFACE) { - // One of the three oppo faces in 'searchtet' intersects the segment. - neightet.tet = searchtet->tet; - neightet.ver = 0; - for (i = 0; i < 3; i++) { - neightet.loc = locpivot[searchtet->loc][i]; - 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; + return ACROSSEDGE; + } else { // ori3 == 0.0; + // Collinear with edge (org, oppo) + return TOPCOLLINEAR; } } - assert(dir != DISJOINT); // SELF_CHECK - } else { // dir == ACROSSEDGE - // Check the two opposite faces (of the edge) in 'searchtet'. - neightet = *searchtet; - neightet.ver = 0; - for (i = 0; i < 2; i++) { - neightet.loc = locverpivot[searchtet->loc][searchtet->ver][i]; - 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; + } else { // ori1 == 0.0; + // Possible cases are: RIGHTCOLLINEAR, LEFTCOLLINEAR, ACROSSEDGE. + if (ori2 < 0.0) { + if (ori3 < 0.0) { + // Cross edge (tdest, tapex) + return ACROSSEDGE; + } else { // ori3 == 0.0 + // Collinear with edge (torg, tapex) + return LEFTCOLLINEAR; } + } else { // ori2 == 0.0; +#ifdef SELF_CHECK + assert(ori3 != 0.0); +#endif + // Collinear with edge (torg, tdest) + return RIGHTCOLLINEAR; } - if (dir == DISJOINT) { - // No intersection. Go to the next tet. - dir = INTEREDGE; - tfnextself(*searchtet); - continue; - } - } - - if (dir == INTERVERT) { - // This segment passing a vertex. Choose it and return. - for (i = 0; i < pos; i++) { - enextself(neightet); - } - pd = org(neightet); - if (b->verbose > 2) { - angmax = interiorangle(pd, startpt, endpt, NULL); - } - *refpt = pd; - break; - } - if (dir == INTEREDGE) { - // Get the edge intersects with the segment. - for (i = 0; i < pos; i++) { - enextself(neightet); - } - } - // Go to the next tet. - symedge(neightet, *searchtet); - - if (dir == INTEREDGE) { - // Check whether two segments are intersecting. - tsspivot1(*searchtet, checkseg); - if (checkseg.sh != dummysh) { - printf("Error: Invalid PLC! Two segments intersect.\n"); - startpt = getsubsegfarorg(sseg); - endpt = getsubsegfardest(sseg); - pa = getsubsegfarorg(&checkseg); - pb = getsubsegfardest(&checkseg); - printf(" 1st: (%d, %d), 2nd: (%d, %d).\n", pointmark(startpt), - pointmark(endpt), pointmark(pa), pointmark(pb)); - terminatetetgen(3); - } - across_edge_count++; } - - } // while (1) - - // dir is either ACROSSVERT, or ACROSSEDGE, or ACROSSFACE. - if (b->verbose > 2) { - printf(" Refpt %d (%g), visited %ld faces.\n", pointmark(*refpt), - 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; + // Loop breakout. It may happen when the mesh is non-Delaunay. + return BELOWHULL; } /////////////////////////////////////////////////////////////////////////////// // // -// getsegmentsplitpoint() Calculate a split point in the given segment. // +// getsearchtet() Find a tetrahedron whose origin is either 'p1' or 'p2'. // +// // +// On return, the origin of 'searchtet' is either 'p1' or 'p2', and 'tend' // +// returns the other point. 'searchtet' serves as the starting tetrahedron // +// for searching of the line segment from 'p1' to 'p2' or vice versa. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::getsegmentsplitpoint2(face* sseg, point refpt, REAL* vt) +void tetgenmesh::getsearchtet(point p1, point p2, triface* searchtet, + point* tend) { - point ei, ej, ek; - REAL split, L, d, d1, d2, d3; - int stype, sign; - int i; - - // Decide the type of this segment. - sign = 1; - ei = sorg(*sseg); - ej = sdest(*sseg); + tetrahedron encodedtet1, encodedtet2; - if (pointtype(ei) == ACUTEVERTEX) { - if (pointtype(ej) == ACUTEVERTEX) { - // Both ei and ej are ACUTEVERTEX. - stype = 0; - } else { - // ej is either a NACUTEVERTEX or a STEINERVERTEX. - stype = 1; - } - } else { - if (pointtype(ei) == NACUTEVERTEX) { - if (pointtype(ej) == ACUTEVERTEX) { - stype = 1; sign = -1; - } else { - if (pointtype(ej) == NACUTEVERTEX) { - // Both ei and ej are non-acute. - stype = 0; - } else { - // ej is a STEINERVETEX. - ek = getsubsegfardest(sseg); - if (pointtype(ek) == ACUTEVERTEX) { - stype = 1; sign = -1; - } else { - stype = 0; - } - } - } + // Is there a valid handle provided by the user? + if ((searchtet->tet != (tetrahedron *) NULL) && !isdead(searchtet)) { + // Find which endpoint the handle holds. + if (findorg(searchtet, p1)) { + *tend = p2; + return; } else { - // ei is a STEINERVERTEX. - if (pointtype(ej) == ACUTEVERTEX) { - stype = 1; sign = -1; - } else { - ek = getsubsegfarorg(sseg); - if (pointtype(ej) == NACUTEVERTEX) { - if (pointtype(ek) == ACUTEVERTEX) { - stype = 1; - } else { - stype = 0; - } - } else { - // Both ei and ej are STEINERVETEXs. ei has priority. - if (pointtype(ek) == ACUTEVERTEX) { - stype = 1; - } else { - ek = getsubsegfardest(sseg); - if (pointtype(ek) == ACUTEVERTEX) { - stype = 1; sign = -1; - } else { - stype = 0; - } - } - } + if (findorg(searchtet, p2)) { + *tend = p1; + return; } } } - - // Adjust the endpoints: ei, ej. - if (sign == -1) { - sesymself(*sseg); - ei = sorg(*sseg); - ej = sdest(*sseg); - } - - if (b->verbose > 1) { - printf(" Split a type-%d seg(%d, %d) ref(%d)", stype, - pointmark(ei), pointmark(ej), pointmark(refpt)); - if (stype) { - ek = getsubsegfarorg(sseg); - printf(" ek(%d)", pointmark(ek)); + // If not, search the tet handle stored in 'p1' or 'p2'. + *tend = (point) NULL; + encodedtet1 = point2tet(p1); + encodedtet2 = point2tet(p2); + if (encodedtet1 != (tetrahedron) NULL) { + decode(encodedtet1, *searchtet); + // Be careful, here 'searchtet' may be dead. + if (findorg(searchtet, p1)) { + *tend = p2; + } + } else if (encodedtet2 != (tetrahedron) NULL) { + decode(encodedtet2, *searchtet); + // Be careful, here 'searchtet' may be dead. + if (findorg(searchtet, p2)) { + *tend = p1; } - printf(".\n"); } - - // Calculate the split point. - if (stype == 0) { - // Use rule-1. - L = DIST(ei, ej); - d1 = DIST(ei, refpt); - d2 = DIST(ej, refpt); - if (d1 < d2) { - // Choose ei as center. - if (d1 < 0.5 * L) { - split = d1 / L; - // Adjust split if it is close to middle. (2009-02-01) - if ((split > 0.4) || (split < 0.6)) split = 0.5; - } else { - split = 0.5; - } - for (i = 0; i < 3; i++) { - vt[i] = ei[i] + split * (ej[i] - ei[i]); - } - } else { - // Choose ej as center. - if (d2 < 0.5 * L) { - split = d2 / L; - // Adjust split if it is close to middle. (2009-02-01) - if ((split > 0.4) || (split < 0.6)) split = 0.5; - } else { - split = 0.5; - } - for (i = 0; i < 3; i++) { - vt[i] = ej[i] + split * (ei[i] - ej[i]); - } + // If still not, perform a full point location. The starting tet is + // chosen as follows: Use the handle stored in 'p1' or 'p2' if it is + // alive; otherwise, start from a tet on the convex hull. + if (*tend == (point) NULL) { + if (encodedtet1 != (tetrahedron) NULL) { + decode(encodedtet1, *searchtet); + // Be careful, here 'searchtet' may be dead. } - r1count++; - } else { - // Use rule-2. - ek = getsubsegfarorg(sseg); - L = DIST(ek, ej); - d = DIST(ek, refpt); - split = d / L; - for (i = 0; i < 3; i++) { - vt[i] = ek[i] + split * (ej[i] - ek[i]); - } - d1 = DIST(vt, refpt); - d2 = DIST(vt, ej); - if (d1 > d2) { - // Use rule-3. - d3 = DIST(ei, refpt); - if (d1 < 0.5 * d3) { - split = (d - d1) / L; - } else { - split = (d - 0.5 * d3) / L; + if (isdead(searchtet)) { + if (encodedtet2 != (tetrahedron) NULL) { + decode(encodedtet2, *searchtet); + // Be careful, here 'searchtet' may be dead. } - for (i = 0; i < 3; i++) { - vt[i] = ek[i] + split * (ej[i] - ek[i]); + if (isdead(searchtet)) { + searchtet->tet = dummytet; + searchtet->loc = 0; + symself(*searchtet); } +#ifdef SELF_CHECK + assert(!isdead(searchtet)); +#endif } - d1 > d2 ? r3count++ : r2count++; - } - - if (b->verbose > 1) { - printf(" split (%g), vt (%g, %g, %g).\n", split, vt[0], vt[1], vt[2]); - } -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// delaunizesegments() Recover segments in a Delaunay tetrahedralization. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::delaunizesegments2() -{ - triface searchtet; - face splitsh; - face *psseg, sseg; // *parysh; - point refpt, newpt; - enum interresult dir; - bool visflag; - - if (b->verbose) { - printf(" Delaunizing segments.\n"); - } - - // Loop until 'subsegstack' is empty. - while (subsegstack->objects > 0l) { - // seglist is used as a stack. - subsegstack->objects--; - psseg = (face *) fastlookup(subsegstack, subsegstack->objects); - sseg = *psseg; - - if (!sinfected(sseg)) continue; // Not a missing segment. - suninfect(sseg); - - // Insert the segment. - searchtet.tet = NULL; - dir = scoutsegment2(&sseg, &searchtet, &refpt); - - if (dir != SHAREEDGE) { - // The segment is missing, split it. - spivot(sseg, splitsh); - if (dir != INTERVERT) { - // Create the new point. - makepoint(&newpt); - getsegmentsplitpoint2(&sseg, refpt, newpt); - setpointtype(newpt, FREESEGVERTEX); - setpoint2sh(newpt, sencode(sseg)); - // Split the segment by newpt. - sinsertvertex(newpt, &splitsh, &sseg, true, false); - // Insert newpt into the DT. If 'checksubfaces == 1' the current - // mesh is constrained Delaunay (but may not Delaunay). - visflag = (checksubfaces == 1); - insertvertexbw(newpt, &searchtet, true, visflag, false, false); - } else { - /*if (getpointtype(refpt) != ACUTEVERTEX) { - setpointtype(refpt, RIDGEVERTEX); - } - // Split the segment by refpt. - sinsertvertex(refpt, &splitsh, &sseg, true, false);*/ - printf("Error: Invalid PLC! A point and a segment intersect.\n"); - point pa, pb; - pa = getsubsegfarorg(&sseg); - pb = getsubsegfardest(&sseg); - printf(" Point: %d. Segment: (%d, %d).\n", pointmark(refpt), - pointmark(pa), pointmark(pb)); - terminatetetgen(3); - } + if (locate(p1, searchtet) != ONVERTEX) { + printf("Internal error in getsearchtet(): Failed to locate point\n"); + internalerror(); } - } - - if (b->verbose) { - printf(" %ld protecting points.\n", r1count + r2count + r3count); + // Remember this handle in 'p1' to enhance the search speed. + setpoint2tet(p1, encode(*searchtet)); + *tend = p2; } } /////////////////////////////////////////////////////////////////////////////// // // -// scoutsubface() Look for a given subface in the tetrahedralization T. // -// // -// 'ssub' is the subface, denoted as abc. If abc exists in T, it is 'locked' // -// at the place where the two tets sharing at it. // +// isedgeencroached() Check whether or not a subsegment is encroached. // // // -// 'convexflag' indicates the current mesh is convex (1) or non-convex (0). // -// // -// The returned value indicates one of the following cases: // -// - SHAREFACE, abc exists and is inserted; // -// - TOUCHEDGE, a vertex (the origin of 'searchtet') lies on ab. // -// - EDGETRIINT, all three edges of abc are missing. // -// - ACROSSTET, a tet (in 'searchtet') crosses the facet containg abc. // -// // -// If the retunred value is ACROSSTET, the subface is missing. 'searchtet' // -// returns a tet which shares the same edge as 'pssub'. // +// A segment with endpoints 'p1' and 'p2' is encroached by the point 'testpt'// +// if it lies in the diametral sphere of this segment. The degenerate case // +// that 'testpt' lies on the sphere is treated as encroached if 'degflag' is // +// set to be TRUE. // // // /////////////////////////////////////////////////////////////////////////////// -enum tetgenmesh::interresult tetgenmesh::scoutsubface(face* pssub, - triface* searchtet, int convexflag) +bool tetgenmesh::isedgeencroached(point p1, point p2, point testpt, + bool degflag) { - triface spintet; - face checksh; - point pa, pb, pc, pd; - enum interresult dir; - int hitbdry; - int i; + REAL dotproduct; - if ((searchtet->tet == NULL) || (searchtet->tet == dummytet)) { - // Search an edge of 'ssub' in tetrahedralization. - pssub->shver = 0; - for (i = 0; i < 3; i++) { - pa = sorg(*pssub); - pb = sdest(*pssub); - // Get a tet whose origin is pa. - point2tetorg(pa, *searchtet); - // Search the edge from pa->pb. - dir = finddirection2(searchtet, pb); - if (dir == INTERVERT) { - if (dest(*searchtet) == pb) { - // Found the edge. Break the loop. - break; - } else { - // A vertex lies on the search edge. Return it. - enextself(*searchtet); - return TOUCHEDGE; - } - } else if (dir == BELOWHULL2) { - if (convexflag > 0) { - assert(0); - } - // The domain is non-convex, and we got stucked at a boundary face. - point2tetorg(pa, *searchtet); - dir = finddirection3(searchtet, pb); - if (dir == INTERVERT) { - if (dest(*searchtet) == pb) { - // Found the edge. Break the loop. - break; - } else { - // A vertex lies on the search edge. Return it. - enextself(*searchtet); - return TOUCHEDGE; - } - } - } - senextself(*pssub); - } - if (i == 3) { - // None of the three edges exists. - return EDGETRIINT; // ab intersects the face in 'searchtet'. - } + // Check if the segment is facing an angle larger than 90 degree? + dotproduct = (p1[0] - testpt[0]) * (p2[0] - testpt[0]) + + (p1[1] - testpt[1]) * (p2[1] - testpt[1]) + + (p1[2] - testpt[2]) * (p2[2] - testpt[2]); + if (dotproduct < 0) { + return true; + } else if (dotproduct == 0 && degflag) { + return true; } else { - // 'searchtet' holds the current edge of 'pssub'. - pa = org(*searchtet); - pb = dest(*searchtet); - } - - pc = sapex(*pssub); - - if (b->verbose > 1) { - printf(" Scout subface (%d, %d, %d) (%ld).\n", pointmark(pa), - pointmark(pb), pointmark(pc), subfacstack->objects); - } - - // Searchtet holds edge pa->pb. Search a face with apex pc. - spintet = *searchtet; - pd = apex(spintet); - hitbdry = 0; - while (1) { - if (pd == pc) { - // Found! Insert the subface. - tspivot(spintet, checksh); // SELF_CHECK - if (checksh.sh == dummysh) { - // Comment: here we know that spintet and pssub refer to the same - // edge and the same DIRECTION: pa->pb. - if ((spintet.ver & 1) == 1) { - // Stay in CCW edge ring. - esymself(spintet); - } - if (sorg(*pssub) != org(spintet)) { - sesymself(*pssub); - } - tsbond(spintet, *pssub); - symself(spintet); - if (spintet.tet != dummytet) { - tspivot(spintet, checksh); // SELF_CHECK - assert(checksh.sh == dummysh); // SELF_CHECK - sesymself(*pssub); - tsbond(spintet, *pssub); - } - return SHAREFACE; - } else { - *searchtet = spintet; - if (checksh.sh != pssub->sh) { - // Another subface is laready inserted. - // Comment: This is possible when there are faked tets. - return COLLISIONFACE; - } else { - // The subface has already been inserted (when you do check). - return SHAREFACE; - } - } - } - if (!fnextself(spintet)) { - hitbdry++; - if (hitbdry == 2) break; - esym(*searchtet, spintet); - if (!fnextself(spintet)) break; - } - pd = apex(spintet); - if (pd == apex(*searchtet)) break; + return false; } - - return INTERTET; } /////////////////////////////////////////////////////////////////////////////// // // -// scoutcrosstet() Scout a tetrahedron across a facet. // +// scoutrefpoint() Search the reference point of a missing segment. // // // -// A subface (abc) of the facet (F) is given in 'pssub', 'searchtet' holds // -// the edge ab, it is the tet starting the search. 'facpoints' contains all // -// points which are co-facet with a, b, and c. // +// A segment S is missing in current Delaunay tetrahedralization DT and will // +// be split by inserting a point V in it. The two end points of S are the // +// origin of 'searchtet' and 'tend'. And we know that S is crossing the face // +// of 'searchtet' opposite to its origin (may be intersecting with the edge // +// from the destination to the apex of the 'searchtet'). The search of P is // +// completed by walking through all faces of DT across by S. // // // -// The subface (abc) was produced by a 2D CDT algorithm under the Assumption // -// that F is flat. In real data, however, F may not be strictly flat. Hence // -// a tet (abde) that crosses abc may be in one of the two cases: (i) abde // -// intersects F in its interior, or (ii) abde intersects F on its boundary. // -// In case (i) F (or part of it) is missing in DT and needs to be recovered. // -// In (ii) F is not missing, the surface mesh of F needs to be adjusted. // -// // -// This routine distinguishes the two cases by the returned value, which is // -// - INTERTET, if it is case (i), 'searchtet' is abde, d and e lies below // -// and above abc, respectively, neither d nor e is dummypoint; or // -// - INTERFACE, if it is case (ii), 'searchtet' is abde, where the face // -// abd intersects abc, i.e., d is co-facet with abc, e may be co-facet // -// with abc or dummypoint. // +// Warning: This routine is correct when the tetrahedralization is Delaunay // +// and convex. Otherwise, the search loop may not terminate. // // // /////////////////////////////////////////////////////////////////////////////// -enum tetgenmesh::interresult tetgenmesh::scoutcrosstet(face *pssub, - triface* searchtet, arraypool* facpoints) +tetgenmesh::point tetgenmesh::scoutrefpoint(triface* searchtet, point tend) { - triface spintet, crossface; - point pa, pb, pc, pd, pe; - REAL ori, ori1, len, n[3]; - REAL r, dr, drmin; - bool cofacetflag; - int hitbdry; - int i; + triface checkface; + point tstart, testpt, refpoint; + REAL cent[3], radius, largest; + REAL ahead; + bool ncollinear; + int sides; - if (facpoints != NULL) { - // Infect all vertices of the facet. - for (i = 0; i < (int) facpoints->objects; i++) { - pd = * (point *) fastlookup(facpoints, i); - pinfect(pd); - } + if (b->verbose > 2) { + printf(" Scout the reference point of segment (%d, %d).\n", + pointmark(org(*searchtet)), pointmark(tend)); } - // Search an edge crossing the facet containing abc. - if (searchtet->ver & 01) { - esymself(*searchtet); // Adjust to 0th edge ring. - sesymself(*pssub); + tstart = org(*searchtet); + refpoint = (point) NULL; + largest = 0; // avoid compile warning. + + // Check the three vertices of the crossing face. + testpt = apex(*searchtet); + if (isedgeencroached(tstart, tend, testpt, true)) { + ncollinear = circumsphere(tstart, tend, testpt, NULL, cent, &radius); +#ifdef SELF_CHECK + assert(ncollinear); +#endif + refpoint = testpt; + largest = radius; } - - pa = sorg(*pssub); - pb = sdest(*pssub); - pc = sapex(*pssub); - - // 'searchtet' refers to edge pa->pb. - assert(org(*searchtet) == pa); - assert(dest(*searchtet) == pb); - - // Search an apex lies below the subface. Note that such apex may not - // exist which indicates there is a co-facet apex. - cofacetflag = false; - pd = apex(*searchtet); - spintet = *searchtet; - hitbdry = 0; - while (1) { - ori = orient3d(pa, pb, pc, pd); - if ((ori != 0) && pinfected(pd)) { - ori = 0; // Force d be co-facet with abc. - } - if (ori > 0) { - break; // Found a lower point (the apex of spintet). - } - // Go to the next face. - if (!fnextself(spintet)) { - hitbdry++; - if (hitbdry == 2) { - cofacetflag = true; break; // Not found. + testpt = dest(*searchtet); + if (isedgeencroached(tstart, tend, testpt, true)) { + ncollinear = circumsphere(tstart, tend, testpt, NULL, cent, &radius); +#ifdef SELF_CHECK + assert(ncollinear); +#endif + if (refpoint == (point) NULL) { + refpoint = testpt; + largest = radius; + } else { + if (radius > largest) { + refpoint = testpt; + largest = radius; } - esym(*searchtet, spintet); - if (!fnextself(spintet)) { - cofacetflag = true; break; // Not found. + } + } + testpt = oppo(*searchtet); + if (isedgeencroached(tstart, tend, testpt, true)) { + ncollinear = circumsphere(tstart, tend, testpt, NULL, cent, &radius); +#ifdef SELF_CHECK + assert(ncollinear); +#endif + if (refpoint == (point) NULL) { + refpoint = testpt; + largest = radius; + } else { + if (radius > largest) { + refpoint = testpt; + largest = radius; } } - pd = apex(spintet); - if (pd == apex(*searchtet)) { - cofacetflag = true; break; // Not found. + } + // Check the opposite vertex of the neighboring tet in case the segment + // crosses the edge (leftpoint, rightpoint) of the crossing face. + sym(*searchtet, checkface); + if (checkface.tet != dummytet) { + testpt = oppo(checkface); + if (isedgeencroached(tstart, tend, testpt, true)) { + ncollinear = circumsphere(tstart, tend, testpt, NULL, cent, &radius); +#ifdef SELF_CHECK + assert(ncollinear); +#endif + if (refpoint == (point) NULL) { + refpoint = testpt; + largest = radius; + } else { + if (radius > largest) { + refpoint = testpt; + largest = radius; + } + } } } - if (!cofacetflag) { - if (hitbdry > 0) { - // The edge direction is reversed, which means we have to reverse - // the face rotation direction to find the crossing edge d->e. - esymself(spintet); - } - // Keep the edge a->b be in the CCW edge ring of spintet. - if (spintet.ver & 1) { - symedgeself(spintet); - assert(spintet.tet != dummytet); - } - // Search a tet whose apex->oppo crosses the face [a, b, c]. - // -- spintet is a face [a, b, d]. - // -- the apex (d) of spintet is below [a, b, c]. - while (1) { - pe = oppo(spintet); - ori = orient3d(pa, pb, pc, pe); - if ((ori != 0) && pinfected(pe)) { - ori = 0; // Force it to be a coplanar point. - } - if (ori == 0) { - cofacetflag = true; - break; // Found a co-facet point. - } - if (ori < 0) { - *searchtet = spintet; - break; // Found. edge [d, e]. - } - // Go to the next tet. - tfnextself(spintet); - if (spintet.tet == dummytet) { - cofacetflag = true; - break; // There is a co-facet point. - } - } - // Now if "cofacetflag != true", searchtet contains a cross tet (abde), - // where d and e lie below and above abc, respectively, and - // orient3d(a, b, d, e) < 0. - } - - if (cofacetflag) { - // There are co-facet points. Calculate a point above the subface. - facenormal2(pa, pb, pc, n, 1); - len = sqrt(DOT(n, n)); - n[0] /= len; - n[1] /= len; - n[2] /= len; - len = DIST(pa, pb); - len += DIST(pb, pc); - len += DIST(pc, pa); - len /= 3.0; - dummypoint[0] = pa[0] + len * n[0]; - dummypoint[1] = pa[1] + len * n[1]; - dummypoint[2] = pa[2] + len * n[2]; - // Search a co-facet point d, s.t. (i) [a, b, d] intersects [a, b, c], - // AND (ii) a, b, c, d has the closet circumradius of [a, b, c]. - // NOTE: (ii) is needed since there may be several points satisfy (i). - // For an example, see file2.poly. - circumsphere(pa, pb, pc, NULL, n, &r); - crossface.tet = NULL; - pe = apex(*searchtet); - spintet = *searchtet; - hitbdry = 0; - while (1) { - pd = apex(spintet); - ori = orient3d(pa, pb, pc, pd); - if ((ori == 0) || pinfected(pd)) { - ori1 = orient3d(pa, pb, dummypoint, pd); - if (ori1 > 0) { - // [a, b, d] intersects with [a, b, c]. - if (pinfected(pd)) { - len = DIST(n, pd); - dr = fabs(len - r); - if (crossface.tet == NULL) { - // This is the first cross face. - crossface = spintet; - drmin = dr; - } else { - if (dr < drmin) { - crossface = spintet; - drmin = dr; - } - } + // Walk through all crossing faces. + enextfnext(*searchtet, checkface); + sym(checkface, *searchtet); + while (true) { + // Check if we are reaching the boundary of the triangulation. +#ifdef SELF_CHECK + assert(searchtet->tet != dummytet); +#endif + // Search for an adjoining tetrahedron we can walk through. + searchtet->ver = 0; + // 'testpt' is the shared vertex for the following orientation tests. + testpt = oppo(*searchtet); + if (testpt == tend) { + // The searching is finished. + break; + } else { + // 'testpt' may encroach the segment. + if ((testpt != tstart) && (testpt != refpoint)) { + if (isedgeencroached(tstart, tend, testpt, true)) { + ncollinear = circumsphere(tstart, tend, testpt, NULL, cent, &radius); + if (!ncollinear) { + // 'testpt' is collinear with the segment. It may happen when a + // set of collinear and continuous segments is defined by two + // extreme endpoints. In this case, we should choose 'testpt' + // as the splitting point immediately. No new point should be + // created. + refpoint = testpt; + break; + } + if (refpoint == (point) NULL) { + refpoint = testpt; + largest = radius; } else { - assert(ori == 0); // SELF_CHECK - // Found a coplanar but not co-facet point (pd). - printf("Error: Invalid PLC! A point and a subface intersect\n"); - // get_origin_facet_corners(pssub, &pa, &pb, &pc); - printf(" Point %d. Subface (#%d) (%d, %d, %d)\n", - pointmark(pd), shellmark(*pssub), pointmark(pa), pointmark(pb), - pointmark(pc)); - terminatetetgen(3); + if (radius > largest) { + refpoint = testpt; + largest = radius; + } } } } - // Go to the next face. - if (!fnextself(spintet)) { - hitbdry++; - if (hitbdry == 2) break; - esym(*searchtet, spintet); - if (!fnextself(spintet)) break; - } - if (apex(spintet) == pe) { - break; - } - } - if(crossface.tet == NULL) { - assert(crossface.tet != NULL); // Not handled yet. } - *searchtet = crossface; - dummypoint[0] = dummypoint[1] = dummypoint[2] = 0; - } - - if (cofacetflag) { - if (b->verbose > 1) { - printf(" Found a co-facet face (%d, %d, %d) op (%d).\n", - pointmark(pa), pointmark(pb), pointmark(apex(*searchtet)), - pointmark(oppo(*searchtet))); - } - if (facpoints != NULL) { - // Unmark all facet vertices. - for (i = 0; i < (int) facpoints->objects; i++) { - pd = * (point *) fastlookup(facpoints, i); - puninfect(pd); - } - } - // Comment: Now no vertex is infected. - /*if (getpointtype(apex(*searchtet)) == VOLVERTEX) { - // A vertex lies on the facet. - enext2self(*searchtet); // org(*searchtet) == pd - return TOUCHFACE; - }*/ - return INTERFACE; - } else { - // Return a crossing tet. - if (b->verbose > 1) { - printf(" Found a crossing tet (%d, %d, %d, %d).\n", pointmark(pa), - pointmark(pb), pointmark(apex(*searchtet)), pointmark(pe)); - } - // Comment: if facpoints != NULL, co-facet vertices are stll infected. - // They will be uninfected in formcavity(); - return INTERTET; // abc intersects the volume of 'searchtet'. - } -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// recoversubfacebyflips() Recover a subface by flips in the surface mesh. // -// // -// A subface [a, b, c] ('pssub') intersects with a face [a, b, d] ('cross- // -// face'), where a, b, c, and d belong to the same facet. It indicates that // -// the face [a, b, d] should appear in the surface mesh. // -// // -// This routine recovers [a, b, d] in the surface mesh through a sequence of // -// 2-to-2 flips. No Steiner points is needed. 'pssub' returns [a, b, d]. // -// // -// If 'facfaces' is not NULL, all flipped subfaces are queued for recovery. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::recoversubfacebyflips(face* pssub, triface* crossface, - arraypool *facfaces) -{ - triface neightet; - face flipfaces[2], *parysh; - face checkseg; - point pa, pb, pc, pd, pe; - REAL ori, len, n[3]; - - // Get the missing subface is [a, b, c]. - pa = sorg(*pssub); - pb = sdest(*pssub); - pc = sapex(*pssub); - - // The crossface is [a, b, d, e]. - // assert(org(*crossface) == pa); - // assert(dest(*crossface) == pb); - pd = apex(*crossface); - pe = dummypoint; // oppo(*crossface); - - if (pe == dummypoint) { - // Calculate a point above the faces. - facenormal2(pa, pb, pd, n, 1); - len = sqrt(DOT(n, n)); - n[0] /= len; - n[1] /= len; - n[2] /= len; - len = DIST(pa, pb); - len += DIST(pb, pd); - len += DIST(pd, pa); - len /= 3.0; - pe[0] = pa[0] + len * n[0]; - pe[1] = pa[1] + len * n[1]; - pe[2] = pa[2] + len * n[2]; - } - - // Adjust face [a, b, c], so that edge [b, c] crosses edge [a, d]. - ori = orient3d(pb, pc, pe, pd); - assert(ori != 0); // SELF_CHECK - - if (ori > 0) { - // Swap a and b. - sesymself(*pssub); - esymself(*crossface); // symedgeself(*crossface); - pa = sorg(*pssub); - pb = sdest(*pssub); - if (pe == dummypoint) { - pe[0] = pe[1] = pe[2] = 0; - } - pe = dummypoint; // oppo(*crossface); - } - - while (1) { - - // Flip edge [b, c] to edge [a, d]. - senext(*pssub, flipfaces[0]); - sspivot(flipfaces[0], checkseg); // SELF_CHECK - assert(checkseg.sh == dummysh); // SELF_CHECK - spivot(flipfaces[0], flipfaces[1]); - - stpivot(flipfaces[1], neightet); - if (neightet.tet != dummytet) { - // A recovered subface, clean sub<==>tet connections. - tsdissolve(neightet); - symself(neightet); - tsdissolve(neightet); - stdissolve(flipfaces[1]); - sesymself(flipfaces[1]); - stdissolve(flipfaces[1]); - sesymself(flipfaces[1]); - // flipfaces[1] refers to edge [b, c] (either b->c or c->b). - } - - flip22sub(&(flipfaces[0]), NULL); - flip22count++; - - // Comment: now flipfaces[0] is [d, a, b], flipfaces[1] is [a, d, c]. - - // Add them into list (make ensure that they must be recovered). - facfaces->newindex((void **) &parysh); - *parysh = flipfaces[0]; - facfaces->newindex((void **) &parysh); - *parysh = flipfaces[1]; - - // Find the edge [a, b]. - senext(flipfaces[0], *pssub); - assert(sorg(*pssub) == pa); // SELF_CHECK - assert(sdest(*pssub) == pb); // SELF_CHECK - - pc = sapex(*pssub); - if (pc == pd) break; - - if (pe == dummypoint) { - // Calculate a point above the faces. - facenormal2(pa, pb, pd, n, 1); - len = sqrt(DOT(n, n)); - n[0] /= len; - n[1] /= len; - n[2] /= len; - len = DIST(pa, pb); - len += DIST(pb, pd); - len += DIST(pd, pa); - len /= 3.0; - pe[0] = pa[0] + len * n[0]; - pe[1] = pa[1] + len * n[1]; - pe[2] = pa[2] + len * n[2]; - } - - while (1) { - ori = orient3d(pb, pc, pe, pd); - assert(ori != 0); // SELF_CHECK - if (ori > 0) { - senext2self(*pssub); - spivotself(*pssub); - if (sorg(*pssub) != pa) sesymself(*pssub); - pb = sdest(*pssub); - pc = sapex(*pssub); - continue; + // Check three side-faces of 'searchtet' to find the one through + // which we can walk next. + for (sides = 0; sides < 3; sides++) { + fnext(*searchtet, checkface); + ahead = orient3d(org(checkface), dest(checkface), testpt, tend); + if (ahead < 0.0) { + // We can walk through this face and continue the searching. + sym(checkface, *searchtet); + break; } - break; + enextself(*searchtet); } +#ifdef SELF_CHECK + assert (sides < 3); +#endif } - if (pe == dummypoint) { - pe[0] = pe[1] = pe[2] = 0; - } +#ifdef SELF_CHECK + assert(refpoint != (point) NULL); +#endif + return refpoint; } /////////////////////////////////////////////////////////////////////////////// // // -// formcavity() Form the cavity of a missing region. // -// // -// A missing region R is a set of co-facet (co-palanr) subfaces. 'pssub' is // -// a missing subface [a, b, c]. 'crosstets' contains only one tet, [a, b, d, // -// e], where d and e lie below and above [a, b, c], respectively. Other // -// crossing tets are sought from this tet and saved in 'crosstets'. // -// // -// The cavity C is divided into two parts by R,one at top and one at bottom. // -// 'topfaces' and 'botfaces' return the upper and lower boundary faces of C. // -// 'toppoints' contains vertices of 'crosstets' 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). // +// getsegmentorigin() Return the origin of the (unsplit) segment. // // // -// A pair of boundary faces: 'firsttopface' and 'firstbotface', are saved. // -// They share the same edge in the boundary of the missing region. // -// // -// 'facpoints' contains all vertices of the facet containing R. They are // -// used for searching the crossing tets. On input all vertices are infected. // -// They are uninfected after the cavity is formed. // +// After a segment (or a subsegment) is split. Two resulting subsegments are // +// connecting each other through the pointers saved in their data fields. // +// With these pointers, the whole (unsplit) segment can be found. 'splitseg' // +// may be a split subsegment. Returns the origin of the unsplit segment. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::formcavity(face *pssub, arraypool* crosstets, - arraypool* topfaces, arraypool* botfaces, arraypool* toppoints, - arraypool* botpoints, arraypool* facpoints, arraypool* facfaces) +tetgenmesh::point tetgenmesh::getsegmentorigin(face* splitseg) { - arraypool *crossedges; - triface *parytet, crosstet, spintet, neightet, faketet; - face neighsh, checksh, *parysh; - face checkseg; - point pa, pb, pc, pf, pg; - point pd, pe; - point *ppt; - // REAL ori; - int i, j; + face workseg; + point farorg; - // For triangle-edge test. - enum interresult dir; - int types[2], poss[4]; - - // Get the missing subface abc. - pa = sorg(*pssub); - pb = sdest(*pssub); - pc = sapex(*pssub); - - // Comment: Now all facet vertices are infected. - - // Get a crossing tet abde. - parytet = (triface *) fastlookup(crosstets, 0); // face abd. - // The edge de crosses the facet. d lies below abc. - enext2fnext(*parytet, crosstet); - enext2self(crosstet); - esymself(crosstet); // the edge d->e at face [d,e,a] - infect(crosstet); - *parytet = crosstet; // Save it in list. - - // Temporarily re-use 'topfaces' for storing crossing edges. - crossedges = topfaces; - crossedges->newindex((void **) &parytet); - *parytet = crosstet; - - // Collect all crossing tets. Each cross tet is saved in the standard - // form deab, where de is a corrsing edge, orient3d(d,e,a,b) < 0. - // 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 < (int) crossedges->objects; i++) { - crosstet = * (triface *) fastlookup(crossedges, i); - // It may already be tested. - if (!edgemarked(crosstet)) { - // Collect all tets sharing at the edge. - pg = apex(crosstet); - spintet = crosstet; - while (1) { - // Mark this edge as tested. - markedge(spintet); - if (!infected(spintet)) { - infect(spintet); - crosstets->newindex((void **) &parytet); - *parytet = spintet; - } - // Go to the neighbor tet. - tfnextself(spintet); - if (spintet.tet != dummytet) { - // Check the validity of the PLC. - tspivot(spintet, checksh); - if (checksh.sh != dummysh) { - printf("Error: Invalid PLC! Two subfaces intersect.\n"); - printf(" 1st (#%4d): (%d, %d, %d)\n", shellmark(*pssub), - pointmark(pa), pointmark(pb), pointmark(pc)); - printf(" 2nd (#%4d): (%d, %d, %d)\n", shellmark(checksh), - pointmark(sorg(checksh)), pointmark(sdest(checksh)), - pointmark(sapex(checksh))); - terminatetetgen(3); - } - } else { - // Encounter a boundary face. - assert(0); // Not handled yet. - } - if (apex(spintet) == pg) break; - } - // Detect new cross edges. - // Comment: A crossing edge must intersect one missing subface of - // this facet. We do edge-face tests. - pd = org(spintet); - pe = dest(spintet); - while (1) { - // Remember: spintet is edge d->e, d lies below [a, b, c]. - pf = apex(spintet); - // if (pf != dummypoint) { // Do not grab a hull edge. - if (!pinfected(pf)) { - for (j = 0; j < (int) facfaces->objects; j++) { - parysh = (face *) fastlookup(facfaces, j); - pa = sorg(*parysh); - pb = sdest(*parysh); - pc = sapex(*parysh); - // Check if pd->pf crosses the facet. - if (tri_edge_test(pa, pb, pc, pd, pf, NULL, 1, types, poss)) { - dir = (enum interresult) types[0]; - if ((dir == INTEREDGE) || (dir == INTERFACE)) { - // The edge d->f corsses the facet. - enext2fnext(spintet, neightet); - esymself(neightet); // d->f. - // pd must lie below the subface. - break; - } - } - // Check if pe->pf crosses the facet. - if (tri_edge_test(pa, pb, pc, pe, pf, NULL, 1, types, poss)) { - dir = (enum interresult) types[0]; - if ((dir == INTEREDGE) || (dir == INTERFACE)) { - // The edge f->e crosses the face. - enextfnext(spintet, neightet); - esymself(neightet); // f->e. - // pf must lie below the subface. - break; - } - } - } - // There must exist a crossing edge. - assert(j < (int) facfaces->objects); - /*// There exist a crossing edge, either d->f, or f->e. - ori = orient3d(pa, pb, pc, pf); - if (ori == 0) { - printf("Error: Invalid PLC! Point and subface intersect.\n"); - printf(" Point %d, subface (#%4d): (%d, %d, %d)\n", - pointmark(pf), shellmark(*pssub), pointmark(pa), - pointmark(pb), pointmark(pc)); - terminatetetgen(3); - } - if (ori < 0) { - // The edge d->f corsses the facet. - enext2fnext(spintet, neightet); - esymself(neightet); // d->f. - } else { - // The edge f->e crosses the face. - enextfnext(spintet, neightet); - esymself(neightet); // f->e. - } - */ - if (!edgemarked(neightet)) { - // Add a new cross edge. - crossedges->newindex((void **) &parytet); - *parytet = neightet; - } - } - // } - tfnextself(spintet); - if (spintet.tet == dummytet) { - // Encounter a boundary face. - assert(0); // Not handled yet. + farorg = sorg(*splitseg); + if ((pointtype(farorg) != ACUTEVERTEX) && + (pointtype(farorg) != NACUTEVERTEX)) { + workseg = *splitseg; + do { + senext2self(workseg); + spivotself(workseg); + if (workseg.sh != dummysh) { + workseg.shver = 0; // It's a subsegment. + if (sdest(workseg) != farorg) { + sesymself(workseg); +#ifdef SELF_CHECK + assert(sdest(workseg) == farorg); +#endif } - if (apex(spintet) == pg) break; + farorg = sorg(workseg); + if ((pointtype(farorg) == ACUTEVERTEX) || + (pointtype(farorg) == NACUTEVERTEX)) break; } - } + } while (workseg.sh != dummysh); } +#ifdef SELF_CHECK + assert((pointtype(farorg) == ACUTEVERTEX) || + (pointtype(farorg) == NACUTEVERTEX)); +#endif + return farorg; +} - // Unmark all facet vertices. - for (i = 0; i < (int) facpoints->objects; i++) { - ppt = (point *) fastlookup(facpoints, i); - puninfect(*ppt); - } +/////////////////////////////////////////////////////////////////////////////// +// // +// getsplitpoint() Get a point for splitting a segment. // +// // +// 'splitseg' is the segment will be split. 'refpoint' is a reference point // +// for splitting this segment. Moreover, it should not collinear with the // +// splitting segment. (The collinear case will be detected by iscollinear() // +// before entering this routine.) The calculation of the splitting point is // +// governed by three rules introduced in my paper. // +// // +// After the position is calculated, a new point is created at this location.// +// The new point has one of the two pointtypes: FREESEGVERTEX indicating it // +// is an inserting vertex on segment, and NACUTEVERTEX indicating it is an // +// endpoint of a segment which original has type-3 now becomes type-2. // +// // +/////////////////////////////////////////////////////////////////////////////// - // Comments: Now no vertex is marked. Next we will mark vertices which - // belong to the top and bottom boundary faces of the cavity and put - // them in 'toppopints' and 'botpoints', respectively. +tetgenmesh::point tetgenmesh::getsplitpoint(face* splitseg, point refpoint) +{ + point splitpoint; + point farorg, fardest; + point ei, ej, ek, c; + REAL v[3], r, split; + REAL d1, d2, ps, rs; + bool acuteorg, acutedest; + int stype, rule; + int i; - // All cross tets are found. Unmark cross edges. - for (i = 0; i < (int) crossedges->objects; i++) { - crosstet = * (triface *) fastlookup(crossedges, i); - if (edgemarked(crosstet)) { - // Add the vertices of the cross edge [d, e] in lists. It must be - // that d lies below the facet (i.e., its a bottom vertex). - // Note that a cross edge contains no dummypoint. - pf = org(crosstet); - // assert(pf != dummypoint); // SELF_CHECK - if (!pinfected(pf)) { - pinfect(pf); - botpoints->newindex((void **) &ppt); // Add a bottom vertex. - *ppt = pf; - } - pf = dest(crosstet); - // assert(pf != dummypoint); // SELF_CHECK - if (!pinfected(pf)) { - pinfect(pf); - toppoints->newindex((void **) &ppt); // Add a top vertex. - *ppt = pf; - } - // Unmark this edge in all tets containing it. - pg = apex(crosstet); - spintet = crosstet; - while (1) { - assert(edgemarked(spintet)); // SELF_CHECK - unmarkedge(spintet); - tfnextself(spintet); // Go to the neighbor tet. - if (spintet.tet == dummytet) { - assert(0); // Not handled yet. - } - if (apex(spintet) == pg) break; - } - } - } + // First determine the type of the segment (type-1, type-2, or type-3). + farorg = getsegmentorigin(splitseg); + acuteorg = (pointtype(farorg) == ACUTEVERTEX); + sesymself(*splitseg); + fardest = getsegmentorigin(splitseg); + acutedest = (pointtype(fardest) == ACUTEVERTEX); + sesymself(*splitseg); - if (b->verbose > 1) { - printf(" Formed cavity: %ld (%ld) cross tets (edges).\n", - crosstets->objects, crossedges->objects); - } - crossedges->restart(); - - // 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 < (int) crosstets->objects; i++) { - crosstet = * (triface *) fastlookup(crosstets, i); - enextfnext(crosstet, spintet); - enextself(spintet); - symedge(spintet, neightet); - // if (!infected(neightet)) { - if ((neightet.tet == dummytet) || !infected(neightet)) { - // A top face. - if (neightet.tet == dummytet) { - // Create a fake tet to hold the boundary face. - maketetrahedron(&faketet); // Create a faked tet. - setorg(faketet, org(spintet)); - setdest(faketet, dest(spintet)); - setapex(faketet, apex(spintet)); - setoppo(faketet, dummypoint); - bond(faketet, spintet); - tspivot(spintet, checksh); - if (checksh.sh != dummysh) { - sesymself(checksh); - tsbond(faketet, checksh); - } - for (j = 0; j < 3; j++) { // Bond segments. - tsspivot1(spintet, checkseg); - if (checkseg.sh != dummysh) { - tssbond1(faketet, checkseg); - } - enextself(spintet); - enextself(faketet); - } - firsttopface = faketet; - } else { - firsttopface = neightet; - } + ek = (point) NULL; // avoid a compilation warning. + + if (acuteorg) { + if (acutedest) { + stype = 3; } else { - continue; // Go to the next cross tet. - } - enext2fnext(crosstet, spintet); - enext2self(spintet); - symedge(spintet, neightet); - // if (!infected(neightet)) { - if ((neightet.tet == dummytet) || !infected(neightet)) { - // A bottom face. - if (neightet.tet == dummytet) { - // Create a fake tet to hold the boundary face. - maketetrahedron(&faketet); // Create a faked tet. - setorg(faketet, org(spintet)); - setdest(faketet, dest(spintet)); - setapex(faketet, apex(spintet)); - setoppo(faketet, dummypoint); - bond(spintet, faketet); - tspivot(spintet, checksh); - if (checksh.sh != dummysh) { - sesymself(checksh); - tsbond(faketet, checksh); - } - for (j = 0; j < 3; j++) { // Bond segments. - tsspivot1(spintet, checkseg); - if (checkseg.sh != dummysh) { - tssbond1(faketet, checkseg); - } - enextself(spintet); - enextself(faketet); - } - firstbotface = faketet; - } else { - firstbotface = neightet; - } + stype = 2; + ek = farorg; + } + } else { + if (acutedest) { + stype = 2; + // Adjust splitseg, so that its origin is acute. + sesymself(*splitseg); + ek = fardest; } else { - continue; + stype = 1; } - break; } - assert(i < (int) crosstets->objects); // SELF_CHECK - - // Collect the top and bottom faces and the middle vertices. Since all top - // and bottom vertices have been marked in above. Unmarked vertices are - // middle vertices. - // NOTE 1: Hull tets may be collected. Process them as normal one. - // (see fig/dump-cavity-case2.lua.) - // NOTE 2: Some previously recovered subfaces may be completely - // contained in a cavity (see fig/dump-cavity-case6.lua). In such case, - // we create two faked tets to hold this subface, one at each side. - // The faked tets will be removed in fillcavity(). - for (i = 0; i < (int) crosstets->objects; i++) { - crosstet = * (triface *) fastlookup(crosstets, i); - enextfnext(crosstet, spintet); - enextself(spintet); - symedge(spintet, neightet); - // if (!infected(neightet)) { - if ((neightet.tet == dummytet) || !infected(neightet)) { - // A top face. - topfaces->newindex((void **) &parytet); - if (neightet.tet == dummytet) { - // Create a fake tet to hold the boundary face. - maketetrahedron(&faketet); // Create a faked tet. - setorg(faketet, org(spintet)); - setdest(faketet, dest(spintet)); - setapex(faketet, apex(spintet)); - setoppo(faketet, dummypoint); - bond(spintet, faketet); - tspivot(spintet, checksh); - if (checksh.sh != dummysh) { - sesymself(checksh); - tsbond(faketet, checksh); - } - for (j = 0; j < 3; j++) { // Bond segments. - tsspivot1(spintet, checkseg); - if (checkseg.sh != dummysh) { - tssbond1(faketet, checkseg); - } - enextself(spintet); - enextself(faketet); - } - *parytet = faketet; - } else { - *parytet = neightet; - } + ei = sorg(*splitseg); + ej = sdest(*splitseg); + + if (b->verbose > 1) { + printf(" Splitting segment (%d, %d) type-%d with refpoint %d.\n", + pointmark(ei), pointmark(ej), stype, pointmark(refpoint)); + } + + if (stype == 1 || stype == 3) { + // Use rule-1. + REAL eij, eip, ejp; + eij = distance(ei, ej); + eip = distance(ei, refpoint); + ejp = distance(ej, refpoint); + if ((eip < ejp) && (eip < 0.5 * eij)) { + c = ei; + r = eip; + } else if ((eip > ejp) && (ejp < 0.5 * eij)) { + c = ej; + ej = ei; + r = ejp; } else { - if ((neightet.tet != dummytet) && infected(neightet)) { - // Check if this side is a subface. - tspivot(spintet, neighsh); - if (neighsh.sh != dummysh) { - // Found a subface (inside the cavity)! - maketetrahedron(&faketet); // Create a faked tet. - setorg(faketet, org(spintet)); - setdest(faketet, dest(spintet)); - setapex(faketet, apex(spintet)); - setoppo(faketet, dummypoint); - marktest(faketet); // To distinguish it from other faked tets. - sesymself(neighsh); - tsbond(faketet, neighsh); // Let it hold the subface. - for (j = 0; j < 3; j++) { // Bond segments. - tsspivot1(spintet, checkseg); - if (checkseg.sh != dummysh) { - tssbond1(faketet, checkseg); - } - enextself(spintet); - enextself(faketet); - } - // Add a top face (at faked tet). - topfaces->newindex((void **) &parytet); - *parytet = faketet; - } - } + c = ei; + r = 0.5 * eij; } - enext2fnext(crosstet, spintet); - enext2self(spintet); - symedge(spintet, neightet); - // if (!infected(neightet)) { - if ((neightet.tet == dummytet) || !infected(neightet)) { - // A bottom face. - botfaces->newindex((void **) &parytet); - if (neightet.tet == dummytet) { - // Create a fake tet to hold the boundary face. - maketetrahedron(&faketet); // Create a faked tet. - setorg(faketet, org(spintet)); - setdest(faketet, dest(spintet)); - setapex(faketet, apex(spintet)); - setoppo(faketet, dummypoint); - bond(spintet, faketet); - tspivot(spintet, checksh); - if (checksh.sh != dummysh) { - sesymself(checksh); - tsbond(faketet, checksh); - } - for (j = 0; j < 3; j++) { // Bond segments. - tsspivot1(spintet, checkseg); - if (checkseg.sh != dummysh) { - tssbond1(faketet, checkseg); - } - enextself(spintet); - enextself(faketet); - } - *parytet = faketet; + split = r / eij; + for (i = 0; i < 3; i++) { + v[i] = c[i] + split * (ej[i] - c[i]); + } + rule = 1; + } else { + // Use rule-2 or rule-3. + REAL eki, ekj, ekp, evj, evp, eiv; + c = ek; + eki = distance(ek, ei); // eki may equal zero. + ekj = distance(ek, ej); + ekp = distance(ek, refpoint); + // Calculate v (the going to split position between ei, ej). + r = ekp; + // Check the validity of the position. + if (!(eki < r && r < ekj)) { + printf("Error: Invalid PLC.\n"); + printf(" Hint: Use -d switch to check it.\n"); + terminatetetgen(1); + } + split = r / ekj; + for (i = 0; i < 3; i++) { + v[i] = c[i] + split * (ej[i] - c[i]); + } + rule = 2; + evj = ekj - r; // distance(v, ej); + evp = distance(v, refpoint); + if (evj < evp) { + // v is rejected, use rule-3. + eiv = distance(ei, v); + if (evp <= 0.5 * eiv) { + r = eki + eiv - evp; } else { - *parytet = neightet; + r = eki + 0.5 * eiv; } - } else { - if ((neightet.tet != dummytet) && infected(neightet)) { - tspivot(spintet, neighsh); - if (neighsh.sh != dummysh) { - // Found a subface (inside the cavity)! - maketetrahedron(&faketet); // Create a faked tet. - setorg(faketet, org(spintet)); - setdest(faketet, dest(spintet)); - setapex(faketet, apex(spintet)); - setoppo(faketet, dummypoint); - marktest(faketet); // To distinguish it from other faked tets. - sesymself(neighsh); - tsbond(faketet, neighsh); // Let it hold the subface. - for (j = 0; j < 3; j++) { // Bond segments. - tsspivot1(spintet, checkseg); - if (checkseg.sh != dummysh) { - tssbond1(faketet, checkseg); - } - enextself(spintet); - enextself(faketet); - } - // Add a bottom face (at faked tet). - botfaces->newindex((void **) &parytet); - *parytet = faketet; - } +#ifdef SELF_CHECK + assert(eki < r && r < ekj); +#endif + split = r / ekj; + for (i = 0; i < 3; i++) { + v[i] = c[i] + split * (ej[i] - c[i]); } + if (b->verbose > 1) { + printf(" Using rule-3.\n"); + } + rule = 3; } - // Add middle vertices if there are (skip dummypoint). - pf = org(spintet); - if (!pinfected(pf)) { - // if (pf != dummypoint) { - pinfect(pf); - botpoints->newindex((void **) &ppt); // Add a bottom vertex. - *ppt = pf; - toppoints->newindex((void **) &ppt); // Add a top vertex. - *ppt = pf; - // } - } - pf = dest(spintet); - if (!pinfected(pf)) { - // if (pf != dummypoint) { - pinfect(pf); - botpoints->newindex((void **) &ppt); // Add a bottom vertex. - *ppt = pf; - toppoints->newindex((void **) &ppt); // Add a top vertex. - *ppt = pf; - // } + } + + // Accumulate the corresponding counters. + if (rule == 1) r1count++; + else if (rule == 2) r2count++; + else if (rule == 3) r3count++; + + if (b->verbose > 1) { + if (stype == 2) { + printf(" Split = %.12g.\n", distance(ei, v) / distance(ei, ej)); + } else { + printf(" Split = %.12g.\n", distance(c, v) / distance(c, ej)); } } - // Unmark all collected top, bottom, and middle vertices. - for (i = 0; i < (int) toppoints->objects; i++) { - ppt = (point *) fastlookup(toppoints, i); - puninfect(*ppt); + // Create the newpoint. + makepoint(&splitpoint); + // Add a random perturbation on splitpoint. + d1 = distance(c, v); + d2 = distance(refpoint, v); + if (stype == 1 || stype == 3) { + ps = randgenerator(d1 * 1.0e-3); + } else { + // For type-2 segment, add a smaller perturbation. + // ps = randgenerator(d1 * 1.0e-5); + // REAL d2 = distance(refpoint, v); + ps = randgenerator(d2 * 1.0e-5); } - for (i = 0; i < (int) botpoints->objects; i++) { - ppt = (point *) fastlookup(botpoints, i); - puninfect(*ppt); + rs = ps / d1; + // Perturb splitpoint away from c. + for (i = 0; i < 3; i++) { + splitpoint[i] = c[i] + (1.0 + rs) * (v[i] - c[i]); + } + // for (i = 0; i < in->numberofpointattributes; i++) { + // splitpoint[i + 3] = c[i + 3] + (split + rs) * (ej[i + 3] - c[i + 3]); + // } + if (stype == 3) { + // Change a type-3 segment into two type-2 segments. + setpointtype(splitpoint, NACUTEVERTEX); + } else { + // Set it's type be FREESEGVERTEX. + setpointtype(splitpoint, FREESEGVERTEX); } - // Comments: Now no vertex is marked. + setpoint2sh(splitpoint, sencode(*splitseg)); + + return splitpoint; } /////////////////////////////////////////////////////////////////////////////// // // -// delaunizecavity() Fill a cavity by Delaunay tetrahedra. // -// // -// The tetrahedralizing cavity is the half (top or bottom part) of the whole // -// cavity. The boundary faces of the half cavity are given in 'cavfaces', // -// the bounday faces of the internal facet are not given. These faces will // -// be recovered later in fillcavity(). // -// // -// This routine first constructs the DT of the vertices by the Bowyer-Watson // -// algorithm. Then it identifies the boundary faces of the cavity in DT. // -// The DT is returned in 'newtets'. // +// insertsegment() Insert segment into DT. Queue it if it does not exist. // // // /////////////////////////////////////////////////////////////////////////////// -bool tetgenmesh::delaunizecavity(arraypool *cavpoints, arraypool *cavfaces, - arraypool *cavshells, arraypool *newtets, arraypool *crosstets, - arraypool *misfaces) +bool tetgenmesh::insertsegment(face *insseg, list *misseglist) { - triface *parytet, searchtet, neightet, spintet, *parytet1; - triface newtet, faketet; - face checksh, tmpsh, *parysh; - face checkseg; - point pa, pb, pc, pd, pt[3], *parypt; - // badface *newflipface; - enum interresult dir; - REAL ori; - // int miscount; - int i, j, k; + badface *misseg; + triface searchtet, spintet; + point tend, checkpoint; + point p1, p2; + enum finddirectionresult collinear; + int hitbdry; - if (b->verbose > 1) { - printf(" Delaunizing cavity: %ld points, %ld faces.\n", - cavpoints->objects, cavfaces->objects); - } - - // Get four non-coplanar points (no dummypoint). - parytet = (triface *) fastlookup(cavfaces, 0); - pa = org(*parytet); - pb = dest(*parytet); - pc = apex(*parytet); - pinfect(pa); - pinfect(pb); - pinfect(pc); - pd = NULL; - for (i = 1; i < (int) 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; + // Search segment ab in DT. + p1 = (point) insseg->sh[3]; + p2 = (point) insseg->sh[4]; + getsearchtet(p1, p2, &searchtet, &tend); + collinear = finddirection(&searchtet, tend, tetrahedrons->items); + if (collinear == LEFTCOLLINEAR) { + checkpoint = apex(searchtet); + enext2self(searchtet); + esymself(searchtet); + } else if (collinear == RIGHTCOLLINEAR) { + checkpoint = dest(searchtet); + } else if (collinear == TOPCOLLINEAR) { + checkpoint = oppo(searchtet); + fnextself(searchtet); + enext2self(searchtet); + esymself(searchtet); + } else { + // assert(collinear == ACROSSFACE || collinear == ACROSSEDGE); + checkpoint = (point) NULL; + } + if (checkpoint == tend) { + // Segment exist. Bond it to all tets containing it. + hitbdry = 0; + adjustedgering(searchtet, CCW); + fnextself(searchtet); + spintet = searchtet; + do { + tssbond1(spintet, *insseg); + if (!fnextself(spintet)) { + hitbdry++; + if (hitbdry < 2) { + esym(searchtet, spintet); + if (!fnextself(spintet)) { + hitbdry++; } } - // } + } + } while ((apex(spintet) != apex(searchtet)) && (hitbdry < 2)); + return true; + } else { + // Segment is missing. + if (misseglist != (list *) NULL) { + if (b->verbose > 2) { + printf(" Queuing missing segment (%d, %d).\n", pointmark(p1), + pointmark(p2)); + } + misseg = (badface *) misseglist->append(NULL); + misseg->ss = *insseg; + misseg->forg = p1; + misseg->fdest = p2; + misseg->foppo = (point) NULL; // Not used. + // setshell2badface(misseg->ss, misseg); } - if (pd != NULL) break; + return false; } - assert(i < (int) cavfaces->objects); // SELF_CHECK - pinfect(pd); +} - // Create an init DT. - // initialDT(pa, pb, pc, pd); - // Create the initial tet. - maketetrahedron(&newtet); - if (b->verbose > 2) { - printf(" Create the first tet (%d, %d, %d, %d).\n", - pointmark(pa), pointmark(pb), pointmark(pc), pointmark(pd)); - } - setorg(newtet, pa); - setdest(newtet, pb); - setapex(newtet, pc); - setoppo(newtet, pd); - // Update the point-to-tet map. - setpoint2tet(pa, encode(newtet)); - setpoint2tet(pb, encode(newtet)); - setpoint2tet(pc, encode(newtet)); - setpoint2tet(pd, encode(newtet)); - // Bond to 'dummytet' for point location. - dummytet[0] = encode(newtet); - recenttet = newtet; - // At init, all faces of this tet are hull faces. - hullsize = 4; +/////////////////////////////////////////////////////////////////////////////// +// // +// tallmissegs() Find and queue all missing segments in DT. // +// // +/////////////////////////////////////////////////////////////////////////////// - for (i = 0; i < (int) cavpoints->objects; i++) { - pt[0] = * (point *) fastlookup(cavpoints, i); - assert(pt[0] != dummypoint); // SELF_CHECK - if (!pinfected(pt[0])) { - searchtet = recenttet; - insertvertexbw(pt[0], &searchtet, true, false, false, false); - } else { - puninfect(pt[0]); // It is already inserted. - } +void tetgenmesh::tallmissegs(list *misseglist) +{ + face segloop; + + if (b->verbose) { + printf(" Queuing missing segments.\n"); } - // Comment: All vertices of the cavity are NOT marked. - while (1) { + subsegs->traversalinit(); + segloop.sh = shellfacetraverse(subsegs); + while (segloop.sh != (shellface *) NULL) { + insertsegment(&segloop, misseglist); + segloop.sh = shellfacetraverse(subsegs); + } +} - // Identify boundary faces. Remember interior tets. Save missing faces. - // For each identified boundary face in the new DT, we insert a subface - // temporarily at that place. The subface also contains a pointer to - // the adjacent tet outside of the cavity. We save the temp subface - // with its side facing to the interior of the cavity. - for (i = 0; i < (int) cavfaces->objects; i++) { - parytet = (triface *) fastlookup(cavfaces, i); - // Skip an interior face (due to the enlargement of the cavity). - if (infected(*parytet)) continue; - // Choose the CCW edge ring. - parytet->ver = 4; - pt[0] = org(*parytet); - pt[1] = dest(*parytet); - pt[2] = apex(*parytet); - // Create a temp subface. - makeshellface(subfaces, &tmpsh); - // setshvertices(tmpsh, pt[0], pt[1], pt[2]); - setsorg(tmpsh, pt[0]); - setsdest(tmpsh, pt[1]); - setsapex(tmpsh, pt[2]); - // Comment: This side of tmpsh faces to the outside of the cavity. - // Insert tmpsh in DT. - searchtet.tet = NULL; - dir = scoutsubface(&tmpsh, &searchtet, 1); - if (dir == SHAREFACE) { - // Let tmpsh face to the interior tet of the cavity. - if (sorg(tmpsh) == pt[0]) { - sesymself(tmpsh); - } - assert(sorg(tmpsh) == pt[1]); - assert(sdest(tmpsh) == pt[0]); - } else if (dir == COLLISIONFACE) { - // A subface is already inserted. This case can only happen when there - // exist a subface inside the cavity, and two faked tets were created - // for protecting such a subface (see fig/dum-cavity-case6). - assert(oppo(*parytet) == dummypoint); - assert(marktested(*parytet)); - // This subface is redundant. But it is needed here (to remember the - // faked tet and the real subface which is inside the cavity). - if ((searchtet.ver & 01) != 0) esymself(searchtet); - // Adjust the searchtet to edge pt[1]->pt[0]. - if (org(searchtet) != pt[1]) { - symedgeself(searchtet); - assert(org(searchtet) == pt[1]); // SELF_CHECK - } - assert(dest(searchtet) == pt[0]); // SELF_CHECK - // Only connect: tmpsh<--searchtet. So stpivot() works. - sesymself(tmpsh); - tmpsh.sh[6 + EdgeRing(tmpsh.shver)] = (shellface) encode(searchtet); - } else { - if (b->verbose > 1) { - printf(" p:draw_subface(%d, %d, %d) -- %d is missing\n", - pointmark(pt[0]), pointmark(pt[1]), pointmark(pt[2]), i); - } - shellfacedealloc(subfaces, tmpsh.sh); - // Save this face in list. - misfaces->newindex((void **) &parytet1); - *parytet1 = *parytet; - continue; - } - // Remember the boundary tet in tmpsh (use the adjacent subface slot). - tmpsh.sh[0] = (shellface) encode(*parytet); - // Save this subface. - cavshells->newindex((void **) &parysh); - *parysh = tmpsh; +/////////////////////////////////////////////////////////////////////////////// +// // +// delaunizesegments() Split segments repeatedly until they appear in a // +// Delaunay tetrahedralization. // +// // +// Given a PLC X, which has a set V of vertices and a set of segments. Start // +// from a Delaunay tetrahedralization D of V, this routine recovers segments // +// of X in D by incrementally inserting points on missing segments, updating // +// D with the newly inserted points into D', which remains to be a Delaunay // +// tetrahedralization and respects the segments of X. Hence, each segment of // +// X appears as a union of edges in D'. // +// // +// This routine dynamically maintains two meshes, one is DT, another is the // +// surface mesh F of X. DT and F have exactly the same vertices. They are // +// updated simultaneously with the newly inserted points. // +// // +// Missing segments are found by looping the set S of segments, checking the // +// existence of each segment in DT. Once a segment is found missing in DT, // +// it is split into two subsegments by inserting a point into both DT and F, // +// and S is updated accordingly. However, the inserted point may cause some // +// other existing segments be non-Delaunay, hence are missing from the DT. // +// In order to force all segments to appear in DT, we have to loop S again // +// after some segments are split. (A little ugly method) Use a handle to // +// remember the last segment be split in one loop, hence all segments after // +// it are existing and need not be checked. // +// // +// In priciple, a segment on the convex hull should exist in DT. However, if // +// there are four coplanar points on the convex hull, and the DT only can // +// contain one diagonal edge which is unfortunately not the segment, then it // +// is missing. During the recovery of the segment, it is possible that the // +// calculated inserting point for recovering this convex hull segment is not // +// exact enough and lies (slightly) outside the DT. In order to insert the // +// point, we enlarge the convex hull of the DT, so it can contain the point // +// and remains convex. 'inserthullsite()' is called for this case. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::delaunizesegments() +{ + list *misseglist; + queue *flipqueue; + badface *misloop; + tetrahedron encodedtet; + triface searchtet, splittet; + face splitsh, symsplitsub; + face segloop, symsplitseg; + point refpoint, splitpoint, sympoint; + point tend, checkpoint; + point p1, p2, pa; + enum finddirectionresult collinear; + enum insertsiteresult success; + enum locateresult symloc; + bool coll; + long vertcount; + int i, j; + + if (!b->quiet) { + printf("Delaunizing segments.\n"); } - if (misfaces->objects > 0) { - // Removing tempoaray subfaces. - for (i = 0; i < (int) cavshells->objects; i++) { - parysh = (face *) fastlookup(cavshells, i); - stpivot(*parysh, neightet); - tsdissolve(neightet); // Detach it from adj. tets. - symself(neightet); - tsdissolve(neightet); - shellfacedealloc(subfaces, parysh->sh); - } - cavshells->restart(); - - // Infect the points which are of the cavity for detecting new - // cavity point due to the enlargement. - for (i = 0; i < (int) cavpoints->objects; i++) { - pt[0] = * (point *) fastlookup(cavpoints, i); - pinfect(pt[0]); // Mark it as inserted. - } - - // Enlarge the cavity. - for (i = 0; i < (int) misfaces->objects; i++) { - // Get a missing face. - parytet = (triface *) fastlookup(misfaces, i); - if (!infected(*parytet)) { - if (oppo(*parytet) == dummypoint) { - printf("Internal error: A convex hull is missing.\n"); - terminatetetgen(2); - } - // Put it into crossing tet list. - infect(*parytet); - crosstets->newindex((void **) &parytet1); - *parytet1 = *parytet; - // Insert the opposite point if it is not in DT. - pd = oppo(*parytet); - if (!pinfected(pd)) { - if (b->verbose > 1) { - printf(" Insert the opposite point %d.\n", pointmark(pd)); - } - pinfect(pd); - cavpoints->newindex((void **) &parypt); - *parypt = pd; - searchtet = recenttet; - insertvertexbw(pd, &searchtet, true, false, false, false); - } - // Check for a missing subface. - tspivot(*parytet, checksh); - if (checksh.sh != dummysh) { - if (b->verbose > 1) { - printf(" Queue a subface x%lx (%d, %d, %d).\n", - (unsigned long) checksh.sh, pointmark(sorg(checksh)), - pointmark(sdest(checksh)), pointmark(sapex(checksh))); + // Construct a map from points to tets for speeding point location. + makepoint2tetmap(); + // Initialize a flipqueue. + flipqueue = new queue(sizeof(badface)); + // Initialize the pool of missing segments. + misseglist = new list(sizeof(badface), NULL, SUBPERBLOCK); + // Looking for missing segments. + tallmissegs(misseglist); + // The DT contains segments now. + checksubsegs = 1; + // Remember the current number of points. + vertcount = points->items; + // Initialize the counters. + r1count = r2count = r3count = 0l; + + // Loop until 'misseglist' is empty. + while (misseglist->items > 0) { + // Randomly pick a missing segment to recover. + i = randomnation(misseglist->items); + misloop = (badface *)(* misseglist)[i]; + segloop = misloop->ss; + // Fill the "hole" in the list by filling the last one. + *misloop = *(badface *)(* misseglist)[misseglist->items - 1]; + misseglist->items--; + // Now recover the segment. + p1 = (point) segloop.sh[3]; + p2 = (point) segloop.sh[4]; + if (b->verbose > 1) { + printf(" Recover segment (%d, %d).\n", pointmark(p1), pointmark(p2)); + } + getsearchtet(p1, p2, &searchtet, &tend); + collinear = finddirection(&searchtet, tend, tetrahedrons->items); + if (collinear == LEFTCOLLINEAR) { + checkpoint = apex(searchtet); + } else if (collinear == RIGHTCOLLINEAR) { + checkpoint = dest(searchtet); + } else if (collinear == TOPCOLLINEAR) { + checkpoint = oppo(searchtet); + } else { +#ifdef SELF_CHECK + assert(collinear == ACROSSFACE || collinear == ACROSSEDGE); +#endif + checkpoint = (point) NULL; + } + if (checkpoint != tend) { + // ab is missing. + splitpoint = (point) NULL; + if (checkpoint != (point) NULL) { + // An existing point c is found on the segment. It can happen when + // ab is defined by a long segment with c inside it. Use c to + // split ab. No new point is created. + splitpoint = checkpoint; + if (pointtype(checkpoint) == FREEVOLVERTEX) { + // c is not a segment vertex yet. It becomes NACUTEVERTEX. + setpointtype(splitpoint, NACUTEVERTEX); + } else if (pointtype(checkpoint) == ACUTEVERTEX) { + // c is an acute vertex. The definition of PLC is wrong. + } else if (pointtype(checkpoint) == NACUTEVERTEX) { + // c is an nonacute vertex. The definition of PLC is wrong. + } else { + // assert(0); } - stdissolve(checksh); - sesymself(checksh); - stdissolve(checksh); - subfacstack->newindex((void **) &parysh); - *parysh = checksh; - } - // Add three opposite faces into the boundary list. - for (j = 0; j < 3; j++) { - fnext(*parytet, spintet); - symedge(spintet, neightet); - if ((neightet.tet == dummytet) || !infected(neightet)) { - if (b->verbose > 1) { - printf(" Add a cavface (%d, %d, %d).\n", - pointmark(org(spintet)), pointmark(dest(spintet)), - pointmark(apex(spintet))); - } - cavfaces->newindex((void **) &parytet1); - if (neightet.tet == dummytet) { - maketetrahedron(&faketet); // Create a faked tet. - setorg(faketet, org(spintet)); - setdest(faketet, dest(spintet)); - setapex(faketet, apex(spintet)); - setoppo(faketet, dummypoint); - bond(spintet, faketet); - tspivot(spintet, checksh); - if (checksh.sh != dummysh) { - sesymself(checksh); - tspivot(faketet, checksh); - } - for (k = 0; k < 3; k++) { - tsspivot1(spintet, checkseg); - if (checkseg.sh != dummysh) { - tssbond1(faketet, checkseg); - } - enextself(spintet); - enextself(faketet); - } - *parytet1 = faketet; - } else { - *parytet1 = neightet; + } else { + // Find a reference point p of ab. + refpoint = scoutrefpoint(&searchtet, tend); + if (pointtype(refpoint) == FREEVOLVERTEX) { + // p is an input point, check if it is nearly collinear with ab. + coll = iscollinear(p1, p2, refpoint, b->epsilon); + if (coll) { + // a, b, and p are collinear. We insert p into ab. p becomes + // a segment vertex with type NACUTEVERTEX. + splitpoint = refpoint; + setpointtype(splitpoint, NACUTEVERTEX); } - } else { - // Check if a subface is missing again. - tspivot(neightet, checksh); - if (checksh.sh != dummysh) { - if (b->verbose > 1) { - printf(" Queue a subface x%lx (%d, %d, %d).\n", - (unsigned long) checksh.sh, pointmark(sorg(checksh)), - pointmark(sdest(checksh)), pointmark(sapex(checksh))); + } + if (splitpoint == (point) NULL) { + // Calculate a split point v using rule 1, or 2, or 3. + splitpoint = getsplitpoint(&segloop, refpoint); + + // Is there periodic boundary conditions? + if (checkpbcs) { + // Yes! Insert points on other segments of incident pbcgroups. + i = shellmark(segloop) - 1; + for (j = idx2segpglist[i]; j < idx2segpglist[i + 1]; j++) { + makepoint(&sympoint); + symloc = getsegpbcsympoint(splitpoint, &segloop, sympoint, + &symsplitseg, segpglist[j]); +#ifdef SELF_CHECK + assert(symloc != OUTSIDE); +#endif + if ((symloc == ONEDGE) && (symsplitseg.sh != segloop.sh)) { +#ifdef SELF_CHECK + assert(symsplitseg.sh != dummysh); +#endif + setpointtype(sympoint, FREESEGVERTEX); + setpoint2sh(sympoint, sencode(symsplitseg)); + // Insert sympoint into DT. + pa = sorg(symsplitseg); + splittet.tet = dummytet; + // Find a good start point to search. + encodedtet = point2tet(pa); + if (encodedtet != (tetrahedron) NULL) { + decode(encodedtet, splittet); + if (isdead(&splittet)) { + splittet.tet = dummytet; + } + } + // Locate sympoint in DT. Do exact location. + success = insertsite(sympoint, &splittet, false, flipqueue); +#ifdef SELF_CHECK + assert(success != DUPLICATEPOINT); +#endif + if (success == OUTSIDEPOINT) { + inserthullsite(sympoint, &splittet, flipqueue); + } + if (steinerleft > 0) steinerleft--; + // Let sympoint remember splittet. + setpoint2tet(sympoint, encode(splittet)); + // Do flip in DT. + lawson(misseglist, flipqueue); + // Insert sympoint into F. + symsplitseg.shver = 0; + spivot(symsplitseg, symsplitsub); + // sympoint should on the edge of symsplitsub. + splitsubedge(sympoint, &symsplitsub, flipqueue); + // Do flip in facet. + flipsub(flipqueue); + // Insert the two subsegments. + symsplitseg.shver = 0; + insertsegment(&symsplitseg, misseglist); + senextself(symsplitseg); + spivotself(symsplitseg); + symsplitseg.shver = 0; + insertsegment(&symsplitseg, misseglist); + } else { // if (symloc == ONVERTEX) { + // The sympoint already exists. It is possible when two + // pbc groups are exactly the same. Omit this point. + pointdealloc(sympoint); + } } - stdissolve(checksh); - sesymself(checksh); - stdissolve(checksh); - subfacstack->newindex((void **) &parysh); - *parysh = checksh; } - } - enextself(*parytet); - } // j - } // if (!infected(parytet)) - } - - // Uninfect the points which are of the cavity. - for (i = 0; i < (int) cavpoints->objects; i++) { - pt[0] = * (point *) fastlookup(cavpoints, i); - puninfect(pt[0]); - } - - misfaces->restart(); - cavityexpcount++; - continue; - } - - break; - - } // while (1) - // Collect all tets of the DT. All new tets are marktested. - marktest(recenttet); - newtets->newindex((void **) &parytet); - *parytet = recenttet; - for (i = 0; i < (int) newtets->objects; i++) { - searchtet = * (triface *) fastlookup(newtets, i); - for (searchtet.loc = 0; searchtet.loc < 4; searchtet.loc++) { - sym(searchtet, neightet); - if (neightet.tet != dummytet) { - if (!marktested(neightet)) { - marktest(neightet); - newtets->newindex((void **) &parytet); - *parytet = neightet; + // Insert 'splitpoint' into DT. + if (isdead(&searchtet)) searchtet.tet = dummytet; + success = insertsite(splitpoint, &searchtet, false, flipqueue); + if (success == OUTSIDEPOINT) { + // A convex hull edge is missing, and the inserting point lies + // (slightly) outside the convex hull due to the significant + // digits lost in the calculation. Enlarge the convex hull. + inserthullsite(splitpoint, &searchtet, flipqueue); + } + if (steinerleft > 0) steinerleft--; + // Remember a handle in 'splitpoint' to enhance the speed of + // consequent point location. + setpoint2tet(splitpoint, encode(searchtet)); + // Maintain Delaunayness in DT. + lawson(misseglist, flipqueue); + } } + // Insert 'splitpoint' into F. + spivot(segloop, splitsh); + splitsubedge(splitpoint, &splitsh, flipqueue); + flipsub(flipqueue); + // Insert the two subsegments. + segloop.shver = 0; + insertsegment(&segloop, misseglist); + senextself(segloop); + spivotself(segloop); + segloop.shver = 0; + insertsegment(&segloop, misseglist); } - } } - cavpoints->restart(); - // Comment: Now no vertex is marked. - cavfaces->restart(); + // Detach all segments from tets. + tetrahedrons->traversalinit(); + searchtet.tet = tetrahedrontraverse(); + while (searchtet.tet != (tetrahedron *) NULL) { + for (i = 0; i < 6; i++) { + searchtet.tet[8 + i] = (tetrahedron) dummysh; + } + searchtet.tet = tetrahedrontraverse(); + } + // No segments now. + checksubsegs = 0; - if (cavshells->objects > (long) maxcavsize) { - maxcavsize = cavshells->objects; + if (b->verbose > 0) { + printf(" %ld protect points.\n", points->items - vertcount); + printf(" R1: %ld, R2: %ld, R3: %ld.\n", r1count, r2count, r3count); } - return true; + delete flipqueue; + delete misseglist; } +// +// End of segments recovery routines +// + +// +// Begin of facet recovery routines +// + /////////////////////////////////////////////////////////////////////////////// // // -// fillcavity() Fill new tets into the cavity. // +// insertsubface() Fix a subface in place. // // // -// The new tets are stored in two disjoint sets(which share the same facet). // -// 'topfaces' and 'botfaces' are the boundaries of these two sets, respect- // -// ively. 'midfaces' is empty on input, and will store faces in the facet. // +// Search a subface s in current tetrahedralization T. If s is found a face // +// face of T, it is inserted into T. Return FALSE if s is not found in T. // // // /////////////////////////////////////////////////////////////////////////////// -bool tetgenmesh::fillcavity(arraypool* topshells, arraypool* botshells, - arraypool* midfaces, arraypool* facpoints) +bool tetgenmesh::insertsubface(face* insertsh, triface* searchtet) { - arraypool *cavshells; - triface *parytet, bdrytet, toptet, bottet, neightet, midface, spintet; - face checksh, *parysh; - face checkseg; - point pa, pb, pc, pf, pg; - REAL ori, len, n[3]; - bool mflag, bflag; - int i, j, k; + triface spintet, symtet; + face testsh, testseg; + face spinsh, casin, casout; + point tapex, checkpoint; + enum finddirectionresult collinear; + int hitbdry; - // Connect newtets to tets outside the cavity. - for (k = 0; k < 2; k++) { - cavshells = (k == 0 ? topshells : botshells); - if (cavshells != NULL) { - for (i = 0; i < (int) cavshells->objects; i++) { - // Get a temp subface. - parysh = (face *) fastlookup(cavshells, i); - // Get the boundary tet outsode the cavity. - decode(parysh->sh[0], bdrytet); - pa = sorg(*parysh); - pb = sdest(*parysh); - // Fix bdrytet at the edge pb->pa. - bdrytet.ver = 0; - for (j = 0; j < 3; j++) { - if (org(bdrytet) == pb) break; - enextself(bdrytet); - } - assert(j < 3); - assert(dest(bdrytet) == pa); - // pa = org(bdrytet); - // pb = dest(bdrytet); - pc = apex(bdrytet); - // Get the adjacent new tet which is in the cavity. - stpivot(*parysh, neightet); - // Fix neightet at the edge pa->pb. - neightet.ver = 0; - for (j = 0; j < 3; j++) { - if (org(neightet) == pa) break; - enextself(neightet); - } - assert(j < 3); - assert(dest(neightet) == pb); // SELF_CHECK - // Mark neightet as an interior tet of this cavity, 2009-04-24. - if (!infected(neightet)) { - infect(neightet); - } - // Comment: bdrytet may be a faked tet, Bond it if it is not - // marktested, i.e., it is not created for holding an interor - // subface. The connections will be used in fillcavity for - // finding middle faces. - if (!marktested(bdrytet)) { - // Bond the two tets. - bond(bdrytet, neightet); - // } else { - // A new boundary face. - // dummytet[0] = encode(neightet); - } - // Bond a subface (if it exists). - tspivot(bdrytet, checksh); - if (checksh.sh != dummysh) { - sesymself(checksh); - tsbond(neightet, checksh); // Also cleared the pointer to tmpsh. - } else { - tsdissolve(neightet); // No subface, clear the pointer to tmpsh. - } - // Bond subsegments - for (j = 0; j < 3; j++) { - tsspivot1(bdrytet, checkseg); - if (checkseg.sh != dummysh) { - spintet = neightet; - while (1) { - tssbond1(spintet, checkseg); - tfnextself(spintet); - if (spintet.tet == dummytet) break; // Outside the cavity. - if (!marktested(spintet)) break; // Outside the cavity. - if (spintet.tet == neightet.tet) break; // Turn back. - } - } - enextself(bdrytet); - enext2self(neightet); - } - // 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); - // if (oppo(bdrytet) == dummypoint) { - // Delete a faked tet. - // tetrahedrondealloc(bdrytet.tet); - // } - } - } // if (cavshells != NULL) + // Search an edge of s. + getsearchtet(sorg(*insertsh), sdest(*insertsh), searchtet, &checkpoint); + collinear = finddirection(searchtet, checkpoint, tetrahedrons->items); + if (collinear == LEFTCOLLINEAR) { + enext2self(*searchtet); + esymself(*searchtet); + } else if (collinear == TOPCOLLINEAR) { + fnextself(*searchtet); + enext2self(*searchtet); + esymself(*searchtet); } - - mflag = true; // Initialize it. - - if (midfaces != NULL) { - - // Mark all facet vertices for finding middle subfaces. - for (i = 0; i < (int) facpoints->objects; i++) { - pf = * (point *) fastlookup(facpoints, i); - pinfect(pf); + if (dest(*searchtet) != checkpoint) { + // The edge doesn't exist => s is missing. + return false; } - // The first pair of top and bottom tets share the same edge [a, b]. - // toptet = * (triface *) fastlookup(topfaces, 0); - if (infected(firsttopface)) { - // The cavity was enlarged. This tet is included in the interior - // (as those of a crossing tet). Find the updated top boundary face - // by rotating the faces around this edge (until an uninfect tet). - pa = apex(firsttopface); - while (1) { - tfnextself(firsttopface); - assert(firsttopface.tet != dummytet); - if (!infected(firsttopface)) break; - assert(apex(firsttopface) != pa); // SELF_CHECK - } - } - toptet = firsttopface; - symedgeself(toptet); - assert(marktested(toptet)); // It must be a new tet. - // Search a subface from the top mesh. - while (1) { - fnextself(toptet); // The next face in the same tet. - pc = apex(toptet); - if (pinfected(pc)) break; // [a,b,c] is a subface. - symedgeself(toptet); // Go to the same face in the adjacent tet. - assert(toptet.tet != dummytet); - } - // Search the subface [a,b,c] in the bottom mesh. - // bottet = * (triface *) fastlookup(botfaces, 0); - if (infected(firstbotface)) { - pa = apex(firstbotface); - while (1) { - tfnextself(firstbotface); - assert(firstbotface.tet != dummytet); - if (!infected(firstbotface)) break; - assert(apex(firstbotface) != pa); // SELF_CHECK - } - } - bottet = firstbotface; - symedgeself(bottet); - assert(marktested(bottet)); // It must be a new tet. - while (1) { - fnextself(bottet); // The next face in the same tet. - pf = apex(bottet); - if (pf == pc) break; // Face matched. - if (pinfected(pf)) { - mflag = false; break; // Not matched. - } - symedgeself(bottet); - assert(bottet.tet != dummytet); - } - if (mflag) { - // Connect the two tets together. - bond(toptet, bottet); - // Both are interior tets. - infect(toptet); - infect(bottet); - // Add this face into search list. - // esymself(toptet); // Choose the 0th edge ring. - markface(toptet); - midfaces->newindex((void **) &parytet); - *parytet = toptet; - } - - // Match pairs of subfaces (middle faces), connect top and bottom tets. - for (i = 0; i < (int) midfaces->objects && mflag; i++) { - // Get a matched middle face [a, b, c] - midface = * (triface *) fastlookup(midfaces, i); - // It is inside the cavity. - assert(marktested(midface)); // SELF_CHECK - // Check the neighbors at edges [b, c] and [c, a]. - midface.ver = 0; - for (j = 0; j < 3 && mflag; j++) { - pg = apex(midface); - toptet = midface; - bflag = false; - while (1) { - // Go to the next face in the same tet. - fnextself(toptet); - pc = apex(toptet); - if (pinfected(pc)) { - break; // Find a subface. - } - // if (pc == dummypoint) { - // break; // Find a subface. - // } - /* if (pc == pg) { - // The adjacent face is not a middle face. - bflag = true; break; - }*/ - symedgeself(toptet); - assert(toptet.tet != dummytet); // The adjacent tet must exist. - // Do we walk outside the cavity? - if (!marktested(toptet)) { - // Yes, the adjacent face is not a middle face. - bflag = true; break; - } - } - if (!bflag) { - // assert(marktested(toptet)); // SELF_CHECK - if (!facemarked(toptet)) { - symedge(midface, bottet); - while (1) { - fnextself(bottet); - pf = apex(bottet); - if (pf == pc) break; // Face matched. - if (pinfected(pf)) { - mflag = false; break; // Not matched. - } - symedgeself(bottet); - assert(bottet.tet != dummytet); // The adjacent tet must exist. - } - if (mflag) { - if (marktested(bottet)) { - // Connect two tets together. - bond(toptet, bottet); - // Both are interior tets. - infect(toptet); - infect(bottet); - // Add this face into list. - // esymself(toptet); - 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. - symedge(toptet, neightet); - assert(neightet.tet == bottet.tet); // SELF_CHECK - assert(neightet.loc == bottet.loc); // SELF_CHECK - // Do not add this face into 'midfaces'. - } - } + // Search s by spinning faces around the edge. + tapex = sapex(*insertsh); + spintet = *searchtet; + hitbdry = 0; + do { + if (apex(spintet) == tapex) { + // Found s in T. Check if s has already been inserted. + tspivot(spintet, testsh); + if (testsh.sh == dummysh) { + adjustedgering(spintet, CCW); + findedge(insertsh, org(spintet), dest(spintet)); + tsbond(spintet, *insertsh); + sym(spintet, symtet); // 'symtet' maybe outside, use it anyway. + sesymself(*insertsh); + tsbond(symtet, *insertsh); + } else { + // Found a duplicated subface (due to the redundant input). + if (!b->quiet) { + printf("Warning: Two subfaces are found duplicated at "); + printf("(%d, %d, %d)\n", pointmark(sorg(testsh)), + pointmark(sdest(testsh)), pointmark(sapex(testsh))); + printf(" Subface of facet #%d is deleted.\n", shellmark(*insertsh)); + // printf(" Hint: -d switch can find all duplicated facets.\n"); } + shellfacedealloc(subfaces, insertsh->sh); } - enextself(midface); // Go to the next edge. - } // j - } // i - - } // if (midfaces != NULL) - - if (mflag) { - if (midfaces != NULL) { - if (b->verbose > 1) { - printf(" Found %ld middle subfaces.\n", midfaces->objects); - } - if (midfaces->objects > (long) maxregionsize) { - maxregionsize = (long) midfaces->objects; - } - // Unmark middle faces. - for (i = 0; i < (int) 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); - if (0) { // if (b->verbose > 1) { - printf(" p:draw_tet(%d, %d, %d, %d) -- top tet.\n", pointmark(pa), - pointmark(pb), pointmark(pc), pointmark(oppo(toptet))); - printf(" p:draw_tet(%d, %d, %d, %d) -- bot tet.\n", - pointmark(org(bottet)), pointmark(dest(bottet)), - pointmark(apex(bottet)), pointmark(oppo(bottet))); - } - // Calculate a point above the faces. - facenormal2(pa, pb, pc, n, 1); - len = sqrt(DOT(n, n)); - n[0] /= len; - n[1] /= len; - n[2] /= len; - len = DIST(pa, pb); - len += DIST(pb, pc); - len += DIST(pc, pa); - len /= 3.0; - dummypoint[0] = pa[0] + len * n[0]; - dummypoint[1] = pa[1] + len * n[1]; - dummypoint[2] = pa[2] + len * n[2]; - // Find the crossing edges. - ori = orient3d(pb, pc, dummypoint, pf); - assert(ori != 0); // SELF_CHECK - if (ori < 0) { - // The top edge [b, c] intersects the bot edge [a, f]. - enextself(toptet); - enextself(bottet); - } else { - // The top edge [c, a] intersects the bot edge [f, b]. - enext2self(toptet); - enext2self(bottet); - } - // Split one of the edges, choose the one has longer length. - n[0] = DIST(org(toptet), dest(toptet)); - n[1] = DIST(org(bottet), dest(bottet)); - if (n[0] > n[1]) { - pf = org(toptet); - pg = dest(toptet); - } else { - pf = org(bottet); - pg = dest(bottet); - } - if (b->verbose > 1) { - printf(" Found a non-Delaunay edge (%d, %d)\n", pointmark(pf), - pointmark(pg)); - } - // Create the midpoint of the non-Delaunay edge. - for (i = 0; i < 3; i++) { - dummypoint[i] = 0.5 * (pf[i] + pg[i]); - } - // Set a tet for searching the new point. - recenttet = firsttopface; - // dummypoint[0] = dummypoint[1] = dummypoint[2] = 0; - ndelaunayedgecount++; - } - - if (facpoints != NULL) { - // Unmark all facet vertices. - for (i = 0; i < (int) facpoints->objects; i++) { - pf = * (point *) fastlookup(facpoints, i); - puninfect(pf); + return true; } - } - - // Delete the temp subfaces and faked tets. - for (k = 0; k < 2; k++) { - cavshells = (k == 0 ? topshells : botshells); - if (cavshells != NULL) { - for (i = 0; i < (int) cavshells->objects; i++) { - parysh = (face *) fastlookup(cavshells, i); - decode(parysh->sh[0], bdrytet); - if (oppo(bdrytet) == dummypoint) { - sym(bdrytet, neightet); - if (neightet.tet != dummytet) { - // This side is a hull face (not an interior subface). - dissolve(neightet); - dummytet[0] = encode(neightet); - tspivot(neightet, checksh); - if (checksh.sh != dummysh) { - assert(checksh.sh != parysh->sh); - // Dis-coonection tet-subface bond. - sesymself(checksh); - stdissolve(checksh); - } - } - // Delete a faked tet. - tetrahedrondealloc(bdrytet.tet); + if (!fnextself(spintet)) { + hitbdry ++; + if (hitbdry < 2) { + esym(*searchtet, spintet); + if (!fnextself(spintet)) { + hitbdry ++; } - shellfacedealloc(subfaces, parysh->sh); } } - } - - topshells->restart(); - if (botshells != NULL) { - botshells->restart(); - } - if (midfaces != NULL) { - midfaces->restart(); - } - // Comment: Now no vertex is marked. + } while (hitbdry < 2 && apex(spintet) != apex(*searchtet)); - return mflag; + // s is missing. + return false; } /////////////////////////////////////////////////////////////////////////////// // // -// carvecavity() Delete old tets and outer new tets of the cavity. // +// tritritest() Test if two triangles are intersecting in their interior. // +// // +// One triangle t1 is the face of 'checktet', the other t2 is given by three // +// corners 'p1', 'p2' and 'p3'. This routine calls tri_tri_inter() to detect // +// whether t1 and t2 exactly intersect in their interior. Cases like share a // +// vertex, share an edge, or coincidence are considered not intersect. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::carvecavity(arraypool *crosstets, arraypool *topnewtets, - arraypool *botnewtets) +bool tetgenmesh::tritritest(triface* checktet, point p1, point p2, point p3) { - arraypool *newtets; - triface *parytet, *pnewtet, neightet; - face checkseg; //, *parysh; - // int hitbdry; - int i, j, k; - - /*// NOTE: Some subsegments may contained inside the cavity. They must be - // queued for recovery. See fig/dump-cavity-case20. - for (i = 0; i < (int) crosstets->objects; i++) { - parytet = (triface *) fastlookup(crosstets, i); - assert(infected(*parytet)); // SELF_CHECK - if (parytet->tet[8] != NULL) { - for (j = 0; j < 6; j++) { - parytet->loc = edge2locver[j][0]; - parytet->ver = edge2locver[j][1]; - tsspivot1(*parytet, checkseg); - if (checkseg.sh != dummysh) { - if (!sinfected(checkseg)) { - // It is not queued yet. - neightet = *parytet; - hitbdry = 0; - while (1) { - tfnextself(neightet); - if (neightet.tet == dummytet) { - hitbdry++; - if (hitbdry == 2) break; - esym(*parytet, neightet); - tfnextself(neightet); - if (neightet.tet == dummytet) break; - } - if (!infected(neightet)) break; - if (apex(neightet) == apex(*parytet)) break; - } - if (infected(neightet)) { - if (b->verbose > 1) { - printf(" Queue a missing segment (%d, %d).\n", - pointmark(sorg(checkseg)), pointmark(sdest(checkseg))); - } - sinfect(checkseg); - subsegstack->newindex((void **) &parysh); - *parysh = checkseg; - } - } - } - } - } - }*/ - - // Delete the old tets in cavity. - for (i = 0; i < (int) crosstets->objects; i++) { - parytet = (triface *) fastlookup(crosstets, i); - tetrahedrondealloc(parytet->tet); - } - crosstets->restart(); // crosstets will be re-used. + point forg, fdest, fapex; + enum interresult intersect; - // Collect infected new tets in cavity. - for (k = 0; k < 2; k++) { - newtets = (k == 0 ? topnewtets : botnewtets); - if (newtets != NULL) { - for (i = 0; i < (int) newtets->objects; i++) { - parytet = (triface *) fastlookup(newtets, i); - if (infected(*parytet)) { - crosstets->newindex((void **) &pnewtet); - *pnewtet = *parytet; - } - } - } - } - // Collect all new tets in cavity. - for (i = 0; i < (int) 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(neightet.tet != dummytet); // SELF_CHECK - infect(neightet); - crosstets->newindex((void **) &pnewtet); - *pnewtet = neightet; - } - } - } - } + forg = org(*checktet); + fdest = dest(*checktet); + fapex = apex(*checktet); - // Delete outer new tets (those new tets which are not infected). - for (k = 0; k < 2; k++) { - newtets = (k == 0 ? topnewtets : botnewtets); - if (newtets != NULL) { - for (i = 0; i < (int) newtets->objects; i++) { - parytet = (triface *) fastlookup(newtets, i); - if (infected(*parytet)) { - // This is an interior tet. - uninfect(*parytet); - unmarktest(*parytet); - } else { - // An outer tet. Delete it. - tetrahedrondealloc(parytet->tet); - } - } - } - } +#ifdef SELF_CHECK + REAL ax, ay, az, bx, by, bz; + REAL n[3]; + // face (torg, tdest, tapex) should not be degenerate. However p1, p2, + // and p3 may be collinear. Check it. + ax = forg[0] - fdest[0]; + ay = forg[1] - fdest[1]; + az = forg[2] - fdest[2]; + bx = forg[0] - fapex[0]; + by = forg[1] - fapex[1]; + bz = forg[2] - fapex[2]; + n[0] = ay * bz - by * az; + n[1] = az * bx - bz * ax; + n[2] = ax * by - bx * ay; + assert(fabs(n[0]) + fabs(n[1]) + fabs(n[2]) > 0.0); + // The components of n should not smaller than the machine epsilon. + + ax = p1[0] - p2[0]; + ay = p1[1] - p2[1]; + az = p1[2] - p2[2]; + bx = p1[0] - p3[0]; + by = p1[1] - p3[1]; + bz = p1[2] - p3[2]; + n[0] = ay * bz - by * az; + n[1] = az * bx - bz * ax; + n[2] = ax * by - bx * ay; + assert(fabs(n[0]) + fabs(n[1]) + fabs(n[2]) > 0.0); + // The components of n should not smaller than the machine epsilon. +#endif - crosstets->restart(); - topnewtets->restart(); - if (botnewtets != NULL) { - botnewtets->restart(); - } + intersect = tri_tri_inter(forg, fdest, fapex, p1, p2, p3); + return intersect == INTERSECT; } /////////////////////////////////////////////////////////////////////////////// // // -// restorecavity() Reconnect old tets and delete new tets of the cavity. // +// initializecavity() Initialize the cavity. // +// // +// A cavity C is bounded by a list of faces, called fronts. Each front f is // +// hold by a tet t adjacent to C, t is not in C (uninfected). If f is a hull // +// face, t does't exist, a fake tet t' is created to hold f. t' has the same // +// vertices as f but no opposite. t' will be removed automatically after C // +// is filled with new tets (by carvecavity()). // +// // +// The faces of C are given in two lists. 'floorlist' is a set of subfaces, // +// each subface has been oriented to face to the inside of C. 'ceillist' is // +// a set of tetrahedral faces. 'frontlist' returns the initialized fronts. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::restorecavity(arraypool *crosstets, arraypool *topnewtets, - arraypool *botnewtets) +void tetgenmesh::initializecavity(list* floorlist, list* ceillist, + list* frontlist) { - triface *parytet, neightet; - face checksh; - point *ppt; - int i, j; + triface neightet, casingtet; + triface faketet; + face worksh; + int i; - // Reconnect crossing tets to cavity boundary. - for (i = 0; i < (int) 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->loc = 0; parytet->loc < 4; parytet->loc++) { - sym(*parytet, neightet); - // The neighbor may be a deleted faked tet. - if (isdead(&neightet) || (neightet.tet == dummytet)) { - dissolve(*parytet); // Detach a faked tet. - // Remember a boundary tet. - dummytet[0] = encode(*parytet); - } else if (!infected(neightet)) { - bond(*parytet, neightet); - tspivot(*parytet, checksh); - if (checksh.sh != dummysh) { - tsbond(*parytet, checksh); - } - } + // Initialize subfaces of C. + for (i = 0; i < floorlist->len(); i++) { + // Get a subface s. + worksh = * (face *)(* floorlist)[i]; +#ifdef SELF_CHECK + // Current side of s should be empty. + stpivot(worksh, neightet); + assert(neightet.tet == dummytet); +#endif + // Get the adjacent tet t. + sesymself(worksh); + stpivot(worksh, casingtet); + // Does t exist? + if (casingtet.tet == dummytet) { + // Create a fake tet t' to hold f temporarily. + maketetrahedron(&faketet); + setorg(faketet, sorg(worksh)); + setdest(faketet, sdest(worksh)); + setapex(faketet, sapex(worksh)); + setoppo(faketet, (point) NULL); // Indicates it is 'fake'. + tsbond(faketet, worksh); + frontlist->append(&faketet); + } else { + frontlist->append(&casingtet); } - // Update the point-to-tet map. - parytet->loc = 0; - ppt = (point *) &(parytet->tet[4]); - for (j = 0; j < 4; j++) { - setpoint2tet(ppt[j], encode(*parytet)); + } + // Initialize tet faces of C. + for (i = 0; i < ceillist->len(); i++) { + // Get a tet face c. + neightet = * (triface *) (* ceillist)[i]; +#ifdef SELF_CHECK + // The tet of c must be inside C (going to be deleted). + assert(infected(neightet)); +#endif + // Get the adjacent tet t. + sym(neightet, casingtet); + // Does t exist? + if (casingtet.tet == dummytet) { + // No. Create a fake tet t' to hold f temporarily. + maketetrahedron(&faketet); + // Be sure that the vertices of t' are CCW oriented. + adjustedgering(neightet, CW); // CW edge ring. + setorg(faketet, org(neightet)); + setdest(faketet, dest(neightet)); + setapex(faketet, apex(neightet)); + setoppo(faketet, (point) NULL); // Indicates it is 'fake'. + // Bond t' to a subface if it exists. + tspivot(neightet, worksh); + if (worksh.sh != dummysh) { + sesymself(worksh); + tsbond(faketet, worksh); + } + // Bond c <--> t'. So we're able to find t' and remove it. + bond(faketet, neightet); + // c may become uninfected due to the bond(). + infect(neightet); + frontlist->append(&faketet); + } else { + frontlist->append(&casingtet); } } +} - // Uninfect all crossing tets. - for (i = 0; i < (int) crosstets->objects; i++) { - parytet = (triface *) fastlookup(crosstets, i); - uninfect(*parytet); - } +/////////////////////////////////////////////////////////////////////////////// +// // +// retrievenewtets() Retrieve the newly created tets. // +// // +// On input, 'newtetlist' contains at least one alive new tet. From this tet,// +// other new tets can be found by a broadth-first searching. // +// // +/////////////////////////////////////////////////////////////////////////////// - // Delete new tets. - for (i = 0; i < (int) topnewtets->objects; i++) { - parytet = (triface *) fastlookup(topnewtets, i); - tetrahedrondealloc(parytet->tet); - } +void tetgenmesh::retrievenewtets(list* newtetlist) +{ + triface searchtet, casingtet; + int i; - if (botnewtets != NULL) { - for (i = 0; i < (int) botnewtets->objects; i++) { - parytet = (triface *) fastlookup(botnewtets, i); - tetrahedrondealloc(parytet->tet); + // There may be dead tets due to flip32(). Delete them first. + for (i = 0; i < newtetlist->len(); i++) { + searchtet = * (triface *)(* newtetlist)[i]; + if (isdead(&searchtet)) { + newtetlist->del(i, 0); i--; + continue; + } + infect(searchtet); + } + // Find all new tets. + for (i = 0; i < newtetlist->len(); i++) { + searchtet = * (triface *)(* newtetlist)[i]; + for (searchtet.loc = 0; searchtet.loc < 4; searchtet.loc++) { + sym(searchtet, casingtet); + if ((casingtet.tet != dummytet) && !infected(casingtet)) { + infect(casingtet); + newtetlist->append(&casingtet); + } } } - - crosstets->restart(); - topnewtets->restart(); - if (botnewtets != NULL) { - botnewtets->restart(); + // Uninfect new tets. + for (i = 0; i < newtetlist->len(); i++) { + searchtet = * (triface *)(* newtetlist)[i]; + uninfect(searchtet); } } /////////////////////////////////////////////////////////////////////////////// // // -// splitsubedge() Split a non-Delaunay edge (not a segment) in the // -// surface mesh of a facet. // +// delaunizecavvertices() Form a DT of the vertices of a cavity. // // // -// The new point 'newpt' will be inserted in the tetrahedral mesh if it does // -// not cause any existing (sub)segments become non-Delaunay. Otherwise, the // -// new point is not inserted and one of such subsegments will be split. // +// 'floorptlist' and 'ceilptlist' are the vertices of the cavity. // // // -// Next,the actual inserted new point is also inserted into the surface mesh.// -// Non-Delaunay segments and newly created subfaces are queued for recovery. // +// The tets of the DT are created directly in the pool 'tetrahedrons', i.e., // +// no auxiliary data structure and memory are required. The trick is at the // +// time they're created, there are no connections between them to the other // +// tets in the pool. You can imagine they form an ioslated island. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::splitsubedge(point newpt, face *searchsh, arraypool *facfaces, - arraypool *facpoints) +void tetgenmesh::delaunizecavvertices(triface* oldtet, list* floorptlist, + list* ceilptlist, list* newtetlist, queue* flipque) { - // queue *flipqueue; - triface searchtet; - face splitsh; - face *psseg, sseg; // *parysh; - point pa, pb; - enum locateresult loc; - int s, i; - - // Try to insert the point. Do not insert if it will encroach any segment - // (noencsegflag is TRUE). Queue encroacged subfaces. - assert(subsegstack->objects == 0l); // SELF_CHECK - searchtet = recenttet; // Start search it from recentet - // loc = insertvertexbw(newpt, &searchtet, true, true, true, false); - // Always insert this point, missing segments are queued. 2009-06-11. - loc = insertvertexbw(newpt, &searchtet, true, true, false, false); - - if (loc == ENCSEGMENT) { - // Some segments are encroached. Randomly pick one to split. - assert(subsegstack->objects > 0l); - s = randomnation(subsegstack->objects); - psseg = (face *) fastlookup(subsegstack, s); - sseg = *psseg; - pa = sorg(sseg); - pb = sdest(sseg); - for (i = 0; i < 3; i++) newpt[i] = 0.5 * (pa[i] + pb[i]); - setpointtype(newpt, FREESEGVERTEX); - setpoint2sh(newpt, sencode(sseg)); - // Uninfect all queued segments. - for (i = 0; i < (int) subsegstack->objects; i++) { - psseg = (face *) fastlookup(subsegstack, i); - suninfect(*psseg); - } - subsegstack->restart(); // Clear the queue. - // Split the segment. Two subsegments are queued. - sinsertvertex(newpt, searchsh, &sseg, true, false); - // Insert the point. Missing segments are queued. - searchtet = recenttet; // Start search it from recentet - insertvertexbw(newpt, &searchtet, true, true, false, false); - } else { - /*// Calc an above point for point location in surface triangulation. - calculateabovepoint(facpoints, NULL, NULL, NULL); - // Insert the new point on facet. New subfaces are queued for reocvery. - loc = sinsertvertex(newpt, searchsh, NULL, true, false); - if (loc == OUTSIDE) { - assert(0); // Not handled yet. - } - // Clear the above point. - dummypoint[0] = dummypoint[1] = dummypoint[2] = 0; - */ - // Set the abovepoint of f for point location. - abovepoint = facetabovepointarray[shellmark(*searchsh)]; - if (abovepoint == (point) NULL) { - getfacetabovepoint(searchsh); - } - // Insert the new point on facet. New subfaces are queued for reocvery. - loc = sinsertvertex(newpt, searchsh, NULL, true, false); - if (loc == OUTSIDE) { - assert(0); // Not handled yet. + point *insertarray; + triface bakhulltet, newtet; + long bakhullsize; + long arraysize; + int bakchksub; + int i, j; + + // Prepare the array of points for inserting. + arraysize = floorptlist->len(); + if (ceilptlist != (list *) NULL) { + arraysize += ceilptlist->len(); + } + insertarray = new point[arraysize]; + for (i = 0; i < floorptlist->len(); i++) { + insertarray[i] = * (point *)(* floorptlist)[i]; + } + if (ceilptlist != (list *) NULL) { + for (j = 0; j < ceilptlist->len(); j++) { + insertarray[i + j] = * (point *)(* ceilptlist)[j]; } } -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// constrainedfacets() Recover subfaces saved in 'subfacestack'. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::constrainedfacets2() -{ - arraypool *crosstets, *topnewtets, *botnewtets; - arraypool *topfaces, *botfaces, *midfaces; - arraypool *topshells, *botshells, *facfaces; - arraypool *toppoints, *botpoints, *facpoints; - triface *parytet, searchtet, neightet; - face *pssub, ssub, neighsh; - face checkseg; - point *ppt, pt, newpt; - enum interresult dir; - bool success, delaunayflag; - long bakflip22count; - long cavitycount; - int facetcount; - int bakhullsize; - int s, i, j; - - if (b->verbose) { - printf(" Constraining facets.\n"); - } - - // Initialize arrays. - crosstets = new arraypool(sizeof(triface), 10); - topnewtets = new arraypool(sizeof(triface), 10); - botnewtets = new arraypool(sizeof(triface), 10); - topfaces = new arraypool(sizeof(triface), 10); - botfaces = new arraypool(sizeof(triface), 10); - midfaces = new arraypool(sizeof(triface), 10); - toppoints = new arraypool(sizeof(point), 8); - botpoints = new arraypool(sizeof(point), 8); - facpoints = new arraypool(sizeof(point), 8); - facfaces = new arraypool(sizeof(face), 10); - topshells = new arraypool(sizeof(face), 10); - botshells = new arraypool(sizeof(face), 10); - - bakflip22count = flip22count; - cavitycount = 0; - facetcount = 0; - - // Loop until 'subfacstack' is empty. - while (subfacstack->objects > 0l) { - subfacstack->objects--; - pssub = (face *) fastlookup(subfacstack, subfacstack->objects); - ssub = *pssub; - - if (ssub.sh[3] == NULL) continue; // Skip a dead subface. - - stpivot(ssub, neightet); - if (neightet.tet == dummytet) { - sesymself(ssub); - stpivot(ssub, neightet); - } - - if (neightet.tet == dummytet) { - // Find an unrecovered subface. - smarktest(ssub); - facfaces->newindex((void **) &pssub); - *pssub = ssub; - // Get all subfaces and vertices of the same facet. - for (i = 0; i < (int) facfaces->objects; i++) { - ssub = * (face *) fastlookup(facfaces, i); - for (j = 0; j < 3; j++) { - sspivot(ssub, checkseg); - if (checkseg.sh == dummysh) { - spivot(ssub, neighsh); - assert(neighsh.sh != dummysh); // SELF_CHECK - if (!smarktested(neighsh)) { - // It may be already recovered. - stpivot(neighsh, neightet); - if (neightet.tet == dummytet) { - sesymself(neighsh); - stpivot(neighsh, neightet); - } - if (neightet.tet == dummytet) { - // Add it into list. - smarktest(neighsh); - facfaces->newindex((void **) &pssub); - *pssub = neighsh; - } - } - } - pt = sorg(ssub); - if (!pinfected(pt)) { - pinfect(pt); - facpoints->newindex((void **) &ppt); - *ppt = pt; - } - senextself(ssub); - } // j - } // i - // Have found all facet subfaces (vertices). Uninfect them. - for (i = 0; i < (int) facfaces->objects; i++) { - pssub = (face *) fastlookup(facfaces, i); - sunmarktest(*pssub); - } - for (i = 0; i < (int) facpoints->objects; i++) { - ppt = (point *) fastlookup(facpoints, i); - puninfect(*ppt); - } - if (b->verbose > 1) { - printf(" Recover facet #%d: %ld subfaces, %ld vertices.\n", - facetcount + 1, facfaces->objects, facpoints->objects); - } - facetcount++; - - // Loop until 'facfaces' is empty. - while (facfaces->objects > 0l) { - // Get the last subface of this array. - facfaces->objects--; - pssub = (face *) fastlookup(facfaces, facfaces->objects); - ssub = *pssub; - - stpivot(ssub, neightet); - if (neightet.tet == dummytet) { - sesymself(ssub); - stpivot(ssub, neightet); - } - - if (neightet.tet != dummytet) continue; // Not a missing subface. - - // Insert the subface. - searchtet.tet = NULL; - dir = scoutsubface(&ssub, &searchtet, 1); - if (dir == SHAREFACE) continue; // The subface is inserted. - assert(dir != COLLISIONFACE); // SELF_CHECK - - // Not exist. Push the subface back into stack. - s = randomnation(facfaces->objects + 1); - facfaces->newindex((void **) &pssub); - *pssub = * (face *) fastlookup(facfaces, s); - * (face *) fastlookup(facfaces, s) = ssub; - - if (dir == EDGETRIINT) continue; // All three edges are missing. - - // Search for a crossing tet. - dir = scoutcrosstet(&ssub, &searchtet, facpoints); - - if (dir == INTERTET) { - // Recover subfaces by local retetrahedralization. - cavitycount++; - bakhullsize = hullsize; - checksubsegs = checksubfaces = 0; - crosstets->newindex((void **) &parytet); - *parytet = searchtet; - // Form a cavity of crossing tets. - formcavity(&ssub, crosstets, topfaces, botfaces, toppoints, - botpoints, facpoints, facfaces); - delaunayflag = true; - // Tetrahedralize the top part. Re-use 'midfaces'. - success = delaunizecavity(toppoints, topfaces, topshells, - topnewtets, crosstets, midfaces); - if (success) { - // Tetrahedralize the bottom part. Re-use 'midfaces'. - success = delaunizecavity(botpoints, botfaces, botshells, - botnewtets, crosstets, midfaces); - if (success) { - // Fill the cavity with new tets. - success = fillcavity(topshells, botshells, midfaces, facpoints); - if (success) { - // Delete old tets and outer new tets. - carvecavity(crosstets, topnewtets, botnewtets); - } - } else { - delaunayflag = false; - } - } else { - delaunayflag = false; - } - if (!success) { - // Restore old tets and delete new tets. - restorecavity(crosstets, topnewtets, botnewtets); - } - /*if (!delaunayflag) { - dump_facetof(&ssub, "facet1.lua"); - while (futureflip != NULL) { - formedgecavity(futureflip->forg, futureflip->fdest, crosstets, - topfaces, toppoints); - crosstets->restart(); - topfaces->restart(); - toppoints->restart(); - futureflip = futureflip->nextitem; - } - flippool->restart(); - outnodes(0); - checkmesh(); - checkshells(1); - assert(0); // Stop the program. - }*/ - hullsize = bakhullsize; - checksubsegs = checksubfaces = 1; - } else if (dir == INTERFACE) { - // Recover subfaces by flipping edges in surface mesh. - recoversubfacebyflips(&ssub, &searchtet, facfaces); - success = true; - } else { // dir == TOUCHFACE - assert(0); - } - if (!success) break; - } // while - - if (facfaces->objects > 0l) { - // Found a non-Delaunay edge, split it (or a segment close to it). - // Create a new point at the middle of this edge, its coordinates - // were saved in dummypoint in 'fillcavity()'. - makepoint(&newpt); - for (i = 0; i < 3; i++) newpt[i] = dummypoint[i]; - setpointtype(newpt, FREESUBVERTEX); - setpoint2sh(newpt, sencode(ssub)); - dummypoint[0] = dummypoint[1] = dummypoint[2] = 0; - // Insert the new point. Starting search it from 'ssub'. - splitsubedge(newpt, &ssub, facfaces, facpoints); - facfaces->restart(); - } - // Clear the list of facet vertices. - facpoints->restart(); - // Some subsegments may be queued, recover them. - if (subsegstack->objects > 0l) { - b->verbose--; // Suppress the message output. - delaunizesegments2(); - b->verbose++; - } - // Now the mesh should be constrained Delaunay. - } // if (neightet.tet == NULL) - } + // The incrflipdelaunay() is re-used. Backup global variables. + decode(dummytet[0], bakhulltet); + bakhullsize = hullsize; + bakchksub = checksubfaces; + checksubfaces = 0; + b->verbose--; - if (b->verbose) { - printf(" %ld subedge flips.\n", flip22count - bakflip22count); - printf(" %ld cavities remeshed.\n", cavitycount); - } + // Form the DT by incremental flip Delaunay algorithm. Do not jump for + // point location, do not merge points. + incrflipdelaunay(oldtet, insertarray, arraysize, false, false, 0.0, flipque); + + // Get a tet in D. + decode(dummytet[0], newtet); + newtetlist->append(&newtet); + // Get all tets of D. + retrievenewtets(newtetlist); - // Delete arrays. - delete crosstets; - delete topnewtets; - delete botnewtets; - delete topfaces; - delete botfaces; - delete midfaces; - delete toppoints; - delete botpoints; - delete facpoints; - delete facfaces; - delete topshells; - delete botshells; + // Restore global variables. + dummytet[0] = encode(bakhulltet); + hullsize = bakhullsize; + checksubfaces = bakchksub; + b->verbose++; + + delete [] insertarray; } /////////////////////////////////////////////////////////////////////////////// // // -// formskeleton() Form a constrained tetrahedralization. // +// insertauxsubface() Fix an auxilary subface in place. // +// // +// An auxilary subface s is fixed in D as it is a real subface, but s has no // +// vertices and neighbors. It has two uses: (1) it protects an identfied // +// front f in D; (2) it serves the link to bond a tet in C and f later. The // +// first neighbor of s (s->sh[0]) stores a pointer to f. // // // -// The segments and facets of a PLS will be recovered. // +// 'front' is a front f of C. idfront' t is a tet in D where f is identified // +// be a face of it. s will be fixed between t and its neighbor. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::formskeleton(clock_t& tv) +void tetgenmesh::insertauxsubface(triface* front, triface* idfront) { - triface searchtet; - face *pssub, ssub; - int s, i; - - if (!b->quiet) { - printf("Recovering boundaries.\n"); - } - - caveshlist = new arraypool(sizeof(face), 10); - caveshbdlist = new arraypool(sizeof(face), 10); - - // Put all segments into the list. - if (b->nojettison == 1) { // '-J' option (for debug) - // The sequential order. - subsegs->traversalinit(); - for (i = 0; i < subsegs->items; i++) { - ssub.sh = shellfacetraverse(subsegs); - sinfect(ssub); // Only save it once. - subsegstack->newindex((void **) &pssub); - *pssub = ssub; - } - } else { - // Randomly order the segments. - 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 **) &pssub); - *pssub = * (face *) fastlookup(subsegstack, s); - // Put i-th seg to be the s-th. - ssub.sh = shellfacetraverse(subsegs); - sinfect(ssub); // Only save it once. - pssub = (face *) fastlookup(subsegstack, s); - *pssub = ssub; - } - } - - // Segments will be introduced. - checksubsegs = 1; - // Recover segments. - delaunizesegments2(); - - tv = clock(); - - // Randomly order the subfaces. - subfaces->traversalinit(); - for (i = 0; i < subfaces->items; i++) { - s = randomnation(i + 1); - // Move the s-th subface to the i-th. - subfacstack->newindex((void **) &pssub); - *pssub = * (face *) fastlookup(subfacstack, s); - // Put i-th subface to be the s-th. - ssub.sh = shellfacetraverse(subfaces); - pssub = (face *) fastlookup(subfacstack, s); - *pssub = ssub; - } - - // Subfaces will be introduced. - checksubfaces = 1; - // Recover facets. - constrainedfacets2(); - - delete caveshlist; - delete caveshbdlist; - caveshlist = NULL; - caveshbdlist = NULL; + triface neightet; + face auxsh; - // Detach all segments from tets. - tetrahedrons->traversalinit(); - searchtet.tet = tetrahedrontraverse(); - while (searchtet.tet != (tetrahedron *) NULL) { - if (searchtet.tet[8] != NULL) { - for (i = 0; i < 6; i++) { - searchtet.loc = edge2locver[i][0]; - searchtet.ver = edge2locver[i][1]; - tssdissolve1(searchtet); - } - searchtet.tet[8] = NULL; - } - searchtet.tet = tetrahedrontraverse(); + // Create the aux subface s. + makeshellface(subfaces, &auxsh); + // Bond s <--> t. + tsbond(*idfront, auxsh); + // Does t's neighbor n exist? + sym(*idfront, neightet); + if (neightet.tet != dummytet) { + // Bond s <--> n. + sesymself(auxsh); + tsbond(neightet, auxsh); } - // Now no segment is bonded to tets. - checksubsegs = 0; - // Delete the memory. - tet2segpool->restart(); + // Let s remember f. + auxsh.sh[0] = (shellface) encode(*front); } /////////////////////////////////////////////////////////////////////////////// // // -// infecthull() Virally infect all of the tetrahedra of the convex hull // -// that are not protected by subfaces. Where there are // -// subfaces, set boundary markers as appropriate. // +// scoutfront() Scout a face in D. // // // -// Memorypool 'viri' is used to return all the infected tetrahedra. // +// Search a 'front' f in D. If f is found, return TRUE and the face of D is // +// returned in 'idfront'. Otherwise, return FALSE. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::infecthull(memorypool *viri) +bool tetgenmesh::scoutfront(triface* front, triface* idfront, list* newtetlist) { - triface tetloop, tsymtet; - tetrahedron **deadtet; - face hullface; - // point horg, hdest, hapex; + triface spintet; + point pa, pb, pc; + enum locateresult loc; + enum finddirectionresult col; + int hitbdry; + int i; - if (b->verbose > 1) { - printf(" Marking concavities for elimination.\n"); + // Let the front we're searching is abc. + pa = org(*front); + pb = dest(*front); + // Get a tet in D for searching. + *idfront = recenttet; + // Make sure the tet is valid (it may be killed by flips). + if (isdead(idfront)) { + // The tet is dead. Search a live tet in D. !!! + for (i = 0; i < newtetlist->len(); i++) { + recenttet = * (triface *)(* newtetlist)[i]; + if (!isdead(&recenttet)) break; + } + assert(i < newtetlist->len()); } - tetrahedrons->traversalinit(); - tetloop.tet = tetrahedrontraverse(); - while (tetloop.tet != (tetrahedron *) NULL) { - // Is this tetrahedron on the hull? - for (tetloop.loc = 0; tetloop.loc < 4; tetloop.loc++) { - sym(tetloop, tsymtet); - if (tsymtet.tet == dummytet) { - // Is the tetrahedron protected by a subface? - tspivot(tetloop, hullface); - if (hullface.sh == dummysh) { - // The tetrahedron is not protected; infect it. - if (!infected(tetloop)) { - infect(tetloop); - deadtet = (tetrahedron **) viri->alloc(); - *deadtet = tetloop.tet; - break; // Go and get next tet. - } - } else { - // The tetrahedron is protected; set boundary markers if appropriate. - if (shellmark(hullface) == 0) { - setshellmark(hullface, 1); - /* - horg = sorg(hullface); - hdest = sdest(hullface); - hapex = sapex(hullface); - if (pointmark(horg) == 0) { - setpointmark(horg, 1); - } - if (pointmark(hdest) == 0) { - setpointmark(hdest, 1); - } - if (pointmark(hapex) == 0) { - setpointmark(hapex, 1); - } - */ + + // Search a tet having vertex a. + loc = preciselocate(pa, idfront, (long) newtetlist->len()); + assert(loc == ONVERTEX); + recenttet = *idfront; + // Search a tet having edge ab. + col = finddirection(idfront, pb, (long) newtetlist->len()); + if (col == RIGHTCOLLINEAR) { + // b is just the destination. + } else if (col == LEFTCOLLINEAR) { + enext2self(*idfront); + esymself(*idfront); + } else if (col == TOPCOLLINEAR) { + fnextself(*idfront); + enext2self(*idfront); + esymself(*idfront); + } + + if (dest(*idfront) == pb) { + // Search a tet having face abc + pc = apex(*front); + spintet = *idfront; + hitbdry = 0; + do { + if (apex(spintet) == pc) { + // Found abc. Insert an auxilary subface s at idfront. + // insertauxsubface(front, &spintet); + *idfront = spintet; + return true; + } + if (!fnextself(spintet)) { + hitbdry ++; + if (hitbdry < 2) { + esym(*idfront, spintet); + if (!fnextself(spintet)) { + hitbdry ++; } } } - } - tetloop.tet = tetrahedrontraverse(); + if (apex(spintet) == apex(*idfront)) break; + } while (hitbdry < 2); + } + + // f is missing in D. + if (b->verbose > 2) { + printf(" Front (%d, %d, %d) is missing.\n", pointmark(pa), + pointmark(pb), pointmark(apex(*front))); } + return false; } /////////////////////////////////////////////////////////////////////////////// // // -// plague() Spread the virus from all infected tets to any neighbors not // -// protected by subfaces. // -// // -// This routine identifies all the tetrahedra that will die, and marks them // -// as infected. They are marked to ensure that each tetrahedron is added to // -// the virus pool only once, so the procedure will terminate. 'viri' returns // -// all infected tetrahedra which are outside the domian. // +// gluefronts() Glue two fronts together. // +// // +// This is a support routine for identifyfront(). Two fronts f and f1 are // +// found indentical. This is caused by the non-coplanarity of vertices of a // +// facet. Hence f and f1 are a subface and a tet. They are not fronts of the // +// cavity anymore. This routine glues f and f1 together. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::plague(memorypool *viri) +void tetgenmesh::gluefronts(triface* front, triface* front1) { - tetrahedron **virusloop; - tetrahedron **deadtet; - triface testtet, neighbor; - face neighsh, testseg; - face spinsh, casingin, casingout; - int firstdadsub; - int i; + face consh; - if (b->verbose > 1) { - printf(" Marking neighbors of marked tetrahedra.\n"); + // Glue f and f1 together. There're four cases: + // (1) both f and f1 are not fake; + // (2) f is not fake, f1 is fake; + // (3) f is fake and f1 is not fake; + // (4) both f and f1 are fake. + // Case (4) should be not possible. + + // Is there a concrete subface c at f. + tspivot(*front, consh); + if (consh.sh != dummysh) { + sesymself(consh); + tsbond(*front1, consh); // Bond: f1 <--> c. + sesymself(consh); } - firstdadsub = 0; - // Loop through all the infected tetrahedra, spreading the virus to - // their neighbors, then to their neighbors' neighbors. - viri->traversalinit(); - virusloop = (tetrahedron **) viri->traverse(); - while (virusloop != (tetrahedron **) NULL) { - testtet.tet = *virusloop; - // Temporarily uninfect this tetrahedron, not necessary. - uninfect(testtet); - // Check each of the tetrahedron's four neighbors. - for (testtet.loc = 0; testtet.loc < 4; testtet.loc++) { - // Find the neighbor. - sym(testtet, neighbor); - // Check for a shell between the tetrahedron and its neighbor. - tspivot(testtet, neighsh); - // Check if the neighbor is nonexistent or already infected. - if ((neighbor.tet == dummytet) || infected(neighbor)) { - if (neighsh.sh != dummysh) { - // There is a subface separating the tetrahedron from its neighbor, - // but both tetrahedra are dying, so the subface dies too. - // Before deallocte this subface, dissolve the connections between - // other subfaces, subsegments and tetrahedra. - neighsh.shver = 0; - if (!firstdadsub) { - firstdadsub = 1; // Report the problem once. - if (!b->quiet) { - printf("Warning: Detecting an open face (%d, %d, %d).\n", - pointmark(sorg(neighsh)), pointmark(sdest(neighsh)), - pointmark(sapex(neighsh))); - } - } - // For keep the same enext() direction. - findedge(&testtet, sorg(neighsh), sdest(neighsh)); - for (i = 0; i < 3; i++) { - sspivot(neighsh, testseg); - if (testseg.sh != dummysh) { - // A subsegment is found at this side, dissolve this subface - // from the face link of this subsegment. - testseg.shver = 0; - spinsh = neighsh; - if (sorg(spinsh) != sorg(testseg)) { - sesymself(spinsh); - } - spivot(spinsh, casingout); - if ((casingout.sh == spinsh.sh) || (casingout.sh == dummysh)) { - // This is a trivial face link, only 'neighsh' itself, - // the subsegment at this side is also died. - shellfacedealloc(subsegs, testseg.sh); - } else { - spinsh = casingout; - do { - casingin = spinsh; - spivotself(spinsh); - } while (spinsh.sh != neighsh.sh); - // Set the link casingin->casingout. - sbond1(casingin, casingout); - // Bond the subsegment anyway. - ssbond(casingin, testseg); - } - } - senextself(neighsh); - enextself(testtet); - } - if (neighbor.tet != dummytet) { - // Make sure the subface doesn't get deallocated again later - // when the infected neighbor is visited. - tsdissolve(neighbor); - } - // This subface has been separated. - if (in->mesh_dim > 2) { - shellfacedealloc(subfaces, neighsh.sh); - } else { - // Dimension is 2. keep it for output. - // Dissolve tets at both sides of this subface. - stdissolve(neighsh); - sesymself(neighsh); - stdissolve(neighsh); - } - } - } else { // The neighbor exists and is not infected. - if (neighsh.sh == dummysh) { - // There is no subface protecting the neighbor, infect it. - infect(neighbor); - // Ensure that the neighbor's neighbors will be infected. - deadtet = (tetrahedron **) viri->alloc(); - *deadtet = neighbor.tet; - } else { // The neighbor is protected by a subface. - // Remove this tetrahedron from the subface. - stdissolve(neighsh); - // The subface becomes a boundary. Set markers accordingly. - if (shellmark(neighsh) == 0) { - setshellmark(neighsh, 1); - } - // This side becomes hull. Update the handle in dummytet. - dummytet[0] = encode(neighbor); - } + // Does f hold by a fake tet. + if (oppo(*front) == (point) NULL) { + // f is fake. Case (3) or (4). + assert(oppo(*front1) != (point) NULL); // Eliminate (4). + // Case (3). + if (consh.sh != dummysh) { + stdissolve(consh); // Dissolve: c -x-> f. + } + // Dealloc f. + tetrahedrondealloc(front->tet); + // f1 becomes a hull. let 'dummytet' bond to it. + dummytet[0] = encode(*front1); + } else { + // Case (1) or (2). + bond(*front, *front1); // Bond f1 <--> f. + } + // Is f a fake tet? + if (!isdead(front)) { + // No. Check for case (2). + tspivot(*front1, consh); + // Is f1 fake? + if (oppo(*front1) == (point) NULL) { + // Case (2) or (4) + assert(oppo(*front) != (point) NULL); // Eliminate (4). + // Case (2). + if (consh.sh != dummysh) { + stdissolve(consh); // Dissolve: c -x-> f1. + sesymself(consh); // Bond: f <--> c. + tsbond(*front, consh); } + // Dissolve: f -x->f1. + dissolve(*front); + // Dealloc f1. + tetrahedrondealloc(front1->tet); + // f becomes a hull. let 'dummytet' bond to it. + dummytet[0] = encode(*front); + } else { + // Case (1). + if (consh.sh != dummysh) { + sesymself(consh); + tsbond(*front, consh); // Bond: f <--> c. + } } - // Remark the tetrahedron as infected, so it doesn't get added to the - // virus pool again. - infect(testtet); - virusloop = (tetrahedron **) viri->traverse(); } } /////////////////////////////////////////////////////////////////////////////// // // -// regionplague() Spread regional attributes and/or volume constraints // -// (from a .poly file) throughout the mesh. // +// identifyfronts() Identify cavity faces in D. // // // -// This procedure operates in two phases. The first phase spreads an attri- // -// bute and/or a volume constraint through a (facet-bounded) region. The // -// second phase uninfects all infected tetrahedra, returning them to normal. // +// 'frontlist' are fronts of C need indentfying. This routine searches each // +// front f in D. Once f is found, an auxilary subface s is inserted in D at // +// the face. If f is not found in D, remove it from frontlist and save it in // +// 'misfrontlist'. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh:: -regionplague(memorypool *regionviri, REAL attribute, REAL volume) +bool tetgenmesh::identifyfronts(list* frontlist, list* misfrontlist, + list* newtetlist) { - tetrahedron **virusloop; - tetrahedron **regiontet; - triface testtet, neighbor; - face neighsh; + triface front, front1, tfront; + triface idfront, neightet; + face auxsh; + int len, i, j; - if (b->verbose > 1) { - printf(" Marking neighbors of marked tetrahedra.\n"); - } - // Loop through all the infected tetrahedra, spreading the attribute - // and/or volume constraint to their neighbors, then to their neighbors' - // neighbors. - regionviri->traversalinit(); - virusloop = (tetrahedron **) regionviri->traverse(); - while (virusloop != (tetrahedron **) NULL) { - testtet.tet = *virusloop; - // Temporarily uninfect this tetrahedron, not necessary. - uninfect(testtet); - if (b->regionattrib) { - // Set an attribute. - setelemattribute(testtet.tet, in->numberoftetrahedronattributes, - attribute); - } - if (b->varvolume) { - // Set a volume constraint. - setvolumebound(testtet.tet, volume); - } - // Check each of the tetrahedron's four neighbors. - for (testtet.loc = 0; testtet.loc < 4; testtet.loc++) { - // Find the neighbor. - sym(testtet, neighbor); - // Check for a subface between the tetrahedron and its neighbor. - tspivot(testtet, neighsh); - // Make sure the neighbor exists, is not already infected, and - // isn't protected by a subface, or is protected by a nonsolid - // subface. - if ((neighbor.tet != dummytet) && !infected(neighbor) - && (neighsh.sh == dummysh)) { - // Infect the neighbor. - infect(neighbor); - // Ensure that the neighbor's neighbors will be infected. - regiontet = (tetrahedron **) regionviri->alloc(); - *regiontet = neighbor.tet; + misfrontlist->clear(); + // Set a new tet in D for searching. + recenttet = * (triface *)(* newtetlist)[0]; + + // Identify all fronts in D. + for (i = 0; i < frontlist->len(); i++) { + // Get a front f. + front = * (triface *)( *frontlist)[i]; + if (scoutfront(&front, &idfront, newtetlist)) { + // Found f. Insert an aux subface s. + assert((idfront.tet != dummytet) && !isdead(&idfront)); + // Does s already exist? + tspivot(idfront, auxsh); + if (auxsh.sh != dummysh) { + // There're two identical fronts, f (front) and f1 (s.sh[0])! + decode((tetrahedron) auxsh.sh[0], front1); + assert((front1.tet != dummytet) && !infected(front1)); + // Detach s in D. + tsdissolve(idfront); + sym(idfront, neightet); + if (neightet.tet != dummytet) { + tsdissolve(neightet); + } + // s has fulfilled its duty. Can be deleted. + shellfacedealloc(subfaces, auxsh.sh); + // Remove f from frontlist. + frontlist->del(i, 1); i--; + // Remove f1 from frontlist. + len = frontlist->len(); + for (j = 0; j < frontlist->len(); j++) { + tfront = * (triface *)(* frontlist)[j]; + if ((tfront.tet == front1.tet) && (tfront.loc == front1.loc)) { + // Found f1 in list. Check f1 != f. + assert((tfront.tet != front.tet) || (tfront.loc != front.loc)); + frontlist->del(j, 1); i--; + break; + } + } + assert((frontlist->len() + 1) == len); + // Glue f and f1 together. + gluefronts(&front, &front1); + } else { + // Insert an aux subface to protect f in D. + insertauxsubface(&front, &idfront); + } + } else { + // f is missing. + frontlist->del(i, 1); i--; + // Are there two identical fronts, f (front) and f1 (front1)? + for (j = 0; j < misfrontlist->len(); j++) { + front1 = * (triface *)(* misfrontlist)[j]; + if (isfacehaspoint(&front1, org(front)) && + isfacehaspoint(&front1, dest(front)) && + isfacehaspoint(&front1, apex(front))) break; + } + if (j < misfrontlist->len()) { + // Found an identical front f1. Remove f1 from the list. + misfrontlist->del(j, 1); + // Glue f and f1 together. + gluefronts(&front, &front1); + } else { + // Add f into misfrontlist. + misfrontlist->append(&front); } } - // Remark the tetrahedron as infected, so it doesn't get added to the - // virus pool again. - infect(testtet); - virusloop = (tetrahedron **) regionviri->traverse(); - } - - // Uninfect all tetrahedra. - if (b->verbose > 1) { - printf(" Unmarking marked tetrahedra.\n"); - } - regionviri->traversalinit(); - virusloop = (tetrahedron **) regionviri->traverse(); - while (virusloop != (tetrahedron **) NULL) { - testtet.tet = *virusloop; - uninfect(testtet); - virusloop = (tetrahedron **) regionviri->traverse(); } - // Empty the virus pool. - regionviri->restart(); + return misfrontlist->len() == 0; } /////////////////////////////////////////////////////////////////////////////// // // -// removeholetets() Remove tetrahedra which are outside the domain. // +// detachauxsubfaces() Detach auxilary subfaces in D. // +// // +// This is a reverse routine of identifyfronts(). Some fronts are missing in // +// D. C can not be easily tetrahedralized. It needs remediation (expansion, // +// or constrained flips, or adding a Steiner point). This routine detaches // +// the auxilary subfaces have been inserted in D and delete them. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::removeholetets(memorypool* viri) +void tetgenmesh::detachauxsubfaces(list* newtetlist) { - tetrahedron **virusloop; - triface testtet, neighbor; - point checkpt; - int *tetspernodelist; - int i, j; - - if (b->verbose > 1) { - printf(" Deleting marked tetrahedra.\n"); - } - - // Create and initialize 'tetspernodelist'. - tetspernodelist = new int[points->items + 1]; - for (i = 0; i < points->items + 1; i++) tetspernodelist[i] = 0; - - // Loop the tetrahedra list, counter the number of tets sharing each node. - tetrahedrons->traversalinit(); - testtet.tet = tetrahedrontraverse(); - while (testtet.tet != (tetrahedron *) NULL) { - // Increment the number of sharing tets for each endpoint. - for (i = 0; i < 4; i++) { - j = pointmark((point) testtet.tet[4 + i]); - tetspernodelist[j]++; - } - testtet.tet = tetrahedrontraverse(); - } + triface newtet, neightet; + face auxsh; + int i; - viri->traversalinit(); - virusloop = (tetrahedron **) viri->traverse(); - while (virusloop != (tetrahedron **) NULL) { - testtet.tet = *virusloop; - // Record changes in the number of boundary faces, and disconnect - // dead tetrahedra from their neighbors. - for (testtet.loc = 0; testtet.loc < 4; testtet.loc++) { - sym(testtet, neighbor); - if (neighbor.tet == dummytet) { - // There is no neighboring tetrahedron on this face, so this face - // is a boundary face. This tetrahedron is being deleted, so this - // boundary face is deleted. - hullsize--; - } else { - // Disconnect the tetrahedron from its neighbor. - dissolve(neighbor); - // There is a neighboring tetrahedron on this face, so this face - // becomes a boundary face when this tetrahedron is deleted. - hullsize++; - } - } - // Check the four corners of this tet if they're isolated. - for (i = 0; i < 4; i++) { - checkpt = (point) testtet.tet[4 + i]; - j = pointmark(checkpt); - tetspernodelist[j]--; - if (tetspernodelist[j] == 0) { - // If it is added volume vertex or '-j' is not used, delete it. - if ((pointtype(checkpt) == FREEVOLVERTEX) || !b->nojettison) { - setpointtype(checkpt, UNUSEDVERTEX); - unuverts++; + for (i = 0; i < newtetlist->len(); i++) { + // Get a new tet t. + newtet = * (triface *)(* newtetlist)[i]; + // t may e dead due to flips. + if (isdead(&newtet)) continue; + assert(!infected(newtet)); + // Check the four faces of t. + for (newtet.loc = 0; newtet.loc < 4; newtet.loc++) { + tspivot(newtet, auxsh); + if (auxsh.sh != dummysh) { + // An auxilary subface s. + assert(sorg(auxsh) == (point) NULL); + tsdissolve(newtet); // t -x-> s. + sym(newtet, neightet); + if (neightet.tet != dummytet) { + assert(!isdead(&neightet)); + tsdissolve(neightet); // n -x-> s. } + // Delete s. + shellfacedealloc(subfaces, auxsh.sh); } } - // Return the dead tetrahedron to the pool of tetrahedra. - tetrahedrondealloc(testtet.tet); - virusloop = (tetrahedron **) viri->traverse(); } - - delete [] tetspernodelist; } /////////////////////////////////////////////////////////////////////////////// // // -// assignregionattribs() Assign each tetrahedron a region number. // +// expandcavity() Expand the cavity by adding new fronts. // // // -// This routine is called when '-AA' switch is specified. Every tetrahedron // -// of a (bounded) region will get a integer number to that region. Default, // -// regions are numbered as 1, 2, 3, etc. However, if a number has already // -// been used (set by user in the region section in .poly or .smesh), it is // -// skipped and the next available number will be used. // +// This is the support routine for delaunizecavity(). Some fronts of C are // +// missing in D since they're not strongly Delaunay. Such fronts are removed // +// and the faces of the tets abutting to them are added. C is then expanded. // +// Some removed faces may be subfaces, they're queued to recover later. D is // +// expanded simultaneously with the new vertices of the new fronts. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::assignregionattribs() +void tetgenmesh::expandcavity(list* frontlist, list* misfrontlist, + list* newtetlist, list* crosstetlist, queue* missingshqueue, queue* flipque) { - list *regionnumlist; - list *regiontetlist; - triface tetloop, regiontet, neightet; + triface misfront, newfront, casingtet, crosstet; + triface searchtet, faketet, bakhulltet; face checksh; - bool flag; - int regionnum, num; - int attridx, count; - int i; + point pd; + enum insertsiteresult success; + long bakhullsize; + int bakchksub; + int i, j, k; if (b->verbose > 1) { - printf(" Assign region numbers.\n"); + printf(" Expand cavity (%d missing fronts).\n", misfrontlist->len()); } + // Increase the number of expanded times. + expcavcount++; + // The incrflipdelaunay() is re-used. Backup global variables. + decode(dummytet[0], bakhulltet); + bakhullsize = hullsize; + bakchksub = checksubfaces; + checksubfaces = 0; + b->verbose--; - regionnumlist = new list(sizeof(int), NULL, 256); - regiontetlist = new list(sizeof(triface), NULL, 1024); - attridx = in->numberoftetrahedronattributes; + // Choose a tet in D for searching. + recenttet = * (triface *)(* newtetlist)[0]; + assert((recenttet.tet != dummytet) && !isdead(&recenttet)); - // Loop through all tets. Infect tets which already have a region number, - // and save the used numbers in 'regionnumlist'. - tetrahedrons->traversalinit(); - tetloop.tet = tetrahedrontraverse(); - while (tetloop.tet != (tetrahedron *) NULL) { - if (!infected(tetloop)) { - regionnum = (int) elemattribute(tetloop.tet, attridx); - if (regionnum != 0.0) { - // Found a numbered region tet. - infect(tetloop); - regiontetlist->append(&tetloop); - // Found and infect all tets in this region. - for (i = 0; i < regiontetlist->len(); i++) { - regiontet = * (triface *)(* regiontetlist)[i]; - for (regiontet.loc = 0; regiontet.loc < 4; regiontet.loc++) { - // Is there a boundary face? - tspivot(regiontet, checksh); - if (checksh.sh == dummysh) { - sym(regiontet, neightet); - if ((neightet.tet != dummytet) && !infected(neightet)) { -#ifdef SELF_CHECK - // neightet should have the same region number. Check it. - num = (int) elemattribute(neightet.tet, attridx); - assert(num == regionnum); -#endif - infect(neightet); - regiontetlist->append(&neightet); - } - } - } - } - // Add regionnum to list if it is not exist. - flag = false; - for (i = 0; i < regionnumlist->len() && !flag; i++) { - num = * (int *)(* regionnumlist)[i]; - flag = (num == regionnum); - } - if (!flag) regionnumlist->append(®ionnum); - // Clear list for the next region. - regiontetlist->clear(); - } + // Loop through 'misfrontlist'. + for (i = 0; i < misfrontlist->len(); i++) { + // Get a missing front f. + misfront = * (triface *)(* misfrontlist)[i]; + // C will be expanded at f. + if (b->verbose > 1) { + printf(" Get misfront (%d, %d, %d).\n", pointmark(org(misfront)), + pointmark(dest(misfront)), pointmark(apex(misfront))); } - tetloop.tet = tetrahedrontraverse(); - } - - if (b->verbose) { - printf(" %d user-specified regions.\n", regionnumlist->len()); - } - - // Now loop the tets again. Assign region numbers to uninfected tets. - tetrahedrons->traversalinit(); - tetloop.tet = tetrahedrontraverse(); - regionnum = 1; // Start region number. - count = 0; - while (tetloop.tet != (tetrahedron *) NULL) { - if (!infected(tetloop)) { - // An unassigned region tet. - count++; - do { - flag = false; - // Check if the region number has been used. - for (i = 0; i < regionnumlist->len() && !flag; i++) { - num = * (int *)(* regionnumlist)[i]; - flag = (num == regionnum); - } - if (flag) regionnum++; - } while (flag); - setelemattribute(tetloop.tet, attridx, (REAL) regionnum); - infect(tetloop); - regiontetlist->append(&tetloop); - // Found and infect all tets in this region. - for (i = 0; i < regiontetlist->len(); i++) { - regiontet = * (triface *)(* regiontetlist)[i]; - for (regiontet.loc = 0; regiontet.loc < 4; regiontet.loc++) { - // Is there a boundary face? - tspivot(regiontet, checksh); - if (checksh.sh == dummysh) { - sym(regiontet, neightet); - if ((neightet.tet != dummytet) && !infected(neightet)) { -#ifdef SELF_CHECK - // neightet should have not been assigned yet. Check it. - num = (int) elemattribute(neightet.tet, attridx); - assert(num == 0); + // Is f has a subface s? + tspivot(misfront, checksh); + if (checksh.sh != dummysh) { + // A subface s is found. Check whether f is expandable at s. + sym(misfront, crosstet); + if (!infected(crosstet)) { + // f is not expandable. In principle is should not happen. However, + // it can happen when PBC is in use. + assert(checkpbcs); + // Skip expanding f. It will be processed later. + continue; + } + // Temporarily remove s. Queue and recover it later. + if (b->verbose > 1) { + printf(" Queuing subface (%d, %d, %d).\n", + pointmark(sorg(checksh)), pointmark(sdest(checksh)), + pointmark(sapex(checksh))); + } + // Detach s from tets at its both sides. + tsdissolve(misfront); + tsdissolve(crosstet); + // Detach tets at from s. + stdissolve(checksh); + sesymself(checksh); + stdissolve(checksh); + // Mark and queue it. + sinfect(checksh); + missingshqueue->push(&checksh); + } + // f may already be processed (become a cross tet of C). + if (infected(misfront)) continue; + // Get the point p = oppo(t), t is the tet holds f. + pd = oppo(misfront); +#ifdef SELF_CHECK + // t must not be fake. + assert(pd != (point) NULL); #endif - setelemattribute(neightet.tet, attridx, (REAL) regionnum); - infect(neightet); - regiontetlist->append(&neightet); - } + // Insert p in D. p may not be inserted if it is one of the two cases: + // (1) p is already a vertex of D; + // (2) p lies outside the CH of D; + searchtet = recenttet; + // Make sure the tet is valid (it may be killed by flips). + if (isdead(&searchtet)) { + // The tet is dead. Get a live tet in D. !!! + for (j = 0; j < newtetlist->len(); j++) { + recenttet = * (triface *)(* newtetlist)[j]; + if (!isdead(&recenttet)) break; + } + assert(j < newtetlist->len()); + searchtet = recenttet; + } + success = insertsite(pd, &searchtet, false, flipque); + if (success == OUTSIDEPOINT) { + // case (2). Insert p onto CH of D. + inserthullsite(pd, &searchtet, flipque); + } + if (success != DUPLICATEPOINT) { + // p is inserted. Recover Delaunness of D by flips. + flip(flipque, NULL); + } + // Expand C by adding new fronts. The three faces of t which have p as a + // vertex become new fronts. However, if a new front is coincident with + // an old front of C, it is not added and the old front is removed. + adjustedgering(misfront, CCW); + for (j = 0; j < 3; j++) { + // Notice: Below I mis-used the names. 'newfront' is not exactly a new + // front, instead the 'casingtet' should be called new front. + // Get a new front f_n. + fnext(misfront, newfront); + // Get the neighbor tet n at f_n. + sym(newfront, casingtet); + // Is n a cross tet? + if (!infected(casingtet)) { + // f_n becomes a new front of C. + // Does n exist? + if (casingtet.tet == dummytet) { + // Create a fake tet n' to hold f_n temporarily. + maketetrahedron(&faketet); + // Be sure that the vertices of fake tet are CCW oriented. + adjustedgering(newfront, CW); // CW edge ring. + setorg(faketet, org(newfront)); + setdest(faketet, dest(newfront)); + setapex(faketet, apex(newfront)); + setoppo(faketet, (point) NULL); // Indicates it is 'fake'. + // Bond n' to a subface if it exists. + tspivot(newfront, checksh); + if (checksh.sh != dummysh) { + sesymself(checksh); + tsbond(faketet, checksh); + } + // Bond f_n <--> n'. So we're able to find n' and remove it. + bond(faketet, newfront); + frontlist->append(&faketet); + } else { + // Add n to frontlist. + frontlist->append(&casingtet); + } + } else { + // f_n is coincident with an existing front f' of C. f' is no longer + // a front, remove it from frontlist. Use the inverse order to + // search f' (most likely, a newly added front may be f'). + for (k = frontlist->len() - 1; k >= 0; k--) { + searchtet = * (triface *)(* frontlist)[k]; + if ((newfront.tet == searchtet.tet) && + (newfront.loc == searchtet.loc)) { + frontlist->del(k, 0); + break; } } + // Is f_n a subface? + tspivot(newfront, checksh); + if (checksh.sh != dummysh) { + // Temporarily remove checksh. Make it missing. recover it later. + if (b->verbose > 2) { + printf(" Queuing subface (%d, %d, %d).\n", + pointmark(sorg(checksh)), pointmark(sdest(checksh)), + pointmark(sapex(checksh))); + } + tsdissolve(newfront); + tsdissolve(casingtet); + // Detach tets at the both sides of checksh. + stdissolve(checksh); + sesymself(checksh); + stdissolve(checksh); + sinfect(checksh); + missingshqueue->push(&checksh); + } } - regiontetlist->clear(); - regionnum++; // The next region number. + enextself(misfront); + } + // C has been expanded at f. t becomes a cross tet. + if (!infected(misfront)) { + // t will be deleted, queue it. + infect(misfront); + crosstetlist->append(&misfront); } - tetloop.tet = tetrahedrontraverse(); } - // Uninfect all tets. - tetrahedrons->traversalinit(); - tetloop.tet = tetrahedrontraverse(); - while (tetloop.tet != (tetrahedron *) NULL) { -#ifdef SELF_CHECK - assert(infected(tetloop)); -#endif - uninfect(tetloop); - tetloop.tet = tetrahedrontraverse(); + // Loop through misfrontlist, remove infected misfronts. + for (i = 0; i < misfrontlist->len(); i++) { + misfront = * (triface *)(* misfrontlist)[i]; + if (infected(misfront)) { + // Remove f, keep original list order. + misfrontlist->del(i, 1); + i--; + } } - - if (b->verbose) { - printf(" %d regions are numbered.\n", count); + + // Are we done? + if (misfrontlist->len() > 0) { + // No. There are unexpandable fronts. + // expandcavity_sos(misfrontlist); + assert(0); // Not done yet. } - delete regionnumlist; - delete regiontetlist; + // D has been updated (by added new tets or dead tets) (due to flips). + retrievenewtets(newtetlist); + + // Restore global variables. + dummytet[0] = encode(bakhulltet); + hullsize = bakhullsize; + checksubfaces = bakchksub; + b->verbose++; } /////////////////////////////////////////////////////////////////////////////// // // -// carveholes() Find the holes and infect them. Find the volume // -// constraints and infect them. Infect the convex hull. // -// Spread the infection and kill tetrahedra. Spread the // -// volume constraints. // +// carvecavity() Remove redundant (outside) tetrahedra from D. // // // -// This routine mainly calls other routines to carry out all these functions.// +// The fronts of C have been identified in D. Hence C can be tetrahedralized // +// by removing the tets outside C. The CDT is then updated by filling C with // +// the remaining tets (inside C) of D. // +// // +// Each front is protected by an auxilary subface s in D. s has a pointer to // +// f (s.sh[0]). f can be used to classified the in- and out- tets of C (the // +// CW orientation of f faces to the inside of C). The classified out-tets of // +// C are marked (infected) for removing. // +// // +// Notice that the out-tets may not only the tets on the CH of C, but also // +// tets completely inside D, eg., there is a "hole" in D. Such tets must be // +// marked during classification. The hole tets are poped up and removed too. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::carveholes() +void tetgenmesh::carvecavity(list* newtetlist, list* outtetlist, + queue* flipque) { - memorypool *holeviri, *regionviri; - tetrahedron *tptr, **holetet, **regiontet; - triface searchtet, *holetets, *regiontets; - enum locateresult intersect; + triface newtet, neightet, front, outtet; + face auxsh, consh; + point pointptr; + REAL ori; int i; - if (!b->quiet) { - printf("Removing exterior tetrahedra.\n"); - if ((b->verbose > 1) && (in->numberofholes > 0)) { - printf(" Marking holes for elimination.\n"); - } - } - - // Initialize a pool of viri to be used for holes, concavities. - holeviri = new memorypool(sizeof(tetrahedron *), 1024, POINTER, 0); - // Mark as infected any unprotected tetrahedra on the boundary. - infecthull(holeviri); + // Clear work list. + outtetlist->clear(); - if (in->numberofholes > 0) { - // Allocate storage for the tetrahedra in which hole points fall. - holetets = (triface *) new triface[in->numberofholes]; - // Infect each tetrahedron in which a hole lies. - for (i = 0; i < 3 * in->numberofholes; i += 3) { - // Ignore holes that aren't within the bounds of the mesh. - if ((in->holelist[i] >= xmin) && (in->holelist[i] <= xmax) - && (in->holelist[i + 1] >= ymin) - && (in->holelist[i + 1] <= ymax) - && (in->holelist[i + 2] >= zmin) - && (in->holelist[i + 2] <= zmax)) { - searchtet.tet = dummytet; - // Find a tetrahedron that contains the hole. - intersect = locate(&in->holelist[i], &searchtet); - if ((intersect != OUTSIDE) && (!infected(searchtet))) { - // Record the tetrahedron for processing carve hole. - holetets[i / 3] = searchtet; + // Classify in- and out- tets in D. Mark and queue classified out-tets. + for (i = 0; i < newtetlist->len(); i++) { + // Get a new tet t. + newtet = * (triface *)(* newtetlist)[i]; + assert(!isdead(&newtet)); + // Look for aux subfaces attached at t. + for (newtet.loc = 0; newtet.loc < 4; newtet.loc++) { + tspivot(newtet, auxsh); + if (auxsh.sh != dummysh) { + // Has this side a neighbor n? + sym(newtet, neightet); + if (neightet.tet != dummytet) { + // Classify t and n (one is "in" and another is "out"). + // Get the front f. + decode((tetrahedron) auxsh.sh[0], front); + // Let f face to the inside of C. + adjustedgering(front, CW); + ori = orient3d(org(front), dest(front), apex(front), oppo(newtet)); + assert(ori != 0.0); + if (ori < 0.0) { + // t is in-tet. n is out-tet. + outtet = neightet; + } else { + // n is in-tet. t is out-tet. + outtet = newtet; + } + // Add the out-tet into list. + if (!infected(outtet)) { + infect(outtet); + outtetlist->append(&outtet); + } } } } - // Infect the hole tetrahedron. This is done by marking the tet as - // infected and including the tetrahedron in the virus pool. - for (i = 0; i < in->numberofholes; i++) { - infect(holetets[i]); - holetet = (tetrahedron **) holeviri->alloc(); - *holetet = holetets[i].tet; - } - // Free up memory. - delete [] holetets; } - // Mark as infected all tets of the holes and concavities. - plague(holeviri); - // The virus pool contains all outside tets now. - - // Is -A switch in use. - if (b->regionattrib) { - // Assign every tetrahedron a regional attribute of zero. - tetrahedrons->traversalinit(); - tptr = tetrahedrontraverse(); - while (tptr != (tetrahedron *) NULL) { - setelemattribute(tptr, in->numberoftetrahedronattributes, 0.0); - tptr = tetrahedrontraverse(); + // Find and mark all out-tets. + for (i = 0; i < outtetlist->len(); i++) { + outtet = * (triface *)(* outtetlist)[i]; + for (outtet.loc = 0; outtet.loc < 4; outtet.loc++) { + sym(outtet, neightet); + // Does the neighbor exist and unmarked? + if ((neightet.tet != dummytet) && !infected(neightet)) { + // Is it protected by an aux subface? + tspivot(outtet, auxsh); + if (auxsh.sh == dummysh) { + // It's an out-tet. + infect(neightet); + outtetlist->append(&neightet); + } + } } } - if (in->numberofregions > 0) { - if (b->verbose > 1) { - if (b->regionattrib) { - if (b->varvolume) { - printf("Spreading regional attributes and volume constraints.\n"); - } else { - printf("Spreading regional attributes.\n"); - } - } else { - printf("Spreading regional volume constraints.\n"); + // Remove the out- (and hole) tets. + for (i = 0; i < outtetlist->len(); i++) { + // Get an out-tet t. + outtet = * (triface *)(* outtetlist)[i]; + // Detach t from the in-tets. + for (outtet.loc = 0; outtet.loc < 4; outtet.loc++) { + // Is there an aux subface s? + tspivot(outtet, auxsh); + if (auxsh.sh != dummysh) { + // Get the neighbor n. + sym(outtet, neightet); + assert(!infected(neightet)); // t must be in-tet. + // Detach n -x-> t. + dissolve(neightet); } } - // Allocate storage for the tetrahedra in which region points fall. - regiontets = (triface *) new triface[in->numberofregions]; - // Find the starting tetrahedron for each region. - for (i = 0; i < in->numberofregions; i++) { - regiontets[i].tet = dummytet; - // Ignore region points that aren't within the bounds of the mesh. - if ((in->regionlist[5 * i] >= xmin) - && (in->regionlist[5 * i] <= xmax) - && (in->regionlist[5 * i + 1] >= ymin) - && (in->regionlist[5 * i + 1] <= ymax) - && (in->regionlist[5 * i + 2] >= zmin) - && (in->regionlist[5 * i + 2] <= zmax)) { - searchtet.tet = dummytet; - // Find a tetrahedron that contains the region point. - intersect = locate(&in->regionlist[5 * i], &searchtet); - if ((intersect != OUTSIDE) && (!infected(searchtet))) { - // Record the tetrahedron for processing after the - // holes have been carved. - regiontets[i] = searchtet; + // Dealloc the tet. + tetrahedrondealloc(outtet.tet); + } + + // Connect the in-tets of C to fronts. Remove aux subfaces and fake tets. + for (i = 0; i < newtetlist->len(); i++) { + // Get a new tet t. + newtet = * (triface *)(* newtetlist)[i]; + // t may be an out-tet and has got deleted. + if (isdead(&newtet)) continue; + // t is an in-tet. Look for aux subfaces attached at t. + for (newtet.loc = 0; newtet.loc < 4; newtet.loc++) { + // Is there an aux subface s? + tspivot(newtet, auxsh); + if (auxsh.sh != dummysh) { + // Get the front f. + decode((tetrahedron) auxsh.sh[0], front); + assert((front.tet != dummytet) && !infected(front)); + // s has fulfilled its duty. Can be deleted. + tsdissolve(newtet); // dissolve: t -x-> s. + // Delete s. + shellfacedealloc(subfaces, auxsh.sh); + // Connect the newtet t and front f. + // Is there a concrete subface c at f. + tspivot(front, consh); + if (consh.sh != dummysh) { + sesymself(consh); + // Bond: t <--> c. + tsbond(newtet, consh); } - } - } - // Initialize a pool to be used for regional attrs, and/or regional - // volume constraints. - regionviri = new memorypool(sizeof(tetrahedron *), 1024, POINTER, 0); - // Find and set all regions. - for (i = 0; i < in->numberofregions; i++) { - if (regiontets[i].tet != dummytet) { - // Make sure the tetrahedron under consideration still exists. - // It may have been eaten by the virus. - if (!isdead(&(regiontets[i]))) { - // Put one tetrahedron in the virus pool. - infect(regiontets[i]); - regiontet = (tetrahedron **) regionviri->alloc(); - *regiontet = regiontets[i].tet; - // Apply one region's attribute and/or volume constraint. - regionplague(regionviri, in->regionlist[5 * i + 3], - in->regionlist[5 * i + 4]); - // The virus pool should be empty now. + // Does f hold by a fake tet. + if (oppo(front) == (point) NULL) { + // f is fake. + if (consh.sh != dummysh) { + sesymself(consh); + // Dissolve: c -x-> f. + stdissolve(consh); + } + // Dealloc f. + tetrahedrondealloc(front.tet); + // f becomes a hull. let 'dummytet' bond to it. + dummytet[0] = encode(newtet); + } else { + // Bond t <--> f. + bond(newtet, front); + } + // t may be non-locally Delaunay and flipable. + if (flipque != (queue *) NULL) { + enqueueflipface(newtet, flipque); } } } - // Free up memory. - delete [] regiontets; - delete regionviri; - } - - // Now acutually remove the outside and hole tets. - removeholetets(holeviri); - // The mesh is nonconvex now. - nonconvex = 1; - - // Update the point-to-tet map. - makepoint2tetmap(); - - if (b->regionattrib) { - if (b->regionattrib > 1) { - // -AA switch. Assign each tet a region number (> 0). - assignregionattribs(); - } - // Note the fact that each tetrahedron has an additional attribute. - in->numberoftetrahedronattributes++; + // Let the corners of t2 point to it for fast searching. + pointptr = org(newtet); + setpoint2tet(pointptr, encode(newtet)); + pointptr = dest(newtet); + setpoint2tet(pointptr, encode(newtet)); + pointptr = apex(newtet); + setpoint2tet(pointptr, encode(newtet)); + pointptr = oppo(newtet); + setpoint2tet(pointptr, encode(newtet)); } - - // Free up memory. - delete holeviri; + // The cavity has been re-tetrahedralized. } -//// //// -//// //// -//// constrained_cxx ////////////////////////////////////////////////////////// - -//// steiner_cxx ////////////////////////////////////////////////////////////// -//// //// -//// //// - /////////////////////////////////////////////////////////////////////////////// // // -// initializecavity() Initialize the cavity. // -// // -// A cavity C is bounded by a list of faces, called fronts. Each front f is // -// hold by a tet t adjacent to C, t is not in C (uninfected). If f is a hull // -// face, t does't exist, a fake tet t' is created to hold f. t' has the same // -// vertices as f but no opposite. t' will be removed automatically after C // -// is filled with new tets (by carvecavity()). // +// delaunizecavity() Tetrahedralize a cavity by Delaunay tetrahedra. // // // -// The faces of C are given in two lists. 'floorlist' is a set of subfaces, // -// each subface has been oriented to face to the inside of C. 'ceillist' is // -// a set of tetrahedral faces. 'frontlist' returns the initialized fronts. // +// The cavity C is bounded by a set of triangles in 'floorlist' (a list of // +// coplanar subfaces) and 'ceillist' (a list of tetrahedral faces lie above // +// the subfaces). 'floorptlist' and 'ceilptlist' are the vertices of C. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::initializecavity(list* floorlist, list* ceillist, - list* frontlist, list *ptlist, list* glueshlist) +void tetgenmesh::delaunizecavity(list* floorlist, list* ceillist, + list* ceilptlist, list* floorptlist, list* frontlist, list* misfrontlist, + list* newtetlist, list* crosstetlist, queue* missingshqueue, queue* flipque) { - triface neightet, casingtet; - triface faketet; - face worksh; - point *ppt; - int i, j; + int vertnum; - // Infect all points of the re-triangulated cavity. - for (i = 0; i < ptlist->len(); i++) { - ppt = (point *)(* ptlist)[i]; - pinfect(*ppt); + vertnum = floorptlist->len(); + vertnum += (ceilptlist != (list *) NULL ? ceilptlist->len() : 0); + if (b->verbose > 1) { + printf(" Delaunizing cavity (%d floors, %d ceilings, %d vertices).\n", + floorlist->len(), ceillist->len(), vertnum); } - - // Initialize subfaces of C. - for (i = 0; i < floorlist->len(); i++) { - // Get a subface s. - worksh = * (face *)(* floorlist)[i]; -#ifdef SELF_CHECK - // Current side of s should be empty. - stpivot(worksh, neightet); - assert(neightet.tet == dummytet); -#endif - // Do not insert it if some of its vertices are not in Mesh. - ppt = (point *) &(worksh.sh[3]); - for (j = 0; j < 3; j++) { - if (!pinfected(ppt[j])) break; - } - if (j < 3) { - // Found a subface lies outside the cavity. See an example in - // dump-SteinerRemoval-case2.lua. - // Add this subface in glueshlist (to process it later). - glueshlist->append(&worksh); - // Do not add this face into frontlist. - continue; - } - // Get the adjacent tet t. - sesymself(worksh); - stpivot(worksh, casingtet); - // Does t exist? - if (casingtet.tet == dummytet) { - // Create a fake tet t' to hold f temporarily. - maketetrahedron(&faketet); - setorg(faketet, sorg(worksh)); - setdest(faketet, sdest(worksh)); - setapex(faketet, sapex(worksh)); - setoppo(faketet, (point) NULL); // Indicates it is 'fake'. - tsbond(faketet, worksh); - frontlist->append(&faketet); - } else { - frontlist->append(&casingtet); - } + // Save the size of the largest cavity. + if ((floorlist->len() + ceillist->len()) > maxcavfaces) { + maxcavfaces = floorlist->len() + ceillist->len(); } - // Initialize tet faces of C. - for (i = 0; i < ceillist->len(); i++) { - // Get a tet face c. - neightet = * (triface *) (* ceillist)[i]; -#ifdef SELF_CHECK - // The tet of c must be inside C (going to be deleted). - assert(infected(neightet)); -#endif - // Get the adjacent tet t. - sym(neightet, casingtet); - // Does t exist? - if (casingtet.tet == dummytet) { - // No. Create a fake tet t' to hold f temporarily. - maketetrahedron(&faketet); - // Be sure that the vertices of t' are CCW oriented. - adjustedgering(neightet, CW); // CW edge ring. - setorg(faketet, org(neightet)); - setdest(faketet, dest(neightet)); - setapex(faketet, apex(neightet)); - setoppo(faketet, (point) NULL); // Indicates it is 'fake'. - // Bond t' to a subface if it exists. - tspivot(neightet, worksh); - if (worksh.sh != dummysh) { - sesymself(worksh); - tsbond(faketet, worksh); - } - // Bond c <--> t'. So we're able to find t' and remove it. - bond(faketet, neightet); - // c may become uninfected due to the bond(). - infect(neightet); - frontlist->append(&faketet); - } else { - frontlist->append(&casingtet); - } + if (vertnum > maxcavverts) { + maxcavverts = vertnum; } - // Uninfect all points of the re-triangulated cavity. - for (i = 0; i < ptlist->len(); i++) { - ppt = (point *)(* ptlist)[i]; - puninfect(*ppt); + // Clear these lists. + frontlist->clear(); + misfrontlist->clear(); + newtetlist->clear(); + + // Initialize the cavity C. + initializecavity(floorlist, ceillist, frontlist); + // Form the D of the vertices of C. + delaunizecavvertices(NULL, floorptlist, ceilptlist, newtetlist, flipque); + // Identify faces of C in D. + while (!identifyfronts(frontlist, misfrontlist, newtetlist)) { + // Remove protecting subfaces, keep new tets. + detachauxsubfaces(newtetlist); + // Expand C and updateing D. + expandcavity(frontlist, misfrontlist, newtetlist, crosstetlist, + missingshqueue, flipque); } + // All fronts have identified in D. Get the shape of C by removing out + // tets of C. 'misfrontlist' is reused for removing out tets. + carvecavity(newtetlist, misfrontlist, NULL); } /////////////////////////////////////////////////////////////////////////////// // // -// delaunizecavvertices() Form a DT of the vertices of a cavity. // +// formmissingregion() Form the missing region. // // // -// 'floorptlist' and 'ceilptlist' are the vertices of the cavity. // +// 'missingsh' is a missing subface. Start from it we can form the missing // +// region R (a set of connected missing subfaces). Because all missing sub- // +// faces have been marked (infected) before. R can be formed by checking the // +// neighbors of 'missingsh', and the neighbors of the neighbors, and so on. // +// Stop checking further at either a segment or an unmarked subface. // // // -// The tets of the DT are created directly in the pool 'tetrahedrons', i.e., // -// no auxiliary data structure and memory are required. The trick is at the // -// time they're created, there are no connections between them to the other // -// tets in the pool. You can imagine they form an ioslated island. // +// 'missingshlist' returns R. The edge ring of subfaces of R are oriented in // +// the same direction. 'equatptlist' returns the vertices of R, each vertex // +// is marked with '1' (in 'worklist'). // // // /////////////////////////////////////////////////////////////////////////////// -bool tetgenmesh::delaunizecavvertices(triface* oldtet, list* floorptlist, - list* ceilptlist, list* newtetlist, queue* flipque) +void tetgenmesh::formmissingregion(face* missingsh, list* missingshlist, + list* equatptlist, int* worklist) { - point *insertarray; - triface bakhulltet, newtet; - long bakhullsize; - long arraysize; - bool success; - int bakchksub; - int i, j; + face neighsh, worksh, workseg; + point workpt[3]; + int idx, i, j; - // Prepare the array of points for inserting. - arraysize = floorptlist->len(); - if (ceilptlist != (list *) NULL) { - arraysize += ceilptlist->len(); - } - insertarray = new point[arraysize]; - for (i = 0; i < floorptlist->len(); i++) { - insertarray[i] = * (point *)(* floorptlist)[i]; + // Add 'missingsh' into 'missingshlist'. + missingshlist->append(missingsh); + // Save and mark its three vertices. + workpt[0] = sorg(*missingsh); + workpt[1] = sdest(*missingsh); + workpt[2] = sapex(*missingsh); + for (i = 0; i < 3; i++) { + idx = pointmark(workpt[i]) - in->firstnumber; + worklist[idx] = 1; + equatptlist->append(&workpt[i]); } - if (ceilptlist != (list *) NULL) { - for (j = 0; j < ceilptlist->len(); j++) { - insertarray[i + j] = * (point *)(* ceilptlist)[j]; + // Temporarily uninfect it (avoid to save it again). + suninfect(*missingsh); + + // Find the other missing subfaces. + for (i = 0; i < missingshlist->len(); i++) { + // Get a missing subface. + worksh = * (face *)(* missingshlist)[i]; + // Check three neighbors of this face. + for (j = 0; j < 3; j++) { + sspivot(worksh, workseg); + if (workseg.sh == dummysh) { + spivot(worksh, neighsh); + if (sinfected(neighsh)) { + // Find a missing subface, adjust the face orientation. + if (sorg(neighsh) != sdest(worksh)) { + sesymself(neighsh); + } + if (b->verbose > 2) { + printf(" Add missing subface (%d, %d, %d).\n", + pointmark(sorg(neighsh)), pointmark(sdest(neighsh)), + pointmark(sapex(neighsh))); + } + missingshlist->append(&neighsh); + // Save and mark its apex. + workpt[0] = sapex(neighsh); + idx = pointmark(workpt[0]) - in->firstnumber; + // Has workpt[0] been added? + if (worklist[idx] == 0) { + worklist[idx] = 1; + equatptlist->append(&workpt[0]); + } + // Temporarily uninfect it (avoid to save it again). + suninfect(neighsh); + } + } + senextself(worksh); } } - // The incrflipdelaunay() is re-used. Backup global variables. - decode(dummytet[0], bakhulltet); - bakhullsize = hullsize; - bakchksub = checksubfaces; - checksubfaces = 0; - b->verbose--; - - // Form the DT by incremental flip Delaunay algorithm. Do not jump for - // point location, do not merge points. - success = incrflipdelaunay(oldtet, insertarray, arraysize, false, false, - 0.0, flipque); - - delete [] insertarray; - - if (success) { - // Get a tet in D. - decode(dummytet[0], newtet); - newtetlist->append(&newtet); - // Get all tets of D. - retrievenewtets(newtetlist); - } - - // Restore global variables. - dummytet[0] = encode(bakhulltet); - hullsize = bakhullsize; - checksubfaces = bakchksub; - b->verbose++; - - return success; + // R has been formed. Infect missing subfaces again. + for (i = 0; i < missingshlist->len(); i++) { + worksh = * (face *)(* missingshlist)[i]; + sinfect(worksh); + } } /////////////////////////////////////////////////////////////////////////////// // // -// retrievenewtets() Retrieve the newly created tets. // +// rearrangesubfaces() Rearrange the set of subfaces of a missing region // +// so that they conform to the faces of DT. // // // -// On input, 'newtetlist' contains at least one alive new tet. From this tet,// -// other new tets can be found by a broadth-first searching. // +// The missing region formed by subfaces of 'missingshlist' contains a set // +// of degenerate vertices, hence the set of subfaces don't match the set of // +// faces in DT. Instead of forcing them to present in DT, we re-arrange the // +// connection of them so that the new subfaces conform to the faces of DT. // +// 'boundedgelist' is a set of boundary edges of the region, these edges(may // +// be subsegments) must exist in DT. // +// // +// On completion, we have created and inserted a set of new subfaces which // +// conform to faces of DT. The set of old subfaces in 'missingshlist' are // +// deleted. The region vertices in 'equatptlist' are unmarked. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::retrievenewtets(list* newtetlist) +void tetgenmesh::rearrangesubfaces(list* missingshlist, list* boundedgelist, + list* equatptlist, int* worklist) { - triface searchtet, casingtet; - int i; + link *boundedgelink; + link *newshlink; + triface starttet, spintet, neightet, worktet; + face shloop, newsh, neighsh, spinsh, worksh; + face workseg, casingin, casingout; + point torg, tdest, workpt; + point spt1, spt2, spt3; + enum finddirectionresult collinear; + enum shestype shtype; + REAL area; + bool matchflag, finishflag; + int shmark, pbcgp, idx, hitbdry; + int i, j; - // There may be dead tets due to flip32(). Delete them first. - for (i = 0; i < newtetlist->len(); i++) { - searchtet = * (triface *)(* newtetlist)[i]; - if (isdead(&searchtet)) { - newtetlist->del(i, 0); i--; - continue; + // Initialize the boundary edge link. + boundedgelink = new link(sizeof(face), NULL, 256); + // Initialize the new subface link. + newshlink = new link(sizeof(face), NULL, 256); + // Remember the type (skinny or not) of replaced subfaces. They should + // all have the same type since there is no segment inside the region. + worksh = * (face *)(* missingshlist)[0]; + shtype = shelltype(worksh); + // The following loop is only for checking purpose. + for (i = 1; i < missingshlist->len(); i++) { + worksh = * (face *)(* missingshlist)[i]; + assert(shelltype(worksh) == shtype); + } + // To avoid compilation warnings. + shmark = pbcgp = 0; + area = 0.0; + + // Create an initial boundary link. + for (i = 0; i < boundedgelist->len(); i++) { + shloop = * (face *)(* boundedgelist)[i]; + if (i == 0) { + // 'shmark' will be set to all new created subfaces. + shmark = shellmark(shloop); + if (b->quality && varconstraint) { + // area will be copied to all new created subfaces. + area = areabound(shloop); + } + if (checkpbcs) { + // pbcgp will be copied to all new created subfaces. + pbcgp = shellpbcgroup(shloop); + } + // Get the abovepoint of this facet. + abovepoint = facetabovepointarray[shellmark(shloop)]; + if (abovepoint == (point) NULL) { + getfacetabovepoint(&shloop); + } } - infect(searchtet); - } - // It is possible that all tets are deleted. Check it. 2009-07-27. - if (newtetlist->len() == 0) { - // We must add a live tet to the list for the retrieving. - decode(dummytet[0], searchtet); - assert(searchtet.tet != dummytet); - assert(!isdead(&searchtet)); - infect(searchtet); - newtetlist->append(&searchtet); - } - // Find all new tets. - for (i = 0; i < newtetlist->len(); i++) { - searchtet = * (triface *)(* newtetlist)[i]; - for (searchtet.loc = 0; searchtet.loc < 4; searchtet.loc++) { - sym(searchtet, casingtet); - if ((casingtet.tet != dummytet) && !infected(casingtet)) { - infect(casingtet); - newtetlist->append(&casingtet); + sspivot(shloop, workseg); + if (workseg.sh == dummysh) { + // This edge is an interior edge. + spivot(shloop, neighsh); + boundedgelink->add(&neighsh); + } else { + // This side has a segment, the edge exists. + boundedgelink->add(&shloop); + } + } + + // Each edge ab of boundedgelink will be finished by finding a vertex c + // which is a vertex of the missing region, such that: + // (1) abc is inside the missing region, i.e., abc intersects at least + // one of missing subfaces (saved in missingshlist); + // (2) abc is not intersect with any previously created new subfaces + // in the missing region (saved in newshlink). + // After abc is created, it will be inserted into both the surface mesh + // and the DT. The boundedgelink will be updated, ab is removed, bc and + // ca will be added if they are open. + + while (boundedgelink->len() > 0) { + // Remove an edge (ab) from the link. + shloop = * (face *) boundedgelink->del(1); + // 'workseg' indicates it is a segment or not. + sspivot(shloop, workseg); + torg = sorg(shloop); // torg = a; + tdest = sdest(shloop); // tdest = b; + // Find a tetrahedron containing ab. + getsearchtet(torg, tdest, &starttet, &workpt); + collinear = finddirection(&starttet, workpt, tetrahedrons->items); + if (collinear == LEFTCOLLINEAR) { + enext2self(starttet); + esymself(starttet); + } else if (collinear == TOPCOLLINEAR) { + fnextself(starttet); + enext2self(starttet); + esymself(starttet); + } + assert(dest(starttet) == workpt); + // Checking faces around ab until a valid face is found. + matchflag = false; + spintet = starttet; + hitbdry = 0; + do { + workpt = apex(spintet); + idx = pointmark(workpt) - in->firstnumber; + if (worklist[idx] == 1) { + // (trog, tdest, workpt) is on the facet. Check if it satisfies (1). + finishflag = false; + for (i = 0; i < missingshlist->len(); i++) { + worksh = * (face *)(* missingshlist)[i]; + spt1 = sorg(worksh); + spt2 = sdest(worksh); + spt3 = sapex(worksh); + // Does bc intersect the face? + if (tri_edge_cop_inter(spt1, spt2, spt3, tdest, workpt, abovepoint) + == INTERSECT) { + finishflag = true; break; + } + // Does ca intersect the face? + if (tri_edge_cop_inter(spt1, spt2, spt3, workpt, torg, abovepoint) + == INTERSECT) { + finishflag = true; break; + } + // Does c inside the face? + if (tri_vert_cop_inter(spt1, spt2, spt3, workpt, abovepoint) + == INTERSECT) { + finishflag = true; break; + } + } + if (finishflag) { + // Satisfying (1). Check if it satisfies (2). + matchflag = true; + for (i = 0; i < newshlink->len() && matchflag; i++) { + worksh = * (face *) newshlink->getnitem(i + 1); + spt1 = sorg(worksh); + spt2 = sdest(worksh); + spt3 = sapex(worksh); + // Does bc intersect the face? + if (tri_edge_cop_inter(spt1, spt2, spt3, tdest, workpt, abovepoint) + == INTERSECT) { + matchflag = false; break; + } + // Does ca intersect the face? + if (tri_edge_cop_inter(spt1, spt2, spt3, workpt, torg, abovepoint) + == INTERSECT) { + matchflag = false; break; + } + // Does c inside the face? + if (tri_vert_cop_inter(spt1, spt2, spt3, workpt, abovepoint) + == INTERSECT) { + matchflag = false; break; + } + } + } + if (matchflag == true) { + // Satisfying both (1) and (2). Find abc. + break; + } + } + if (!fnextself(spintet)) { + hitbdry ++; + if (hitbdry < 2) { + esym(starttet, spintet); + if (!fnextself(spintet)) { + hitbdry ++; + } + } + } + } while (hitbdry < 2 && apex(spintet) != apex(starttet)); + assert(matchflag == true); + tspivot(spintet, neighsh); + if (neighsh.sh != dummysh) { + printf("Error: Invalid PLC.\n"); + printf(" Facet #%d and facet #%d overlap each other.\n", + shellmark(neighsh), shellmark(shloop)); + printf(" It might be caused by a facet is defined more than once.\n"); + printf(" Hint: Use -d switch to find all overlapping facets.\n"); + exit(1); + } + // The side of 'spintet' is at which a new subface will be attached. + adjustedgering(spintet, CCW); + // Create the new subface. + makeshellface(subfaces, &newsh); + setsorg(newsh, org(spintet)); + setsdest(newsh, dest(spintet)); + setsapex(newsh, apex(spintet)); + if (b->quality && varconstraint) { + setareabound(newsh, area); + } + if (checkpbcs) { + setshellpbcgroup(newsh, pbcgp); + } + setshellmark(newsh, shmark); + setshelltype(newsh, shtype); // It may be a skinny subface. + // Add newsh into newshlink for intersecting checking. + newshlink->add(&newsh); + // Insert it into the current mesh. + tsbond(spintet, newsh); + sym(spintet, neightet); + if (neightet.tet != dummytet) { + sesym(newsh, neighsh); + tsbond(neightet, neighsh); + } + // Insert it into the surface mesh. + sspivot(shloop, workseg); + if (workseg.sh == dummysh) { + sbond(shloop, newsh); + } else { + // There is a subsegment, 'shloop' is the subface which is going to + // die. Insert the 'newsh' at the place of 'shloop' into its face + // link, so as to dettach 'shloop'. The original connection is: + // -> casingin -> shloop -> casingout ->, it will be changed with: + // -> casingin -> newsh -> casingout ->. Pay attention to the + // case when this subsegment is dangling in the mesh, i.e., 'shloop' + // is bonded to itself. + spivot(shloop, casingout); + if (shloop.sh != casingout.sh) { + // 'shloop' is not bonded to itself. + spinsh = casingout; + do { + casingin = spinsh; + spivotself(spinsh); + } while (sapex(spinsh) != sapex(shloop)); + assert(casingin.sh != shloop.sh); + // Bond casingin -> newsh -> casingout. + sbond1(casingin, newsh); + sbond1(newsh, casingout); + } else { + // Bond newsh -> newsh. + sbond(newsh, newsh); + } + // Bond the segment. + ssbond(newsh, workseg); + } + // Check other two sides of this new subface. If a side is not bonded + // to any edge in the link, it will be added to the link. + for (i = 0; i < 2; i++) { + if (i == 0) { + senext(newsh, worksh); + } else { + senext2(newsh, worksh); + } + torg = sorg(worksh); + tdest = sdest(worksh); + finishflag = false; + for (j = 0; j < boundedgelink->len() && !finishflag; j++) { + neighsh = * (face *) boundedgelink->getnitem(j + 1); + if ((sorg(neighsh) == torg && sdest(neighsh) == tdest) || + (sorg(neighsh) == tdest && sdest(neighsh) == torg)) { + // Find a boundary edge. Bond them and exit the loop. + sspivot(neighsh, workseg); + if (workseg.sh == dummysh) { + sbond(neighsh, worksh); + } else { + // There is a subsegment, 'neighsh' is the subface which is + // going to die. Do the same as above for 'worksh'. + spivot(neighsh, casingout); + if (neighsh.sh != casingout.sh) { + // 'neighsh' is not bonded to itself. + spinsh = casingout; + do { + casingin = spinsh; + spivotself(spinsh); + } while (sapex(spinsh) != sapex(neighsh)); + assert(casingin.sh != neighsh.sh); + // Bond casingin -> worksh -> casingout. + sbond1(casingin, worksh); + sbond1(worksh, casingout); + } else { + // Bond worksh -> worksh. + sbond(worksh, worksh); + } + // Bond the segment. + ssbond(worksh, workseg); + } + // Remove this boundary edge from the link. + boundedgelink->del(j + 1); + finishflag = true; + } + } + if (!finishflag) { + // It's a new boundary edge, add it to link. + boundedgelink->add(&worksh); } } } - // Uninfect new tets. - for (i = 0; i < newtetlist->len(); i++) { - searchtet = * (triface *)(* newtetlist)[i]; - uninfect(searchtet); + + // Deallocate the set of old missing subfaces. + for (i = 0; i < missingshlist->len(); i++) { + worksh = * (face *)(* missingshlist)[i]; + shellfacedealloc(subfaces, worksh.sh); + } + // Unmark region vertices in 'worklist'. + for (i = 0; i < equatptlist->len(); i++) { + workpt = * (point *)(* equatptlist)[i]; + idx = pointmark(workpt) - in->firstnumber; + worklist[idx] = 0; } + + delete boundedgelink; + delete newshlink; } /////////////////////////////////////////////////////////////////////////////// // // -// insertauxsubface() Fix an auxilary subface in place. // +// scoutcrossingedge() Search an edge crossing the missing region. // // // -// An auxilary subface s is fixed in D as it is a real subface, but s has no // -// vertices and neighbors. It has two uses: (1) it protects an identfied // -// front f in D; (2) it serves the link to bond a tet in C and f later. The // -// first neighbor of s (s->sh[0]) stores a pointer to f. // +// 'missingshlist' forms the missing region R. This routine searches for an // +// edge crossing R. It first forms a 'boundedgelist' consisting of the // +// boundary edges of R. Such edges are existing in CDT. A crossing edge is // +// found by rotating faces around one of the boundary edges. It is possible // +// there is no edge crosses R (e.g. R has a degenerate point set). // // // -// 'front' is a front f of C. idfront' t is a tet in D where f is identified // -// be a face of it. s will be fixed between t and its neighbor. // +// If find a croosing edge, return TRUE, 'crossedgelist' contains this edge. // +// Otherwise, return FALSE. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::insertauxsubface(triface* front, triface* idfront) +bool tetgenmesh::scoutcrossingedge(list* missingshlist, list* boundedgelist, + list* crossedgelist, int* worklist) { - triface neightet; - face auxsh; + triface starttet, spintet, worktet; + face startsh, neighsh, worksh, workseg; + point torg, tdest, tapex; + point workpt[3], pa, pb, pc; + enum finddirectionresult collinear; + REAL ori1, ori2; + bool crossflag; + int hitbdry; + int i, j, k; - // Create the aux subface s. - makeshellface(subfaces, &auxsh); - // Bond s <--> t. - tsbond(*idfront, auxsh); - // Does t's neighbor n exist? - sym(*idfront, neightet); - if (neightet.tet != dummytet) { - // Bond s <--> n. - sesymself(auxsh); - tsbond(neightet, auxsh); + // Form the 'boundedgelist'. Loop through 'missingshlist', check each + // edge of these subfaces. If an edge is a segment or the neighbor + // subface is uninfected, add it to 'boundedgelist'. + for (i = 0; i < missingshlist->len(); i++) { + worksh = * (face *)(* missingshlist)[i]; + for (j = 0; j < 3; j++) { + sspivot(worksh, workseg); + if (workseg.sh == dummysh) { + spivot(worksh, neighsh); + if (!sinfected(neighsh)) { + boundedgelist->append(&worksh); + } + } else { + boundedgelist->append(&worksh); + } + senextself(worksh); + } + } + + crossflag = false; + // Find a crossing edge. It is possible there is no such edge. We need to + // loop through all edges of 'boundedgelist' for sure we don't miss any. + for (i = 0; i < boundedgelist->len() && !crossflag; i++) { + startsh = * (face *)(* boundedgelist)[i]; + // 'startsh' (abc) holds an existing edge of the DT, find it. + torg = sorg(startsh); + tdest = sdest(startsh); + tapex = sapex(startsh); + getsearchtet(torg, tdest, &starttet, &workpt[0]); + collinear = finddirection(&starttet, workpt[0], tetrahedrons->items); + if (collinear == LEFTCOLLINEAR) { + enext2self(starttet); + esymself(starttet); + } else if (collinear == TOPCOLLINEAR) { + fnextself(starttet); + enext2self(starttet); + esymself(starttet); + } +#ifdef SELF_CHECK + assert(dest(starttet) == workpt[0]); +#endif + // Now starttet holds edge ab. Find is edge de crossing R. + spintet = starttet; + hitbdry = 0; + do { + if (fnextself(spintet)) { + // splittet = abde. Check if de crosses abc. + workpt[1] = apex(spintet); // workpt[1] = d. + workpt[2] = oppo(spintet); // workpt[2] = e. + j = pointmark(workpt[1]) - in->firstnumber; + k = pointmark(workpt[2]) - in->firstnumber; + if (worklist[j] == 1) { + ori1 = 0.0; // d is a vertex of the missing region. + } else { + // Get the orientation of d wrt. abc. + ori1 = orient3d(torg, tdest, tapex, workpt[1]); + } + if (worklist[k] == 1) { + ori2 = 0.0; // e is a vertex of the missing region. + } else { + // Get the orientation of e wrt. abc. + ori2 = orient3d(torg, tdest, tapex, workpt[2]); + } + // Only do check if d and e locate on different sides of abc. + if (ori1 * ori2 < 0.0) { + // Check if de crosses any subface of R. + for (j = 0; j < missingshlist->len(); j++) { + worksh = * (face *)(* missingshlist)[j]; + pa = sorg(worksh); + pb = sdest(worksh); + pc = sapex(worksh); + crossflag = (tri_tri_inter(pa, pb, pc, workpt[0], workpt[1], + workpt[2]) == INTERSECT); + if (crossflag) { + // Find a crossing edge. We're done. + worktet = spintet; + adjustedgering(worktet, CCW); + enextfnextself(worktet); + enextself(worktet); + // Add this edge (worktet) into 'crossedgelist'. + crossedgelist->append(&worktet); + break; + } + } + if (crossflag) break; + } + if (apex(spintet) == apex(starttet)) break; + } else { + hitbdry++; + // It is only possible to hit boundary once. + if (hitbdry < 2) { + esym(starttet, spintet); + } + } + } while (hitbdry < 2); } - // Let s remember f. - auxsh.sh[0] = (shellface) encode(*front); + + return crossflag; } /////////////////////////////////////////////////////////////////////////////// // // -// scoutfront() Scout a face in D. // +// formcavity() Form the cavity for recovering the missing region. // // // -// Search a 'front' f in D. If f is found, return TRUE and the face of D is // -// returned in 'idfront'. Otherwise, return FALSE. // +// The cavity C is bounded by faces of current CDT. All tetrahedra inside C // +// will be removed, intead a set of constrained Delaunay tetrahedra will be // +// filled in and the missing region are recovered. // +// // +// 'missingshlist' contains a set of subfaces forming the missing region R. // +// C is formed by first finding all the tetrahedra in CDT that intersect the // +// relative interior of R; then deleting them from the CDT, this will form C // +// inside the CDT. At the beginning, 'crossedgelist' contains an edge which // +// is crossing R. All tets containing this edge must cross R. Start from it, // +// other crossing edges can be found incrementally. The discovered crossing // +// tets are saved in 'crosstetlist'. // +// // +// Notice that not all tets in 'crosstetlist' are crossing R. The discovered // +// tets are connected each other. However, there may be other tets crossing // +// R but disjoint with the found tets. Due to this fact we need to check the // +// 'missingshlist' once more. Only recover those subfaces which are crossed // +// by the set of discovered tets, i.e., R may be shrinked to conform the set // +// of discovered tets. The extra subfaces of R will be recovered later. // +// // +// Notice that some previous recovered subfaces may completely included in C.// +// This can happen when R is very big and these subfaces lie above R and so // +// close to it. Such subfaces have to be queued (and sinfected()) to recover // +// them later. Otherwise, we lost the connection to these subfaces forever. // // // /////////////////////////////////////////////////////////////////////////////// -bool tetgenmesh::scoutfront(triface* front, triface* idfront) +void tetgenmesh::formcavity(list* missingshlist, list* crossedgelist, + list* equatptlist, list* crossshlist, list* crosstetlist, + list* belowfacelist, list* abovefacelist, list* horizptlist, + list* belowptlist, list* aboveptlist, queue* missingshqueue, int* worklist) { - triface spintet; - face checksh; // For debug. - point pa, pb, pc; - enum finddirectionresult col; - int hitbdry; - - // Let the front we're searching is abc. - pa = org(*front); - pb = dest(*front); + triface starttet, spintet, neightet, worktet; + face startsh, neighsh, worksh, workseg; + point torg, tdest, tapex, workpt[3]; + REAL checksign, orgori, destori; + bool crossflag, inlistflag; + bool belowflag, aboveflag; + int idx, share; + int i, j, k; - point2tetorg(pa, *idfront); - assert(org(*idfront) == pa); - recenttet = *idfront; + // Get a face at horizon. + startsh = * (face *)(* missingshlist)[0]; + torg = sorg(startsh); + tdest = sdest(startsh); + tapex = sapex(startsh); + + // Collect the set of crossing tetrahedra by rotating crossing edges. + for (i = 0; i < crossedgelist->len(); i++) { + // Get a tet abcd, ab is a crossing edge. + starttet = * (triface *)(* crossedgelist)[i]; + adjustedgering(starttet, CCW); + if (b->verbose > 2) { + printf(" Collect tets containing edge (%d, %d).\n", + pointmark(org(starttet)), pointmark(dest(starttet))); + } + orgori = orient3d(torg, tdest, tapex, org(starttet)); + destori = orient3d(torg, tdest, tapex, dest(starttet)); +#ifdef SELF_CHECK + assert(orgori * destori < 0.0); +#endif + spintet = starttet; + do { + // The face rotation should not meet boundary. + fnextself(spintet); + // Check the validity of the PLC. + tspivot(spintet, worksh); + if (worksh.sh != dummysh) { + printf("Error: Invalid PLC.\n"); + printf(" Two subfaces (%d, %d, %d) and (%d, %d, %d)\n", + pointmark(torg), pointmark(tdest), pointmark(tapex), + pointmark(sorg(worksh)), pointmark(sdest(worksh)), + pointmark(sapex(worksh))); + printf(" are found intersecting each other.\n"); + printf(" Hint: Use -d switch to find all intersecting facets.\n"); + terminatetetgen(1); + } + if (!infected(spintet)) { + if (b->verbose > 2) { + printf(" Add crossing tet (%d, %d, %d, %d).\n", + pointmark(org(spintet)), pointmark(dest(spintet)), + pointmark(apex(spintet)), pointmark(oppo(spintet))); + } + infect(spintet); + crosstetlist->append(&spintet); + } + // Check whether other two edges of 'spintet' is a crossing edge. + // It can be quickly checked from the apex of 'spintet', if it is + // not on the facet, then there exists a crossing edge. + workpt[0] = apex(spintet); + idx = pointmark(workpt[0]) - in->firstnumber; + if (worklist[idx] != 1) { + // Either edge (dest, apex) or edge (apex, org) crosses. + checksign = orient3d(torg, tdest, tapex, workpt[0]); +#ifdef SELF_CHECK + assert(checksign != 0.0); +#endif + if (checksign * orgori < 0.0) { + enext2(spintet, worktet); // edge (apex, org). + workpt[1] = org(spintet); + } else { +#ifdef SELF_CHECK + assert(checksign * destori < 0.0); +#endif + enext(spintet, worktet); // edge (dest, apex). + workpt[1] = dest(spintet); + } + // 'worktet' represents the crossing edge. Add it into list only + // it doesn't exist in 'crossedgelist'. + inlistflag = false; + for (j = 0; j < crossedgelist->len() && !inlistflag; j++) { + neightet = * (triface *)(* crossedgelist)[j]; + if (org(neightet) == workpt[0]) { + if (dest(neightet) == workpt[1]) inlistflag = true; + } else if (org(neightet) == workpt[1]) { + if (dest(neightet) == workpt[0]) inlistflag = true; + } + } + if (!inlistflag) { + crossedgelist->append(&worktet); + } + } + } while (apex(spintet) != apex(starttet)); + } - // Search a tet having edge ab. - col = finddirection(idfront, pb, tetrahedrons->items); - if (col == RIGHTCOLLINEAR) { - // b is just the destination. - } else if (col == LEFTCOLLINEAR) { - enext2self(*idfront); - esymself(*idfront); - } else if (col == TOPCOLLINEAR) { - fnextself(*idfront); - enext2self(*idfront); - esymself(*idfront); - } else if (col == BELOWHULL) { - // This front must be a dangling subface outside the cavity. - // See an example in dump-SteinerRemoval-case2.lua. - assert(0); + // Identifying the boundary faces and vertices of C. Sort them into + // 'abovefacelist', 'aboveptlist, 'belowfacelist', and 'belowptlist', + // respectively. "above" and "below" are wrt.(torg, tdest, tapex). + for (i = 0; i < crosstetlist->len(); i++) { + // Get a tet abcd, ab is the crossing edge. + starttet = * (triface *)(* crosstetlist)[i]; +#ifdef SELF_CHECK + assert(infected(starttet)); +#endif + adjustedgering(starttet, CCW); + // abc and abd are sharing the crossing edge, the two neighbors must + // be crossing tetrahedra too. They can't be boundaries of C. + for (j = 0; j < 2; j++) { + if (j == 0) { + enextfnext(starttet, worktet); // Check bcd. + } else { + enext2fnext(starttet, worktet); // Check acd. + } + sym(worktet, neightet); + // If the neighbor doesn't exist or exists but doesn't be infected, + // it's a boundary face of C, save it. + if ((neightet.tet == dummytet) || !infected(neightet)) { + workpt[0] = org(worktet); + workpt[1] = dest(worktet); + workpt[2] = apex(worktet); + belowflag = aboveflag = false; + share = 0; + for (k = 0; k < 3; k++) { + idx = pointmark(workpt[k]) - in->firstnumber; + if (worklist[idx] == 0) { + // It's not a vertices of facet, find which side it lies. + checksign = orient3d(torg, tdest, tapex, workpt[k]); +#ifdef SELF_CHECK + assert(checksign != 0.0); +#endif + if (checksign > 0.0) { + // It lies "below" the facet wrt. 'startsh'. + worklist[idx] = 2; + belowptlist->append(&workpt[k]); + } else if (checksign < 0.0) { + // It lies "above" the facet wrt. 'startsh'. + worklist[idx] = 3; + aboveptlist->append(&workpt[k]); + } + } + if (worklist[idx] == 2) { + // This face lies "below" the facet wrt. 'startsh'. + belowflag = true; + } else if (worklist[idx] == 3) { + // This face lies "above" the facet wrt. 'startsh'. + aboveflag = true; + } else { +#ifdef SELF_CHECK + // In degenerate case, this face may just be the equator. + assert(worklist[idx] == 1); +#endif + share++; + } + } +#ifdef SELF_CHECK + // The degenerate case has been ruled out. + assert(share < 3); + // Only one flag is possible for a cavity face. + assert(belowflag ^ aboveflag); +#endif + if (belowflag) { + belowfacelist->append(&worktet); + } else if (aboveflag) { + abovefacelist->append(&worktet); + } + } + } } - if (dest(*idfront) == pb) { - // Search a tet having face abc - pc = apex(*front); - spintet = *idfront; - hitbdry = 0; - do { - if (apex(spintet) == pc) { - // Found abc. Insert an auxilary subface s at idfront. - // insertauxsubface(front, &spintet); - *idfront = spintet; - return true; + // Shrink R if not all its subfaces are crossing by the discovered tets. + // 'crossshlist' and 'horizptlist' represent the set of subfaces and + // vertices of the shrinked missing region, respectively. + for (i = 0; i < missingshlist->len(); i++) { + worksh = * (face *)(* missingshlist)[i]; +#ifdef SELF_CHECK + assert(sinfected(worksh)); +#endif + workpt[0] = sorg(worksh); + workpt[1] = sdest(worksh); + workpt[2] = sapex(worksh); + crossflag = false; + for (j = 0; j < crosstetlist->len() && !crossflag; j++) { + // Get a tet abcd, ab is a crossing edge. + starttet = * (triface *)(* crosstetlist)[j]; + adjustedgering(starttet, CCW); + // Only need to check two sides of worktet. + for (k = 0; k < 2 && !crossflag; k++) { + if (k == 0) { + worktet = starttet; // Check abc. + } else { + fnext(starttet, worktet); // Check abd. + } + crossflag = tritritest(&worktet, workpt[0], workpt[1], workpt[2]); } - if (!fnextself(spintet)) { - hitbdry ++; - if (hitbdry < 2) { - esym(*idfront, spintet); - if (!fnextself(spintet)) { - hitbdry ++; + } + if (crossflag) { + // 'worksh' is crossed by 'worktet', uninfect it. + suninfect(worksh); + crossshlist->append(&worksh); + // Add its corners into 'horizptlist'. + for (k = 0; k < 3; k++) { + idx = pointmark(workpt[k]) - in->firstnumber; + if (worklist[idx] != 4) { + worklist[idx] = 4; + horizptlist->append(&workpt[k]); + } + } + } + } + + // Check 'crossingtetlist'. Queue subfaces inside them. + for (i = 0; i < crosstetlist->len(); i++) { + starttet = * (triface *)(* crosstetlist)[i]; + for (starttet.loc = 0; starttet.loc < 4; starttet.loc++) { + sym(starttet, neightet); + // If the neighbor exist and is infected, check it. + if ((neightet.tet != dummytet) && infected(neightet)) { + tspivot(starttet, worksh); + if (worksh.sh != dummysh) { + // Temporarily remove worksh. Make it missing. recover it later. + if (b->verbose > 2) { + printf(" Queuing subface (%d, %d, %d).\n", + pointmark(sorg(worksh)), pointmark(sdest(worksh)), + pointmark(sapex(worksh))); } + tsdissolve(neightet); + tsdissolve(starttet); + // Detach tets at the both sides of this subface. + stdissolve(worksh); + sesymself(worksh); + stdissolve(worksh); + sinfect(worksh); + missingshqueue->push(&worksh); } } - if (apex(spintet) == apex(*idfront)) break; - } while (hitbdry < 2); + } } - // f is missing in D. - if (b->verbose > 1) { - printf(" Front (%d, %d, %d) is missing.\n", pointmark(pa), - pointmark(pb), pointmark(apex(*front))); + // Clear flags set in 'worklist'. + for (i = 0; i < equatptlist->len(); i++) { + workpt[0] = * (point *)(* equatptlist)[i]; + idx = pointmark(workpt[0]) - in->firstnumber; +#ifdef SELF_CHECK + assert((worklist[idx] == 1) || (worklist[idx] == 4)); +#endif + worklist[idx] = 0; + } + for (i = 0; i < belowptlist->len(); i++) { + workpt[0] = * (point *)(* belowptlist)[i]; + idx = pointmark(workpt[0]) - in->firstnumber; +#ifdef SELF_CHECK + assert(worklist[idx] == 2); +#endif + worklist[idx] = 0; + } + for (i = 0; i < aboveptlist->len(); i++) { + workpt[0] = * (point *)(* aboveptlist)[i]; + idx = pointmark(workpt[0]) - in->firstnumber; +#ifdef SELF_CHECK + assert(worklist[idx] == 3); +#endif + worklist[idx] = 0; } - return false; } /////////////////////////////////////////////////////////////////////////////// // // -// gluefronts() Glue two fronts together. // -// // -// This is a support routine for identifyfront(). Two fronts f and f1 are // -// found indentical. This is caused by the non-coplanarity of vertices of a // -// facet. Hence f and f1 are a subface and a tet. They are not fronts of the // -// cavity anymore. This routine glues f and f1 together. // +// insertallsubfaces() Insert all subfaces, queue missing subfaces. // // // -// A tet containing this front and not in the cavity is added into 'gluetet- // -// list' (either f or f1). It will be used to maintain the point-to-tet map. // +// Loop through all subfaces, insert each into the DT. If one already exists,// +// bond it to the tetrahedra having it. Otherwise, it is missing, infect it // +// and save it in 'missingshqueue'. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::gluefronts(triface* front, triface* front1, list* gluetetlist, - list *glueshlist) +void tetgenmesh::insertallsubfaces(queue* missingshqueue) { - face consh; - - // Glue f and f1 together. There're four cases: - // (1) both f and f1 are not fake; - // (2) f is not fake, f1 is fake; - // (3) f is fake and f1 is not fake; - // (4) both f and f1 are fake. - // Case (4) should be not possible. + triface searchtet; + face subloop; - // Is there a concrete subface c at f. - tspivot(*front, consh); - if (consh.sh != dummysh) { - sesymself(consh); - tsbond(*front1, consh); // Bond: f1 <--> c. - sesymself(consh); - // Save this subface if it is not a temp subface. In case the mesh cavity - // fails, we need to restore the original state. - if (!isdead(&consh)) { - // Save this subface into list. - glueshlist->append(&consh); - } - } - // Does f hold by a fake tet. - if (oppo(*front) == (point) NULL) { - // f is fake. Case (3) or (4). - assert(oppo(*front1) != (point) NULL); // Eliminate (4). - // Case (3). - if (consh.sh != dummysh) { - stdissolve(consh); // Dissolve: c -x-> f. - } - // Dealloc f. - tetrahedrondealloc(front->tet); - // f1 becomes a hull. let 'dummytet' bond to it. - dummytet[0] = encode(*front1); - } else { - // Case (1) or (2). - bond(*front, *front1); // Bond f1 <--> f. - // Add f into list. - gluetetlist->append(front); - } - // Is f a fake tet? - if (!isdead(front)) { - // No. Check for case (2). - tspivot(*front1, consh); - // Is f1 fake? - if (oppo(*front1) == (point) NULL) { - // Case (2) or (4) - assert(oppo(*front) != (point) NULL); // Eliminate (4). - // Case (2). - if (consh.sh != dummysh) { - stdissolve(consh); // Dissolve: c -x-> f1. - sesymself(consh); // Bond: f <--> c. - tsbond(*front, consh); - // Save this subface if it is not a temp subface. In case the mesh - // cavity fails, we need to restore the original state. - if (!isdead(&consh)) { - // Save this subface into list. - glueshlist->append(&consh); - } - } - // Dissolve: f -x->f1. - dissolve(*front); - // Dealloc f1. - tetrahedrondealloc(front1->tet); - // f becomes a hull. let 'dummytet' bond to it. - dummytet[0] = encode(*front); - } else { - // Case (1). - if (consh.sh != dummysh) { - sesymself(consh); - tsbond(*front, consh); // Bond: f <--> c. - // Save this subface if it is not a temp subface. In case the mesh - // cavity fails, we need to restore the original state. - if (!isdead(&consh)) { - // Save this subface into list. - glueshlist->append(&consh); - } + searchtet.tet = (tetrahedron *) NULL; + subfaces->traversalinit(); + subloop.sh = shellfacetraverse(subfaces); + while (subloop.sh != (shellface *) NULL) { + if (!insertsubface(&subloop, &searchtet)) { + if (b->verbose > 1) { + printf(" Queuing subface (%d, %d, %d).\n", pointmark(sorg(subloop)), + pointmark(sdest(subloop)), pointmark(sapex(subloop))); } - // Add f1 into list. - gluetetlist->append(front1); + sinfect(subloop); + missingshqueue->push(&subloop); } + subloop.sh = shellfacetraverse(subfaces); } } /////////////////////////////////////////////////////////////////////////////// // // -// identifyfronts() Identify cavity faces in D. // +// constrainedfacets() Recover subfaces in a Delaunay tetrahedralization. // // // -// 'frontlist' are fronts of C need indentfying. This routine searches each // -// front f in D. Once f is found, an auxilary subface s is inserted in D at // -// the face. If f is not found in D, remove it from frontlist and save it in // -// 'misfrontlist'. // +// This routine creates a CDT by incrementally updating a DT D into a CDT T. // +// The process of recovering facets can be imagined by "merging" the surface // +// mesh F into D. At the beginning, F and D are completely seperated. Some // +// faces of them are matching some are not because they are crossed by some // +// tetrahedra of D. The non-matching subfaces will be forced to appear in T // +// by locally retetrahedralizing the regions where F and D are intersecting. // +// // +// When a subface s of F is found missing in D, probably some other subfaces // +// near to s are missing too. The set of adjoining coplanar missing faces // +// forms a missing region R (R may not simply connected). // +// // +// There are two possibilities can result a mssing region R: (1) Some edges // +// of D cross R; (2) No edge of D crosses R, but some faces of D spans R, ie,// +// D is locally degenerate at R. In case (1), D is modified so that it resp- // +// ects R (done by a cavity retetrahedralization algorithm). In case (2), F // +// is modified so that the set of subfaces of R matches faces in D (done by // +// a face rearrangment algorithm). // // // /////////////////////////////////////////////////////////////////////////////// -bool tetgenmesh::identifyfronts(list* frontlist, list* misfrontlist, - list* gluetetlist, list* glueshlist) +void tetgenmesh::constrainedfacets() { - triface front, front1, tfront; - triface idfront, neightet; - face auxsh, checksh; - int len, i, j; + queue *missingshqueue, *flipque; + list *missingshlist, *equatptlist; + list *boundedgelist, *crossedgelist, *crosstetlist; + list *crossshlist, *belowfacelist, *abovefacelist; + list *horizptlist, *belowptlist, *aboveptlist; + list *frontlist, *misfrontlist, *newtetlist; + triface searchtet, worktet; + face subloop, worksh; + int *worklist; + int i; - misfrontlist->clear(); + if (!b->quiet) { + printf("Constraining facets.\n"); + } - // Identify all fronts in D. - for (i = 0; i < frontlist->len(); i++) { - // Get a front f. - front = * (triface *)( *frontlist)[i]; - if (scoutfront(&front, &idfront)) { - // Found f. Insert an aux subface s. - tspivot(idfront, auxsh); - if (auxsh.sh != dummysh) { // Does s already exist? - // There're two identical fronts, f (front) and f1 (s.sh[0])! - decode((tetrahedron) auxsh.sh[0], front1); - assert((front1.tet != dummytet) && !infected(front1)); - // Detach s in D. - tsdissolve(idfront); - sym(idfront, neightet); - if (neightet.tet != dummytet) { - tsdissolve(neightet); - } - // s has fulfilled its duty. Can be deleted. - shellfacedealloc(subfaces, auxsh.sh); - // Remove f from frontlist. - frontlist->del(i, 1); i--; - // Remove f1 from frontlist. - len = frontlist->len(); - for (j = 0; j < frontlist->len(); j++) { - tfront = * (triface *)(* frontlist)[j]; - if ((tfront.tet == front1.tet) && (tfront.loc == front1.loc)) { - // Found f1 in list. Check f1 != f. - assert((tfront.tet != front.tet) || (tfront.loc != front.loc)); - frontlist->del(j, 1); i--; - break; + // Initialize queues. + missingshqueue = new queue(sizeof(face)); + flipque = new queue(sizeof(badface)); + // Initialize the working lists. + missingshlist = new list(sizeof(face), NULL); + boundedgelist = new list(sizeof(face), NULL); + crossedgelist = new list(sizeof(triface), NULL); + equatptlist = new list("point *"); + crossshlist = new list(sizeof(face), NULL); + crosstetlist = new list(sizeof(triface), NULL); + belowfacelist = new list(sizeof(triface), NULL); + abovefacelist = new list(sizeof(triface), NULL); + horizptlist = new list("point *"); + belowptlist = new list("point *"); + aboveptlist = new list("point *"); + frontlist = new list(sizeof(triface), NULL); + misfrontlist = new list(sizeof(triface), NULL); + newtetlist = new list(sizeof(triface), NULL); + // Initialize the array for marking vertices. + worklist = new int[points->items + 1]; + for (i = 0; i < points->items + 1; i++) worklist[i] = 0; + + // Compute a mapping from points to tetrahedra for fast searching. + makepoint2tetmap(); + + // Match subfaces in D, queue all missing subfaces. + insertallsubfaces(missingshqueue); + + // Recover all missing subfaces. + while (!missingshqueue->empty()) { + // Get a queued face s. + subloop = * (face *) missingshqueue->pop(); + // s may have been deleted in a face rearrangment operation. + if (isdead(&subloop)) continue; + // s may have been recovered in a previous missing region. + if (!sinfected(subloop)) continue; + // s may match a face in D now due to previous transformations. + if (insertsubface(&subloop, &searchtet)) { + suninfect(subloop); + continue; + } + if (b->verbose > 1) { + printf(" Recover subface (%d, %d, %d).\n", pointmark(sorg(subloop)), + pointmark(sdest(subloop)), pointmark(sapex(subloop))); + } + // Form the missing region R containing s. + formmissingregion(&subloop, missingshlist, equatptlist, worklist); + // Is R crossing by any tetrahedron? + if (scoutcrossingedge(missingshlist, boundedgelist, crossedgelist, + worklist)) { + // Form the cavity C containing R. + formcavity(missingshlist, crossedgelist, equatptlist, crossshlist, + crosstetlist, belowfacelist, abovefacelist, horizptlist, + belowptlist, aboveptlist, missingshqueue, worklist); + // Recover the above part of C. + delaunizecavity(crossshlist, abovefacelist, aboveptlist, horizptlist, + frontlist, misfrontlist, newtetlist, crosstetlist, + missingshqueue, flipque); + // Inverse the direction of subfaces in R. + for (i = 0; i < crossshlist->len(); i++) { + worksh = * (face *)(* crossshlist)[i]; + sesymself(worksh); + * (face *)(* crossshlist)[i] = worksh; + } + // Recover the below part of C. + delaunizecavity(crossshlist, belowfacelist, belowptlist, horizptlist, + frontlist, misfrontlist, newtetlist, crosstetlist, + missingshqueue, flipque); + // Delete tetrahedra in C. + for (i = 0; i < crosstetlist->len(); i++) { + worktet = * (triface *)(* crosstetlist)[i]; + tetrahedrondealloc(worktet.tet); + } + // There may have some un-recovered subfaces of R. Put them back into + // queue. Otherwise, they will be missing on the boundary. + for (i = 0; i < missingshlist->len(); i++) { + worksh = * (face *)(* missingshlist)[i]; + if (sinfected(worksh)) { + // An unrecovered subface, put it back into queue. + missingshqueue->push(&worksh); + } + } + crossshlist->clear(); + belowfacelist->clear(); + abovefacelist->clear(); + horizptlist->clear(); + belowptlist->clear(); + aboveptlist->clear(); + crosstetlist->clear(); + } else { + // No. Rearrange subfaces of F conforming to that of D in R. It can + // happen when the facet has non-coplanar vertices. + rearrangesubfaces(missingshlist, boundedgelist, equatptlist, worklist); + } + // Clear all working lists. + missingshlist->clear(); + boundedgelist->clear(); + crossedgelist->clear(); + equatptlist->clear(); + } + + // Subfaces have been merged into D. + checksubfaces = 1; + + if (b->verbose > 0) { + printf(" The biggest cavity: %d faces, %d vertices\n", maxcavfaces, + maxcavverts); + printf(" Enlarged %d times\n", expcavcount); + } + + delete missingshqueue; + delete flipque; + delete missingshlist; + delete boundedgelist; + delete crossedgelist; + delete equatptlist; + delete crossshlist; + delete crosstetlist; + delete belowfacelist; + delete abovefacelist; + delete horizptlist; + delete belowptlist; + delete aboveptlist; + delete frontlist; + delete misfrontlist; + delete newtetlist; + delete [] worklist; +} + +// +// End of facet recovery routines +// + +// +// Begin of carving out holes and concavities routines +// + +/////////////////////////////////////////////////////////////////////////////// +// // +// infecthull() Virally infect all of the tetrahedra of the convex hull // +// that are not protected by subfaces. Where there are // +// subfaces, set boundary markers as appropriate. // +// // +// Memorypool 'viri' is used to return all the infected tetrahedra. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::infecthull(memorypool *viri) +{ + triface tetloop, tsymtet; + tetrahedron **deadtet; + face hullface; + // point horg, hdest, hapex; + + if (b->verbose > 0) { + printf(" Marking concavities for elimination.\n"); + } + tetrahedrons->traversalinit(); + tetloop.tet = tetrahedrontraverse(); + while (tetloop.tet != (tetrahedron *) NULL) { + // Is this tetrahedron on the hull? + for (tetloop.loc = 0; tetloop.loc < 4; tetloop.loc++) { + sym(tetloop, tsymtet); + if (tsymtet.tet == dummytet) { + // Is the tetrahedron protected by a subface? + tspivot(tetloop, hullface); + if (hullface.sh == dummysh) { + // The tetrahedron is not protected; infect it. + if (!infected(tetloop)) { + infect(tetloop); + deadtet = (tetrahedron **) viri->alloc(); + *deadtet = tetloop.tet; + break; // Go and get next tet. + } + } else { + // The tetrahedron is protected; set boundary markers if appropriate. + if (shellmark(hullface) == 0) { + setshellmark(hullface, 1); + /* + horg = sorg(hullface); + hdest = sdest(hullface); + hapex = sapex(hullface); + if (pointmark(horg) == 0) { + setpointmark(horg, 1); + } + if (pointmark(hdest) == 0) { + setpointmark(hdest, 1); + } + if (pointmark(hapex) == 0) { + setpointmark(hapex, 1); + } + */ } } - assert((frontlist->len() + 1) == len); - // Glue f and f1 together. - gluefronts(&front, &front1, gluetetlist, glueshlist); - } else { - // Insert an aux subface to protect f in D. - insertauxsubface(&front, &idfront); - } - } else { - // f is missing. - frontlist->del(i, 1); i--; - // Are there two identical fronts, f (front) and f1 (front1)? - for (j = 0; j < misfrontlist->len(); j++) { - front1 = * (triface *)(* misfrontlist)[j]; - if (isfacehaspoint(&front1, org(front)) && - isfacehaspoint(&front1, dest(front)) && - isfacehaspoint(&front1, apex(front))) break; - } - if (j < misfrontlist->len()) { - // Found an identical front f1. Remove f1 from the list. - misfrontlist->del(j, 1); - // Glue f and f1 together. - gluefronts(&front, &front1, gluetetlist, glueshlist); - } else { - // Add f into misfrontlist. - misfrontlist->append(&front); } } + tetloop.tet = tetrahedrontraverse(); } - return misfrontlist->len() == 0; } /////////////////////////////////////////////////////////////////////////////// // // -// detachauxsubfaces() Detach auxilary subfaces in D. // +// plague() Spread the virus from all infected tets to any neighbors not // +// protected by subfaces. // // // -// This is a reverse routine of identifyfronts(). Some fronts are missing in // -// D. C can not be easily tetrahedralized. It needs remediation (expansion, // -// or constrained flips, or adding a Steiner point). This routine detaches // -// the auxilary subfaces have been inserted in D and delete them. // +// This routine identifies all the tetrahedra that will die, and marks them // +// as infected. They are marked to ensure that each tetrahedron is added to // +// the virus pool only once, so the procedure will terminate. 'viri' returns // +// all infected tetrahedra which are outside the domian. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::detachauxsubfaces(list* newtetlist) +void tetgenmesh::plague(memorypool *viri) { - triface newtet, neightet; - face auxsh; + tetrahedron **virusloop; + tetrahedron **deadtet; + triface testtet, neighbor; + face neighsh, testseg; + face spinsh, casingin, casingout; + int firstdadsub; int i; - for (i = 0; i < newtetlist->len(); i++) { - // Get a new tet t. - newtet = * (triface *)(* newtetlist)[i]; - // t may e dead due to flips. - if (isdead(&newtet)) continue; - assert(!infected(newtet)); - // Check the four faces of t. - for (newtet.loc = 0; newtet.loc < 4; newtet.loc++) { - tspivot(newtet, auxsh); - if (auxsh.sh != dummysh) { - // An auxilary subface s. - assert(sorg(auxsh) == (point) NULL); - tsdissolve(newtet); // t -x-> s. - sym(newtet, neightet); - if (neightet.tet != dummytet) { - assert(!isdead(&neightet)); - tsdissolve(neightet); // n -x-> s. + if (b->verbose > 0) { + printf(" Marking neighbors of marked tetrahedra.\n"); + } + firstdadsub = 0; + // Loop through all the infected tetrahedra, spreading the virus to + // their neighbors, then to their neighbors' neighbors. + viri->traversalinit(); + virusloop = (tetrahedron **) viri->traverse(); + while (virusloop != (tetrahedron **) NULL) { + testtet.tet = *virusloop; + // Temporarily uninfect this tetrahedron, not necessary. + uninfect(testtet); + // Check each of the tetrahedron's four neighbors. + for (testtet.loc = 0; testtet.loc < 4; testtet.loc++) { + // Find the neighbor. + sym(testtet, neighbor); + // Check for a shell between the tetrahedron and its neighbor. + tspivot(testtet, neighsh); + // Check if the neighbor is nonexistent or already infected. + if ((neighbor.tet == dummytet) || infected(neighbor)) { + if (neighsh.sh != dummysh) { + // There is a subface separating the tetrahedron from its neighbor, + // but both tetrahedra are dying, so the subface dies too. + // Before deallocte this subface, dissolve the connections between + // other subfaces, subsegments and tetrahedra. + neighsh.shver = 0; + if (!firstdadsub) { + firstdadsub = 1; // Report the problem once. + if (!b->quiet) { + printf("Warning: Detecting an open face (%d, %d, %d).\n", + pointmark(sorg(neighsh)), pointmark(sdest(neighsh)), + pointmark(sapex(neighsh))); + } + } + // For keep the same enext() direction. + findedge(&testtet, sorg(neighsh), sdest(neighsh)); + for (i = 0; i < 3; i++) { + sspivot(neighsh, testseg); + if (testseg.sh != dummysh) { + // A subsegment is found at this side, dissolve this subface + // from the face link of this subsegment. + testseg.shver = 0; + spinsh = neighsh; + if (sorg(spinsh) != sorg(testseg)) { + sesymself(spinsh); + } + spivot(spinsh, casingout); + if (casingout.sh == spinsh.sh) { + // This is a trivial face link, only 'neighsh' itself, + // the subsegment at this side is also died. + shellfacedealloc(subsegs, testseg.sh); + } else { + spinsh = casingout; + do { + casingin = spinsh; + spivotself(spinsh); + } while (spinsh.sh != neighsh.sh); + // Set the link casingin->casingout. + sbond1(casingin, casingout); + // Bond the subsegment anyway. + ssbond(casingin, testseg); + } + } + senextself(neighsh); + enextself(testtet); + } + if (neighbor.tet != dummytet) { + // Make sure the subface doesn't get deallocated again later + // when the infected neighbor is visited. + tsdissolve(neighbor); + } + // This subface has been separated. + if (in->mesh_dim > 2) { + shellfacedealloc(subfaces, neighsh.sh); + } else { + // Dimension is 2. keep it for output. + // Dissolve tets at both sides of this subface. + stdissolve(neighsh); + sesymself(neighsh); + stdissolve(neighsh); + } + } + } else { // The neighbor exists and is not infected. + if (neighsh.sh == dummysh) { + // There is no subface protecting the neighbor, infect it. + infect(neighbor); + // Ensure that the neighbor's neighbors will be infected. + deadtet = (tetrahedron **) viri->alloc(); + *deadtet = neighbor.tet; + } else { // The neighbor is protected by a subface. + // Remove this tetrahedron from the subface. + stdissolve(neighsh); + // The subface becomes a boundary. Set markers accordingly. + if (shellmark(neighsh) == 0) { + setshellmark(neighsh, 1); + } + // This side becomes hull. Update the handle in dummytet. + dummytet[0] = encode(neighbor); } - // Delete s. - shellfacedealloc(subfaces, auxsh.sh); } } + // Remark the tetrahedron as infected, so it doesn't get added to the + // virus pool again. + infect(testtet); + virusloop = (tetrahedron **) viri->traverse(); } } /////////////////////////////////////////////////////////////////////////////// // // -// carvecavity() Remove redundant (outside) tetrahedra from D. // -// // -// The fronts of C have been identified in D. Hence C can be tetrahedralized // -// by removing the tets outside C. The CDT is then updated by filling C with // -// the remaining tets (inside C) of D. // -// // -// Each front is protected by an auxilary subface s in D. s has a pointer to // -// f (s.sh[0]). f can be used to classified the in- and out- tets of C (the // -// CW orientation of f faces to the inside of C). The classified out-tets of // -// C are marked (infected) for removing. // +// regionplague() Spread regional attributes and/or volume constraints // +// (from a .poly file) throughout the mesh. // // // -// Notice that the out-tets may not only the tets on the CH of C, but also // -// tets completely inside D, eg., there is a "hole" in D. Such tets must be // -// marked during classification. The hole tets are poped up and removed too. // +// This procedure operates in two phases. The first phase spreads an attri- // +// bute and/or a volume constraint through a (facet-bounded) region. The // +// second phase uninfects all infected tetrahedra, returning them to normal. // // // /////////////////////////////////////////////////////////////////////////////// -bool tetgenmesh::carvecavity(list* newtetlist, list* outtetlist, - list* gluetetlist, queue* flipque) +void tetgenmesh:: +regionplague(memorypool *regionviri, REAL attribute, REAL volume) { - triface newtet, neightet, front, intet, outtet, oldtet; - face auxsh, consh; - point pa, pb, pc; - point pointptr; - REAL ori; - bool success; - int i; - - // Clear work list. - outtetlist->clear(); - success = true; + tetrahedron **virusloop; + tetrahedron **regiontet; + triface testtet, neighbor; + face neighsh; - // Classify in- and out- tets in D. Mark and queue classified out-tets. - for (i = 0; i < newtetlist->len() && success; i++) { - // Get a new tet t. - newtet = * (triface *)(* newtetlist)[i]; - assert(!isdead(&newtet)); - // Skip an infected tet (it's an out tet). - if (!infected(newtet)) { - // Look for aux subfaces attached at t. - for (newtet.loc = 0; newtet.loc < 4; newtet.loc++) { - tspivot(newtet, auxsh); - if (auxsh.sh != dummysh) { - // Get the front f. - decode((tetrahedron) auxsh.sh[0], front); - // Let f face to the inside of C. - adjustedgering(front, CW); - pa = org(front); - pb = dest(front); - pc = apex(front); - // Has this side a neighbor n? - sym(newtet, neightet); - if ((neightet.tet != dummytet) && !infected(neightet)) { - // Classify t and n (one is "in" and another is "out"). - ori = orient3d(pa, pb, pc, oppo(newtet)); - if (ori == 0.0) { - printf("Internal error at front %d.\n", i); - assert(0); - } - if (ori < 0.0) { - // t is in-tet. n is out-tet. - outtet = neightet; - intet = newtet; - } else { - // n is in-tet. t is out-tet. - outtet = newtet; - intet = neightet; - } - if (!infected(outtet)) { - // Check the special case: if this tet is protected by four - // subfaces, i.e., all 4 faces of this tet are fronts. - // See an example in dbg/dump-SteinerRemoval-case3.lua - neightet = outtet; - for (neightet.loc = 0; neightet.loc < 4; neightet.loc++) { - tspivot(neightet, auxsh); - if (auxsh.sh == dummysh) break; - } - if (neightet.loc < 4) { - // It is an outside tet. Add it into list. - infect(outtet); - outtetlist->append(&outtet); - } - } - } else { - intet = newtet; - } - // Make sure that the intet is not iversed. - ori = orient3d(pa, pb, pc, oppo(intet)); - assert(ori != 0); - if (ori > 0) { - // Found an inversed inside tet. Stop and return. - if (b->verbose > 1) { - printf(" Intet x%lx %d (%d, %d, %d, %d) is iversed.\n", - (unsigned long) intet.tet, intet.loc, pointmark(pa), - pointmark(pb), pointmark(pc), pointmark(oppo(intet))); - } - success = false; - break; - } - } else { - // This side is not protected. Check if it is a hull face. - // Comment: This check is necessary. It is possible that all - // protected subfaces have been moved in 'gluetetlist'. - // If so, without this check, the newtets become orphans - // and remain in the output. 2009-07-29. - sym(newtet, neightet); - if (neightet.tet == dummytet) { - // Found an out tet. - if (!infected(newtet)) { - infect(newtet); - outtetlist->append(&newtet); - } - break; - } - } - } // for (newtet.loc) - } // if (!infected) + if (b->verbose > 1) { + printf(" Marking neighbors of marked tetrahedra.\n"); } - - if (!success) { - // Found inversed tet. The carvecavity failed. - for (i = 0; i < outtetlist->len(); i++) { - outtet = * (triface *)(* outtetlist)[i]; - uninfect(outtet); + // Loop through all the infected tetrahedra, spreading the attribute + // and/or volume constraint to their neighbors, then to their neighbors' + // neighbors. + regionviri->traversalinit(); + virusloop = (tetrahedron **) regionviri->traverse(); + while (virusloop != (tetrahedron **) NULL) { + testtet.tet = *virusloop; + // Temporarily uninfect this tetrahedron, not necessary. + uninfect(testtet); + if (b->regionattrib) { + // Set an attribute. + setelemattribute(testtet.tet, in->numberoftetrahedronattributes, + attribute); } - outtetlist->clear(); - return false; - } - - // Find and mark all out-tets. - for (i = 0; i < outtetlist->len(); i++) { - outtet = * (triface *)(* outtetlist)[i]; - for (outtet.loc = 0; outtet.loc < 4; outtet.loc++) { - sym(outtet, neightet); - // Does the neighbor exist and unmarked? - if ((neightet.tet != dummytet) && !infected(neightet)) { - // Is it protected by an aux subface? - tspivot(outtet, auxsh); - if (auxsh.sh == dummysh) { - // It's an out-tet. - infect(neightet); - outtetlist->append(&neightet); - } - } + if (b->varvolume) { + // Set a volume constraint. + setvolumebound(testtet.tet, volume); } - } - - // Remove the out- (and hole) tets. - for (i = 0; i < outtetlist->len(); i++) { - // Get an out-tet t. - outtet = * (triface *)(* outtetlist)[i]; - assert(!isdead(&outtet)); - // Detach t from the in-tets. - for (outtet.loc = 0; outtet.loc < 4; outtet.loc++) { - // Is there an aux subface s? - tspivot(outtet, auxsh); - if (auxsh.sh != dummysh) { - // Get the neighbor n. - sym(outtet, neightet); - // assert(!infected(neightet)); // t must be in-tet. - if (infected(neightet)) { - printf("Error: A front face (%d, %d, %d) x%lx got deleted.\n", - pointmark(org(neightet)), pointmark(dest(neightet)), - pointmark(apex(neightet)), (unsigned long) auxsh.sh); - printf(" p:draw_tet(%d, %d, %d, %d) -- in\n", - pointmark(org(neightet)), pointmark(dest(neightet)), - pointmark(apex(neightet)), pointmark(oppo(neightet))); - printf(" p:draw_tet(%d, %d, %d, %d) -- out\n", - pointmark(org(outtet)), pointmark(dest(outtet)), - pointmark(apex(outtet)), pointmark(oppo(outtet))); - assert(0); - } - // Detach n -x-> t. - dissolve(neightet); + // Check each of the tetrahedron's four neighbors. + for (testtet.loc = 0; testtet.loc < 4; testtet.loc++) { + // Find the neighbor. + sym(testtet, neighbor); + // Check for a subface between the tetrahedron and its neighbor. + tspivot(testtet, neighsh); + // Make sure the neighbor exists, is not already infected, and + // isn't protected by a subface, or is protected by a nonsolid + // subface. + if ((neighbor.tet != dummytet) && !infected(neighbor) + && (neighsh.sh == dummysh)) { + // Infect the neighbor. + infect(neighbor); + // Ensure that the neighbor's neighbors will be infected. + regiontet = (tetrahedron **) regionviri->alloc(); + *regiontet = neighbor.tet; } } - // Dealloc the tet. - tetrahedrondealloc(outtet.tet); + // Remark the tetrahedron as infected, so it doesn't get added to the + // virus pool again. + infect(testtet); + virusloop = (tetrahedron **) regionviri->traverse(); } - // Connect the in-tets of C to fronts. Remove aux subfaces and fake tets. - for (i = 0; i < newtetlist->len(); i++) { - // Get a new tet t. - newtet = * (triface *)(* newtetlist)[i]; - // t may be an out-tet and has got deleted. - if (isdead(&newtet)) continue; - // t is an in-tet. Look for aux subfaces attached at t. - for (newtet.loc = 0; newtet.loc < 4; newtet.loc++) { - // Is there an aux subface s? - tspivot(newtet, auxsh); - if (auxsh.sh != dummysh) { - // Get the front f. - decode((tetrahedron) auxsh.sh[0], front); - assert((front.tet != dummytet) && !infected(front)); - // s has fulfilled its duty. Can be deleted. - tsdissolve(newtet); // dissolve: t -x-> s. - // Delete s. - shellfacedealloc(subfaces, auxsh.sh); - // Connect the newtet t and front f. - // Is there a concrete subface c at f. - tspivot(front, consh); - if (consh.sh != dummysh) { - sesymself(consh); - // Bond: t <--> c. - tsbond(newtet, consh); - } - // Update point-to-tet map. - pointptr = org(front); - setpoint2tet(pointptr, encode(newtet)); - pointptr = dest(front); - setpoint2tet(pointptr, encode(newtet)); - pointptr = apex(front); - setpoint2tet(pointptr, encode(newtet)); - // Does f hold by a fake tet. - if (oppo(front) == (point) NULL) { - // f is fake. - if (consh.sh != dummysh) { - sesymself(consh); - // Dissolve: c -x-> f. - stdissolve(consh); - } - // Detach the fake tet from its old cavity tet. This is necessary - // in case the mesh of other cavities are failed, and we have to - // restore the original status. 2009-07-24. - sym(front, oldtet); - if (oldtet.tet != dummytet) { - assert(infected(oldtet)); - dissolve(oldtet); - } - // Dealloc f. - tetrahedrondealloc(front.tet); - // f becomes a hull. let 'dummytet' bond to it. - dummytet[0] = encode(newtet); - } else { - // Bond t <--> f. - bond(newtet, front); - } - // t may be non-locally Delaunay and flipable. - if (flipque != (queue *) NULL) { - enqueueflipface(newtet, flipque); - } - } - } - // Let the corners of t2 point to it for fast searching. - pointptr = org(newtet); - setpoint2tet(pointptr, encode(newtet)); - pointptr = dest(newtet); - setpoint2tet(pointptr, encode(newtet)); - pointptr = apex(newtet); - setpoint2tet(pointptr, encode(newtet)); - pointptr = oppo(newtet); - setpoint2tet(pointptr, encode(newtet)); + // Uninfect all tetrahedra. + if (b->verbose > 1) { + printf(" Unmarking marked tetrahedra.\n"); } - // The cavity has been re-tetrahedralized. - - // Maintain point-to-tet map. - for (i = 0; i < gluetetlist->len(); i++) { - // Get a new tet t. - newtet = * (triface *)(* gluetetlist)[i]; - if (isdead(&newtet)) { - assert(0); - } - pointptr = org(newtet); - setpoint2tet(pointptr, encode(newtet)); - pointptr = dest(newtet); - setpoint2tet(pointptr, encode(newtet)); - pointptr = apex(newtet); - setpoint2tet(pointptr, encode(newtet)); + regionviri->traversalinit(); + virusloop = (tetrahedron **) regionviri->traverse(); + while (virusloop != (tetrahedron **) NULL) { + testtet.tet = *virusloop; + uninfect(testtet); + virusloop = (tetrahedron **) regionviri->traverse(); } - - return true; + // Empty the virus pool. + regionviri->restart(); } /////////////////////////////////////////////////////////////////////////////// // // -// replacepolygonsubs() Substitute the subfaces of a polygon. // -// // -// 'oldshlist' (T_old) contains the old subfaces of P. It will be replaced // -// by 'newshlist' (T_new) of new subfaces. Each boundary edge of P is bonded // -// to 'dummysh' in T_new. // -// // -// Notice that Not every boundary edge of T_new is able to bond to a subface,// -// e.g., when it is a segment recovered by removing a Steiner point in it. // +// removeholetets() Remove tetrahedra which are outside the domain. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::replacepolygonsubs(list* oldshlist, list* newshlist) +void tetgenmesh::removeholetets(memorypool* viri) { - face newsh, oldsh, spinsh; - face casingout, casingin; - face checkseg; - point pa, pb; - int i, j, k, l; + tetrahedron **virusloop; + triface testtet, neighbor; + point checkpt; + int *tetspernodelist; + int i, j; - for (i = 0; i < newshlist->len(); i++) { - // Get a new subface s. - newsh = * (face *)(* newshlist)[i]; - // Check the three edges of s. - for (k = 0; k < 3; k++) { - spivot(newsh, casingout); - // Is it a boundary edge? - if (casingout.sh == dummysh) { - // Find the old subface s_o having the same edge as s. - pa = sorg(newsh); - pb = sdest(newsh); - for (j = 0; j < oldshlist->len(); j++) { - oldsh = * (face *)(* oldshlist)[j]; - for (l = 0; l < 3; l++) { - if (((sorg(oldsh) == pa) && (sdest(oldsh) == pb)) || - ((sorg(oldsh) == pb) && (sdest(oldsh) == pa))) break; - senextself(oldsh); - } - if (l < 3) break; - } - // Is there a matched edge? - if (j < oldshlist->len()) { - // Get the neighbor subface s_out. - spivot(oldsh, casingout); - sspivot(oldsh, checkseg); - if (checkseg.sh != dummysh) { - if (casingout.sh != dummysh) { - if (oldsh.sh == casingout.sh) { - // A subface is self-bounded. Not possible. - assert(0); // DEBUG - } - // A segment. Insert s into the face ring, ie, s_in->s->s_out. - spinsh = casingout; - do { - casingin = spinsh; - spivotself(spinsh); - } while (sapex(spinsh) != sapex(oldsh)); - assert(casingin.sh != oldsh.sh); - // Bond s_in -> s -> s_out (and dissolve s_in -> s_old -> s_out). - sbond1(casingin, newsh); - sbond1(newsh, casingout); - } else { - sbond(newsh, casingout); - } - // Bond the segment. - ssbond(newsh, checkseg); - } else { - // Bond s <-> s_out (and dissolve s_out -> s_old). - sbond(newsh, casingout); - } - // Unbound oldsh to indicate it's neighbor has been replaced. - // It will be used to indentfy the edge in the inverse. - sdissolve(oldsh); - ssdissolve(oldsh); + if (b->verbose > 0) { + printf(" Deleting marked tetrahedra.\n"); + } + + // Create and initialize 'tetspernodelist'. + tetspernodelist = new int[points->items + 1]; + for (i = 0; i < points->items + 1; i++) tetspernodelist[i] = 0; + + // Loop the tetrahedra list, counter the number of tets sharing each node. + tetrahedrons->traversalinit(); + testtet.tet = tetrahedrontraverse(); + while (testtet.tet != (tetrahedron *) NULL) { + // Increment the number of sharing tets for each endpoint. + for (i = 0; i < 4; i++) { + j = pointmark((point) testtet.tet[4 + i]); + tetspernodelist[j]++; + } + testtet.tet = tetrahedrontraverse(); + } + + viri->traversalinit(); + virusloop = (tetrahedron **) viri->traverse(); + while (virusloop != (tetrahedron **) NULL) { + testtet.tet = *virusloop; + // Record changes in the number of boundary faces, and disconnect + // dead tetrahedra from their neighbors. + for (testtet.loc = 0; testtet.loc < 4; testtet.loc++) { + sym(testtet, neighbor); + if (neighbor.tet == dummytet) { + // There is no neighboring tetrahedron on this face, so this face + // is a boundary face. This tetrahedron is being deleted, so this + // boundary face is deleted. + hullsize--; + } else { + // Disconnect the tetrahedron from its neighbor. + dissolve(neighbor); + // There is a neighboring tetrahedron on this face, so this face + // becomes a boundary face when this tetrahedron is deleted. + hullsize++; + } + } + // Check the four corners of this tet if they're isolated. + for (i = 0; i < 4; i++) { + checkpt = (point) testtet.tet[4 + i]; + j = pointmark(checkpt); + tetspernodelist[j]--; + if (tetspernodelist[j] == 0) { + // If it is added volume vertex or '-j' is not used, delete it. + if ((pointtype(checkpt) == FREEVOLVERTEX) || !b->nojettison) { + setpointtype(checkpt, UNUSEDVERTEX); + unuverts++; } } - // Go to the next edge of s. - senextself(newsh); } + // Return the dead tetrahedron to the pool of tetrahedra. + tetrahedrondealloc(testtet.tet); + virusloop = (tetrahedron **) viri->traverse(); } + + delete [] tetspernodelist; } /////////////////////////////////////////////////////////////////////////////// // // -// orientnewsubs() Orient new subfaces facing to the inside of cavity. // +// assignregionattribs() Assign each tetrahedron a region number. // // // -// 'newshlist' contains new subfaces of the cavity C (created by re-triangu- // -// lation the polygon P). They're not necessary facing to the inside of C. // -// 'orientsh', faces to the inside of C, is used to adjust new subfaces. The // -// normal of the new subfaces is returned in 'norm'. // +// This routine is called when '-AA' switch is specified. Every tetrahedron // +// of a (bounded) region will get a integer number to that region. Default, // +// regions are numbered as 1, 2, 3, etc. However, if a number has already // +// been used (set by user in the region section in .poly or .smesh), it is // +// skipped and the next available number will be used. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::orientnewsubs(list* newshlist, face* orientsh, REAL* norm) +void tetgenmesh::assignregionattribs() { - face *newsh; - point pa, pb, pc; - REAL ref[3], ori, len, l; + list *regionnumlist; + list *regiontetlist; + triface tetloop, regiontet, neightet; + face checksh; + bool flag; + int regionnum, num; + int attridx, count; int i; - // Calculate the normal of 'orientsh'. - pa = sorg(*orientsh); - pb = sdest(*orientsh); - pc = sapex(*orientsh); - // facenormal(pa, pb, pc, norm, &len); - facenormal2(pa, pb, pc, norm, 1); - // for (i = 0; i < 3; i++) ref[i] = pa[i] + norm[i]; - len = sqrt(norm[0]*norm[0]+norm[1]*norm[1]+norm[2]*norm[2]); - for (i = 0; i < 3; i++) norm[i] /= len; - // Get the longest edge length of [a, b, c] - len = distance(pa, pb); - l = distance(pb, pc); - if (len < l) len = l; - l = distance(pc, pa); - if (len < l) len = l; - // Calculate a local above point. - for (i = 0; i < 3; i++) ref[i] = pa[i] + len * norm[i]; + if (b->verbose > 0) { + printf(" Assign region numbers.\n"); + } + + regionnumlist = new list(sizeof(int), NULL, 256); + regiontetlist = new list(sizeof(triface), NULL, 1024); + attridx = in->numberoftetrahedronattributes; + + // Loop through all tets. Infect tets which already have a region number, + // and save the used numbers in 'regionnumlist'. + tetrahedrons->traversalinit(); + tetloop.tet = tetrahedrontraverse(); + while (tetloop.tet != (tetrahedron *) NULL) { + if (!infected(tetloop)) { + regionnum = (int) elemattribute(tetloop.tet, attridx); + if (regionnum != 0.0) { + // Found a numbered region tet. + infect(tetloop); + regiontetlist->append(&tetloop); + // Found and infect all tets in this region. + for (i = 0; i < regiontetlist->len(); i++) { + regiontet = * (triface *)(* regiontetlist)[i]; + for (regiontet.loc = 0; regiontet.loc < 4; regiontet.loc++) { + // Is there a boundary face? + tspivot(regiontet, checksh); + if (checksh.sh == dummysh) { + sym(regiontet, neightet); + if ((neightet.tet != dummytet) && !infected(neightet)) { +#ifdef SELF_CHECK + // neightet should have the same region number. Check it. + num = (int) elemattribute(neightet.tet, attridx); + assert(num == regionnum); +#endif + infect(neightet); + regiontetlist->append(&neightet); + } + } + } + } + // Add regionnum to list if it is not exist. + flag = false; + for (i = 0; i < regionnumlist->len() && !flag; i++) { + num = * (int *)(* regionnumlist)[i]; + flag = (num == regionnum); + } + if (!flag) regionnumlist->append(®ionnum); + // Clear list for the next region. + regiontetlist->clear(); + } + } + tetloop.tet = tetrahedrontraverse(); + } - // Orient new subfaces. Let the normal above each one. - for (i = 0; i < newshlist->len(); i++) { - newsh = (face *)(* newshlist)[i]; - pa = sorg(*newsh); - pb = sdest(*newsh); - pc = sapex(*newsh); - ori = orient3d(pa, pb, pc, ref); - assert(ori != 0.0); - if (ori > 0.0) { - sesymself(*newsh); + if (b->verbose > 0) { + printf(" %d user-specified regions.\n", regionnumlist->len()); + } + + // Now loop the tets again. Assign region numbers to uninfected tets. + tetrahedrons->traversalinit(); + tetloop.tet = tetrahedrontraverse(); + regionnum = 1; // Start region number. + count = 0; + while (tetloop.tet != (tetrahedron *) NULL) { + if (!infected(tetloop)) { + // An unassigned region tet. + count++; + do { + flag = false; + // Check if the region number has been used. + for (i = 0; i < regionnumlist->len() && !flag; i++) { + num = * (int *)(* regionnumlist)[i]; + flag = (num == regionnum); + } + if (flag) regionnum++; + } while (flag); + setelemattribute(tetloop.tet, attridx, (REAL) regionnum); + infect(tetloop); + regiontetlist->append(&tetloop); + // Found and infect all tets in this region. + for (i = 0; i < regiontetlist->len(); i++) { + regiontet = * (triface *)(* regiontetlist)[i]; + for (regiontet.loc = 0; regiontet.loc < 4; regiontet.loc++) { + // Is there a boundary face? + tspivot(regiontet, checksh); + if (checksh.sh == dummysh) { + sym(regiontet, neightet); + if ((neightet.tet != dummytet) && !infected(neightet)) { +#ifdef SELF_CHECK + // neightet should have not been assigned yet. Check it. + num = (int) elemattribute(neightet.tet, attridx); + assert(num == 0); +#endif + setelemattribute(neightet.tet, attridx, (REAL) regionnum); + infect(neightet); + regiontetlist->append(&neightet); + } + } + } + } + regiontetlist->clear(); + regionnum++; // The next region number. } + tetloop.tet = tetrahedrontraverse(); + } + + // Uninfect all tets. + tetrahedrons->traversalinit(); + tetloop.tet = tetrahedrontraverse(); + while (tetloop.tet != (tetrahedron *) NULL) { +#ifdef SELF_CHECK + assert(infected(tetloop)); +#endif + uninfect(tetloop); + tetloop.tet = tetrahedrontraverse(); + } + + if (b->verbose > 0) { + printf(" %d regions are numbered.\n", count); } + + delete regionnumlist; + delete regiontetlist; } /////////////////////////////////////////////////////////////////////////////// // // -// registerelemflip() Register an elementary flip T23, T32, or T22. // -// // -// If return TRUE, the flip is registered, otherwise, return FALSE, which // -// means a conflict, this flip already exists. // +// carveholes() Find the holes and infect them. Find the volume // +// constraints and infect them. Infect the convex hull. // +// Spread the infection and kill tetrahedra. Spread the // +// volume constraints. // // // -// Depending on the flip type, not all input points are used, those unused // -// points are set to be dummypoint for easily processing. // +// This routine mainly calls other routines to carry out all these functions.// // // /////////////////////////////////////////////////////////////////////////////// -bool tetgenmesh::registerelemflip(enum fliptype ft, point pa1, point pb1, - point pc1, point pa2, point pb2, point pc2) +void tetgenmesh::carveholes() { - elemflip *ef; - bool rflag; + memorypool *holeviri, *regionviri; + tetrahedron *tptr, **holetet, **regiontet; + triface searchtet, *holetets, *regiontets; + enum locateresult intersect; int i; - rflag = false; // The flip has not registed yet. + if (!b->quiet) { + printf("Removing unwanted tetrahedra.\n"); + if (b->verbose && (in->numberofholes > 0)) { + printf(" Marking holes for elimination.\n"); + } + } - pinfect(pa1); - pinfect(pb1); - pinfect(pc1); - pinfect(pa2); - pinfect(pb2); - pinfect(pc2); + // Initialize a pool of viri to be used for holes, concavities. + holeviri = new memorypool(sizeof(tetrahedron *), 1024, POINTER, 0); + // Mark as infected any unprotected tetrahedra on the boundary. + infecthull(holeviri); - // Search the list for a registered flip. - for (i = 0; i < (int) elemfliplist->objects; i++) { - ef = (elemflip *) fastlookup(elemfliplist, i); - if (ef->ft == ft) { - rflag = (pinfected(ef->pset1[0]) && pinfected(ef->pset1[1]) && - pinfected(ef->pset1[2])); - if (rflag) { - rflag = (pinfected(ef->pset2[0]) && pinfected(ef->pset2[1]) && - pinfected(ef->pset2[2])); - if (rflag) { - break; // This flip has been registed before. + if (in->numberofholes > 0) { + // Allocate storage for the tetrahedra in which hole points fall. + holetets = (triface *) new triface[in->numberofholes]; + // Infect each tetrahedron in which a hole lies. + for (i = 0; i < 3 * in->numberofholes; i += 3) { + // Ignore holes that aren't within the bounds of the mesh. + if ((in->holelist[i] >= xmin) && (in->holelist[i] <= xmax) + && (in->holelist[i + 1] >= ymin) + && (in->holelist[i + 1] <= ymax) + && (in->holelist[i + 2] >= zmin) + && (in->holelist[i + 2] <= zmax)) { + searchtet.tet = dummytet; + // Find a tetrahedron that contains the hole. + intersect = locate(&in->holelist[i], &searchtet); + if ((intersect != OUTSIDE) && (!infected(searchtet))) { + // Record the tetrahedron for processing carve hole. + holetets[i / 3] = searchtet; } } } + // Infect the hole tetrahedron. This is done by marking the tet as + // infected and including the tetrahedron in the virus pool. + for (i = 0; i < in->numberofholes; i++) { + infect(holetets[i]); + holetet = (tetrahedron **) holeviri->alloc(); + *holetet = holetets[i].tet; + } + // Free up memory. + delete [] holetets; } - puninfect(pa1); - puninfect(pb1); - puninfect(pc1); - puninfect(pa2); - puninfect(pb2); - puninfect(pc2); + // Mark as infected all tets of the holes and concavities. + plague(holeviri); + // The virus pool contains all outside tets now. - if (rflag) { - if (b->verbose > 1) { - printf(" Flip: %s", ft == T23 ? "T23" : (ft == T32 ? "T32" : "T22")); - printf(" (%d, %d, %d) - (%d, %d, %d) is registered.\n", pointmark(pa1), - pointmark(pb1), pointmark(pc1), pointmark(pa2), pointmark(pb2), - pointmark(pc2)); + // Is -A switch in use. + if (b->regionattrib) { + // Assign every tetrahedron a regional attribute of zero. + tetrahedrons->traversalinit(); + tptr = tetrahedrontraverse(); + while (tptr != (tetrahedron *) NULL) { + setelemattribute(tptr, in->numberoftetrahedronattributes, 0.0); + tptr = tetrahedrontraverse(); } - return false; } - // Register this flip. - elemfliplist->newindex((void **) &ef); - ef->ft = ft; - ef->pset1[0] = pa1; - ef->pset1[1] = pb1; - ef->pset1[2] = pc1; - ef->pset2[0] = pa2; - ef->pset2[1] = pb2; - ef->pset2[2] = pc2; - - return true; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// check4fixededge() Check if the given edge [a, b] is a fixed edge. // -// // -// A fixed edge is saved in the "fixededgelist". Return TRUE if [a, b] has // -// already existed in the list, otherwise, return FALSE. // -// // -/////////////////////////////////////////////////////////////////////////////// - -bool tetgenmesh::check4fixededge(point pa, point pb) -{ - point *ppt; - int i; - - pinfect(pa); - pinfect(pb); - - for (i = 0; i < (int) fixededgelist->objects; i++) { - ppt = (point *) fastlookup(fixededgelist, i); - if (pinfected(ppt[0]) && pinfected(ppt[1])) { - if (b->verbose > 1) { - printf(" Edge (%d, %d) is fixed.\n", pointmark(pa), - pointmark(pb)); + if (in->numberofregions > 0) { + if (!b->quiet) { + if (b->regionattrib) { + if (b->varvolume) { + printf("Spreading regional attributes and volume constraints.\n"); + } else { + printf("Spreading regional attributes.\n"); + } + } else { + printf("Spreading regional volume constraints.\n"); + } + } + // Allocate storage for the tetrahedra in which region points fall. + regiontets = (triface *) new triface[in->numberofregions]; + // Find the starting tetrahedron for each region. + for (i = 0; i < in->numberofregions; i++) { + regiontets[i].tet = dummytet; + // Ignore region points that aren't within the bounds of the mesh. + if ((in->regionlist[5 * i] >= xmin) + && (in->regionlist[5 * i] <= xmax) + && (in->regionlist[5 * i + 1] >= ymin) + && (in->regionlist[5 * i + 1] <= ymax) + && (in->regionlist[5 * i + 2] >= zmin) + && (in->regionlist[5 * i + 2] <= zmax)) { + searchtet.tet = dummytet; + // Find a tetrahedron that contains the region point. + intersect = locate(&in->regionlist[5 * i], &searchtet); + if ((intersect != OUTSIDE) && (!infected(searchtet))) { + // Record the tetrahedron for processing after the + // holes have been carved. + regiontets[i] = searchtet; + } + } + } + // Initialize a pool to be used for regional attrs, and/or regional + // volume constraints. + regionviri = new memorypool(sizeof(tetrahedron *), 1024, POINTER, 0); + // Find and set all regions. + for (i = 0; i < in->numberofregions; i++) { + if (regiontets[i].tet != dummytet) { + // Make sure the tetrahedron under consideration still exists. + // It may have been eaten by the virus. + if (!isdead(&(regiontets[i]))) { + // Put one tetrahedron in the virus pool. + infect(regiontets[i]); + regiontet = (tetrahedron **) regionviri->alloc(); + *regiontet = regiontets[i].tet; + // Apply one region's attribute and/or volume constraint. + regionplague(regionviri, in->regionlist[5 * i + 3], + in->regionlist[5 * i + 4]); + // The virus pool should be empty now. + } } - break; // This edge already exists. } + // Free up memory. + delete [] regiontets; + delete regionviri; } - puninfect(pa); - puninfect(pb); + // Now acutually remove the outside and hole tets. + removeholetets(holeviri); + // The mesh is nonconvex now. + nonconvex = 1; + + if (b->regionattrib) { + if (b->regionattrib > 1) { + // -AA switch. Assign each tet a region number (> 0). + assignregionattribs(); + } + // Note the fact that each tetrahedron has an additional attribute. + in->numberoftetrahedronattributes++; + } - return i < (int) fixededgelist->objects; + // Free up memory. + delete holeviri; } +// +// End of carving out holes and concavities routines +// + +// +// Begin of boundary Steiner points removing routines +// + /////////////////////////////////////////////////////////////////////////////// // // -// removeedgebyflips() Remove an edge by flips. // +// replacepolygonsubs() Substitute the subfaces of a polygon. // +// // +// 'oldshlist' (T_old) contains the old subfaces of P. It will be replaced // +// by 'newshlist' (T_new) of new subfaces. Each boundary edge of P is bonded // +// to 'dummysh' in T_new. // +// // +// Notice that Not every boundary edge of T_new is able to bond to a subface,// +// e.g., when it is a segment recovered by removing a Steiner point in it. // // // /////////////////////////////////////////////////////////////////////////////// -bool tetgenmesh::removeedgebyflips(triface* remedge, int *flipcount) +void tetgenmesh::replacepolygonsubs(list* oldshlist, list* newshlist) { - triface abcd; //, badc; // Tet configuration at edge ab. - // triface baccasing, abdcasing; - triface abtetlist[21]; // Old configuration at ab, save maximum 20 tets. - triface bftetlist[21]; // Old configuration at bf, save maximum 20 tets. - triface newtetlist[90]; // New configuration after removing ab. - face checksh; + face newsh, oldsh, spinsh; + face casingout, casingin; + face checkseg; point pa, pb; - bool remflag, subflag; - int n, n1, m, i; //, j; - - triface newtet; // For update point-to-tet map. - point *ppt; - int j; - - pa = org(*remedge); - pb = dest(*remedge); - - if (b->verbose > 1) { - printf(" Remove edge (%d, %d).\n", pointmark(pa), pointmark(pb)); - } - - // Get the tets configuration at ab. Collect maximum 10 tets. - subflag = false; - abcd = *remedge; - adjustedgering(abcd, CW); - n = 0; - abtetlist[n] = abcd; - do { - // Is the list full? - if (n == 20) break; - // Stop if a subface appears. - tspivot(abtetlist[n], checksh); - if (checksh.sh != dummysh) { - // ab is either a segment or a facet edge. The latter case is not - // handled yet! An edge flip is needed. - if (b->verbose > 1) { - printf(" Can't remove a fixed face (%d, %d, %d).\n", pointmark(pa), - pointmark(pb), pointmark(apex(abtetlist[n]))); - } - subflag = true; break; // return false; - } - // Get the next tet at ab. - if (!fnext(abtetlist[n], abtetlist[n + 1])) { - // This edge is on the hull (2-to-2 flip case). - subflag = true; break; // assert(0); // Not handled yet. - } - n++; - } while (apex(abtetlist[n]) != apex(abcd)); - - if (subflag) { - // The face link contains subfaces, stop. - return false; - } - - // Do not flip this edge if it has been fixed (for recovering a face). - if (check4fixededge(pa, pb)) { - return false; - } - - // 2 < n < 20. - if (n == 3) { - // There are three tets at ab. Try to do a flip32 at ab. - remflag = removeedgebyflip32(NULL, abtetlist, newtetlist, NULL); - } else if ((n > 3) && (n <= b->maxflipedgelinksize)) { - // Four tets case. Try to do edge transformation. - remflag = removeedgebytranNM(NULL,n,abtetlist,newtetlist,NULL,NULL,NULL); - } else { - if (b->verbose > 1) { - printf(" !! Unhandled case: n = %d.\n", n); - } - remflag = false; - } - - if (remflag) { - // Delete the old tets. - for (i = 0; i < n; i++) { - tetrahedrondealloc(abtetlist[i].tet); - } - m = (n - 2) * 2; // The numebr of new tets. - if (b->verbose > 1) { - printf(" Done flip %d-to-%d.\n", n, m); - } - // Update the point-to-tet map - for (i = 0; i < m; i++) { - newtet = newtetlist[i]; - ppt = (point *) &(newtet.tet[4]); - for (j = 0; j < 4; j++) { - setpoint2tet(ppt[j], encode(newtet)); - } - } - *flipcount = *flipcount + 1; - return true; - } + int i, j, k, l; - if (n <= b->maxflipedgelinksize) { - // Try to do a combination of flips. - n1 = 0; - remflag = removeedgebycombNM(NULL, n, abtetlist, &n1, bftetlist, - newtetlist, NULL); - if (remflag) { - // Delete the old tets. - for (i = 0; i < n; i++) { - tetrahedrondealloc(abtetlist[i].tet); - } - for (i = 0; i < n1; i++) { - if (!isdead(&(bftetlist[i]))) { - tetrahedrondealloc(bftetlist[i].tet); + for (i = 0; i < newshlist->len(); i++) { + // Get a new subface s. + newsh = * (face *)(* newshlist)[i]; + // Check the three edges of s. + for (k = 0; k < 3; k++) { + spivot(newsh, casingout); + // Is it a boundary edge? + if (casingout.sh == dummysh) { + // Find the old subface s_o having the same edge as s. + pa = sorg(newsh); + pb = sdest(newsh); + for (j = 0; j < oldshlist->len(); j++) { + oldsh = * (face *)(* oldshlist)[j]; + for (l = 0; l < 3; l++) { + if (((sorg(oldsh) == pa) && (sdest(oldsh) == pb)) || + ((sorg(oldsh) == pb) && (sdest(oldsh) == pa))) break; + senextself(oldsh); + } + if (l < 3) break; } - } - m = ((n1 - 2) * 2 - 1) + (n - 3) * 2; // The number of new tets. - if (b->verbose > 1) { - printf(" Done flip %d-to-%d (n-1=%d, n1=%d). ", n+n1-2, m, n-1,n1); - printf("\n"); - } - // Update the point-to-tet map - for (i = 0; i < m; i++) { - newtet = newtetlist[i]; - ppt = (point *) &(newtet.tet[4]); - for (j = 0; j < 4; j++) { - setpoint2tet(ppt[j], encode(newtet)); + // Is there a matched edge? + if (j < oldshlist->len()) { + // Get the neighbor subface s_out. + spivot(oldsh, casingout); + sspivot(oldsh, checkseg); + if (checkseg.sh != dummysh) { + // A segment. Insert s into the face ring, ie, s_in -> s -> s_out. + if (oldsh.sh != casingout.sh) { + // s is not bonded to itself. + spinsh = casingout; + do { + casingin = spinsh; + spivotself(spinsh); + } while (sapex(spinsh) != sapex(oldsh)); + assert(casingin.sh != oldsh.sh); + // Bond s_in -> s -> s_out (and dissolve s_in -> s_old -> s_out). + sbond1(casingin, newsh); + sbond1(newsh, casingout); + } else { + // Bond newsh -> newsh. + sbond(newsh, newsh); + } + // Bond the segment. + ssbond(newsh, checkseg); + } else { + // Bond s <-> s_out (and dissolve s_out -> s_old). + sbond(newsh, casingout); + } + // Unbound oldsh to indicate it's neighbor has been replaced. + // It will be used to indentfy the edge in the inverse. + sdissolve(oldsh); + ssdissolve(oldsh); } } - *flipcount = *flipcount + 1; - return true; + // Go to the next edge of s. + senextself(newsh); } } - - return false; } /////////////////////////////////////////////////////////////////////////////// // // -// removefacebyflips() Remove a face by a sequence of flips. // +// orientnewsubs() Orient new subfaces facing to the inside of cavity. // // // -// The face should not be a subface. // +// 'newshlist' contains new subfaces of the cavity C (created by re-triangu- // +// lation the polygon P). They're not necessary facing to the inside of C. // +// 'orientsh', faces to the inside of C, is used to adjust new subfaces. The // +// normal of the new subfaces is returned in 'norm'. // // // /////////////////////////////////////////////////////////////////////////////// -bool tetgenmesh::removefacebyflips(triface* remface, int *flipcount) +void tetgenmesh::orientnewsubs(list* newshlist, face* orientsh, REAL* norm) { - triface neightet, checkface1, checkface2; - face checksh1, checksh2; - point pa, pb, pc, pd, pe; - enum interresult dir; - REAL ori; - int types[2], poss[4]; + face *newsh; + point pa, pb, pc; + REAL ref[3], ori, len; int i; - adjustedgering(*remface, CCW); - sym(*remface, neightet); - if (neightet.tet == dummytet) { - // A boundary face. It is not flipable. - return false; - } - pd = oppo(*remface); - pe = oppo(neightet); - - if (b->verbose > 1) { - printf(" Remove face (%d, %d, %d) %d, %d\n", pointmark(org(*remface)), - pointmark(dest(*remface)), pointmark(apex(*remface)), - pointmark(pd), pointmark(pe)); - } - - // Do not remove this face if it is a fixed face. - tspivot(*remface, checksh1); - if (checksh1.sh != dummysh) { - if (b->verbose > 1) { - printf(" Can't remove a fixed face (%d, %d, %d)\n", - pointmark(org(*remface)), pointmark(dest(*remface)), - pointmark(apex(*remface))); - } - return false; - } - - // Check if edge [d, e] intersects the flip face [a, b, c]. - for (i = 0; i < 3; i++) { - pa = org(*remface); - pb = dest(*remface); - pc = apex(*remface); - ori = orient3d(pa, pb, pd, pe); - if (ori <= 0) break; // Coplanar or Above. - enextself(*remface); - } - - if (i == 3) { - // A 2-to-3 flip is found. - // Regist the flipping face. - if (!registerelemflip(T23, pa, pb, pc, pd, pe, dummypoint)) { - // Detected a potential flip loop. - return false; - } - // Do a 2-to-3 flip. - flip23(remface, NULL); - *flipcount = *flipcount + 1; - return true; - } - - if (ori == 0) { - // Check if [a, b] is a hull edge. If so a flip22() could apply. - fnext(*remface, checkface1); - tspivot(checkface1, checksh1); - symedge(*remface, neightet); - fnext(neightet, checkface2); - tspivot(checkface2, checksh2); - // First check if it is a protected face. - if ((checksh1.sh == dummysh) && (checksh2.sh == dummysh)) { - // Check if it is a hull face. - symself(checkface1); - symself(checkface2); - if ((checkface1.tet == dummytet) && (checkface2.tet == dummytet)) { - // Check if the edge [a, b] intersects [d, e] in its interior. - if (tri_edge_test(pa, pb, pc, pd, pe, NULL, 1, types, poss)) { - dir = (enum interresult) types[0]; - if (dir == INTEREDGE) { - // Edge [d, e] intersects [a, b, c]. A flip 2-to-2 is found. - // Check if the edge [a, b] is a fixed one (for recovering a face). - if (check4fixededge(pa, pb)) { - // [a, b] is a fixed edge. Stop the flip. - return false; - } - // Regist this flip. - if (!registerelemflip(T22, pa, pb, dummypoint, pd, pe, - dummypoint)) { - // Detected a potential flip loop. - return false; - } - // We can flip this edge [a, b]. - flip22(remface, NULL); - *flipcount = *flipcount + 1; - return true; - } else { - // Either a or b is collinear with edge [d, e]. NOT flipable. - assert(dir == INTERVERT); - return false; - } - } else { - // [a,b] does not intersect with [d, e]. Don't do a 2-to-2 flip. - // See an example in dbg/dump-nflip22-case.lua - return false; - } - } - } else { - if (b->verbose > 1) { - printf(" Can't remove a fixed face (%d, %d, %d).\n", pointmark(pa), - pointmark(pb), pointmark(apex(neightet))); - } - return false; + // Calculate the normal of 'orientsh'. + pa = sorg(*orientsh); + pb = sdest(*orientsh); + pc = sapex(*orientsh); + facenormal(pa, pb, pc, norm, &len); + for (i = 0; i < 3; i++) ref[i] = pa[i] + norm[i]; + for (i = 0; i < 3; i++) norm[i] /= len; + + // Orient new subfaces. Let the normal above each one. + for (i = 0; i < newshlist->len(); i++) { + newsh = (face *)(* newshlist)[i]; + pa = sorg(*newsh); + pb = sdest(*newsh); + pc = sapex(*newsh); + ori = orient3d(pa, pb, pc, ref); + assert(ori != 0.0); + if (ori > 0.0) { + sesymself(*newsh); } } - - // The edge [d, e] does not intersect [a, b, c]. Try to flip edge [a, b]. - // Comment: We've found that the edge [a, b] is locally non-convex. It - // must be an interior edge. - return removeedgebyflips(remface, flipcount); } /////////////////////////////////////////////////////////////////////////////// // // -// recoveredgebyflips() Recover edge [a, b] by a sequence of flips. // +// constrainedflip() Flip a non-constrained face. // // // -// The edge to be recovered is from a = org(*searchtet) to b. Return TRUE if // -// the edge [a, b] is recovered, and retruned in 'searchtet', otherwise, // -// return FALSE. // +// 'flipface' f (abc) is a face we want to flip. In addition, if 'front' is // +// given (not a NULL), f is a crossface. f may not be flippable if it is one // +// of the following cases: // +// (1) f has an aux subface attached; // +// (2) f is on the convex hull; // +// (3) f is not locally Delaunay (f must be recovered by a previous flip, // +// we should keep it, otherwise, we may fall into a flip loop); // +// (4) f is T32 at ab, but abd or abe has an aux subface attached; // +// (5) f is T22 or T44 at ab, but abd, or abe, or abf has an aux subface // +// attached; // +// (6) f is unflipable at ab, and abd, abe, ... are all unflippable due to // +// the cases (1) - (5). // +// If f is a crssface ('front' != NULL) and it is unflipable due to case (3),// +// (4), (5) and (6). Try to flip the next crossing face of front first. // // // /////////////////////////////////////////////////////////////////////////////// -bool tetgenmesh::recoveredgebyflips(triface *searchtet,point pb,int *flipcount) +bool tetgenmesh::constrainedflip(triface* flipface, triface* front, + queue* flipque) { - triface remface; - point pa; - enum interresult dir; - bool success; - - pa = org(*searchtet); + triface symface, spintet; + face checksh; + point pa, pb, pc, pd, pe; + enum fliptype fc; + REAL sign; + bool doflip; + int ia, ib, ic, id, ie; + int i; - if (b->verbose > 1) { - printf(" Recover edge (%d, %d)\n", pointmark(pa), pointmark(pb)); + // (1) Is f protected by an (auxilary) subface? + tspivot(*flipface, checksh); + if (checksh.sh != dummysh) return false; + // (2) Is f on the convex hull? + sym(*flipface, symface); + if (symface.tet == dummytet) return false; + // (3) Is f not locally Delaunay? + adjustedgering(*flipface, CCW); + pa = dest(*flipface); + pb = org(*flipface); + pc = apex(*flipface); + pd = oppo(*flipface); + pe = oppo(symface); + // if (symbolic) { + ia = pointmark(pa); + ib = pointmark(pb); + ic = pointmark(pc); + id = pointmark(pd); + ie = pointmark(pe); + sign = insphere_sos(pa, pb, pc, pd, pe, ia, ib, ic, id, ie); + assert(sign != 0.0); + // } else { + // sign = insphere(pa, pb, pc, pd, pe); + // } + if (sign <= 0.0) { + // Get the fliptype of f. + checksubfaces = 0; // switch off subface test. + fc = categorizeface(*flipface); + checksubfaces = 1; // switch on subface test. + if (fc == T23) { + doflip = true; + // Avoid one tet created by the 2-3 flip is nearly degenerate. + /* pc = oppo(*flipface); + pd = oppo(symface); + adjustedgering(*flipface, CCW); + for (i = 0; i < 3; i++) { + pa = org(*flipface); + pb = dest(*flipface); + ori = orient3d(pa, pb, pc, pd); + if (iscoplanar(pa, pb, pc, pd, ori, b->epsilon)) { + doflip = false; break; + } + enextself(*flipface); + } */ + if (doflip) { + flip23(flipface, flipque); + return true; + } + } else if (fc == T32) { + // (4) Is abd, or abe protected? + doflip = true; + spintet = *flipface; + for (i = 0; i < 2; i++) { + fnextself(spintet); + tspivot(spintet, checksh); + if (checksh.sh != dummysh) { + doflip = false; break; // f is protected. Unflipable. + } + } + if (doflip) { + flip32(flipface, flipque); + return true; + } + } else if (fc == T22 || fc == T44) { + // (5) Is abd, abe, or abf protected? + doflip = true; + if (fc == T22) { + for (i = 0; i < 2; i++) { + spintet = *flipface; + if (i == 1) { + esymself(spintet); + } + fnextself(spintet); + tspivot(spintet, checksh); + if (checksh.sh != dummysh) { + doflip = false; break; // f is protected. Unflipable. + } + } + } else if (fc == T44) { + spintet = *flipface; + for (i = 0; i < 3; i++) { + fnextself(spintet); + tspivot(spintet, checksh); + if (checksh.sh != dummysh) { + doflip = false; break; // f is protected. Unflipable. + } + } + } + if (doflip) { + flip22(flipface, flipque); + return true; + } + } else if (fc == N32) { + // Is f a crossface? + if (front != (triface *) NULL) { + // (6) Is any obstacle face (abd, or abe, ...) flipable? + spintet = *flipface; + while (fnextself(spintet)) { + if (apex(spintet) == apex(*flipface)) break; + // Check if spintet is flipable, no recursive. + if (constrainedflip(&spintet, NULL, flipque)) { + // One obstacle face has been flipped. + return true; + } + // Unflipable. Go to the next obstacle face. + findedge(&spintet, org(*flipface), dest(*flipface)); + } + } + } } - assert(elemfliplist->objects == 0l); - - while (1) { - - // Go to the intersected face, try to flip it. - enextfnext(*searchtet, remface); - - // Try to remove this crossing face. - success = removefacebyflips(&remface, flipcount); - if (!success) break; - - point2tetorg(pa, *searchtet); - assert(org(*searchtet) == pa); - dir = finddirection2(searchtet, pb); - if (dir == INTERVERT) { - break; // The edge has found. + // f is unflipable. Is f a crossface? + if (front != (triface *) NULL) { + // Look if there is another crossface. + pa = org(*front); + pb = dest(*front); + pc = apex(*front); + // sym(*flipface, symface); + // Have we reach the end of abc (We've started from edge ab). + if (oppo(symface) != pc) { + adjustedgering(symface, CCW); + for (i = 0; i < 3; i++) { + fnext(symface, spintet); + // Is c ahead of this face? + sign = orient3d(org(spintet), dest(spintet), apex(spintet), pc); + if (sign < 0.0) { + if (tritritest(&spintet, pa, pb, pc)) { + if (b->verbose > 2) { + printf(" Next crossface (%d, %d, %d).\n", + pointmark(org(spintet)), pointmark(dest(spintet)), + pointmark(apex(spintet))); + } + return constrainedflip(&spintet, front, flipque); + // return constrainedflip(&spintet, NULL, flipque); + } + } + enextself(symface); + } } - - } // while (1) - - // Clear the recorded flip list. - elemfliplist->restart(); - - return success; + } + return false; } /////////////////////////////////////////////////////////////////////////////// // // -// recoverfacebyflips() Recover a missing face by a sequence of flips. // +// recoverfront() Recover a missing front by flips. // // // -// Assume that at least one of the edges of the face exists in the current // -// mesh. The face is recovered by continusly removing all crossing edges of // -// this face. // +// 'front' f is missing in D - it was crossed by faces of D. The cross faces // +// may be flippable, so f can be recovered by flipping them away. // // // /////////////////////////////////////////////////////////////////////////////// -bool tetgenmesh::recoverfacebyflips(triface* front, int *flipcount) +bool tetgenmesh::recoverfront(triface* front, list* newtetlist, queue* flipque) { - triface searchtet, spintet, bdrytet; - triface remedge, remface; - point pa, pb, pc, pd, pe, *ppt; - enum interresult dir; - bool success; + triface idfront, starttet, spintet; + point pa, pb, pc, pd, ref; + enum locateresult loc; + enum finddirectionresult col; + REAL ori, ori1, ori2, sign; int hitbdry; - int i; - - if (b->verbose > 1) { - printf(" Recover face (%d, %d, %d)\n", pointmark(org(*front)), - pointmark(dest(*front)), pointmark(apex(*front))); - } - - assert(fixededgelist->objects == 0l); + int i, j; - // First recover three edges of this face. + // Find an existing edge of f in D to start with. for (i = 0; i < 3; i++) { pa = org(*front); pb = dest(*front); - pc = apex(*front); // The apex. - point2tetorg(pa, searchtet); - assert(org(searchtet) == pa); - dir = finddirection2(&searchtet, pb); - if (dir == BELOWHULL2) { - // Detect an outside front. This happens when a new subface created by - // re-triangulating is actually outside the cavity. The gluefront() - // is not called in this case. There must be an existing face which - // lies outside the cavity that matches this subface. - // An example is dump-SteinerRemoval-case2.lua. 2009-07-06. - // fixededgelist->restart(); - // return false; - assert(0); - } - if (dir != INTERVERT) { - if (!recoveredgebyflips(&searchtet, pb, flipcount)) { - // Failed to recover edge [a, b], so does the face. - fixededgelist->restart(); - return false; - } - } - // DIR == INTERVERT - if (dest(searchtet) != pb) { - // There eixsts a collonear edge but not the desired one. - // Failed to recover edge [a, b], so does the face. - fixededgelist->restart(); - return false; + // Get a tet for searching. + idfront = recenttet; + // Make sure the tet is valid (flip32() may kill a tet). + if (isdead(&idfront)) { + // The tet is dead. Get a live tet in D. !!! + for (j = 0; j < newtetlist->len(); j++) { + recenttet = * (triface *)(* newtetlist)[j]; + if (!isdead(&recenttet)) break; + } + assert(j < newtetlist->len()); + } + loc = preciselocate(pa, &idfront, (long) newtetlist->len()); + if (loc != ONVERTEX) { + // Do a brute-force search in D. + for (j = 0; j < newtetlist->len(); j++) { + idfront = * (triface *)(* newtetlist)[j]; + if (isdead(&idfront)) continue; + if (findorg(&idfront, pa)) break; + } + assert(j < newtetlist->len()); // a must belong to one tet. } - // Save this edge to avoid flipping it away. - fixededgelist->newindex((void **) &ppt); - ppt[0] = pa; - ppt[1] = pb; - // Go to the next edge. + recenttet = idfront; + // Search for a tet having edge ab. + col = finddirection(&idfront, pb, (long) newtetlist->len()); + if (col == BELOWHULL) { + // Do a brute-force search in D. + for (j = 0; j < newtetlist->len(); j++) { + idfront = * (triface *)(* newtetlist)[j]; + if (isdead(&idfront)) continue; + if (findorg(&idfront, pa)) { + assert(org(idfront) == pa); + if (dest(idfront) == pb) { + col = RIGHTCOLLINEAR; break; + } else if (apex(idfront) == pb) { + col = LEFTCOLLINEAR; break; + } else if (oppo(idfront) == pb) { + col = TOPCOLLINEAR; break; + } + } + } + } + if (col == RIGHTCOLLINEAR) { + // b is just the destination. + } else if (col == LEFTCOLLINEAR) { + enext2self(idfront); + esymself(idfront); + } else if (col == TOPCOLLINEAR) { + fnextself(idfront); + enext2self(idfront); + esymself(idfront); + } + if (dest(idfront) == pb) break; // Found. + // Missing. Go to the next edge of f. enextself(*front); } + if (i == 3) { + // All three edges of f are missing - unrecoverable. + return false; + } - assert(elemfliplist->objects == 0l); - - while (1) { - - success = false; - - // Adjust edge [a->b] to be in the CCW edge ring. - adjustedgering(searchtet, CCW); - if (org(searchtet) != pa) { - fnextself(searchtet); - esymself(searchtet); + // Search for a tet having f (abc). + pc = apex(*front); + spintet = idfront; + hitbdry = 0; + do { + if (apex(spintet) == pc) { + // Found abc. Insert an auxilary subface s at idfront. + insertauxsubface(front, &spintet); + return true; } - assert(org(searchtet) == pa); - assert(dest(searchtet) == pb); - - spintet = searchtet; - hitbdry = 0; - do { - if (apex(spintet) == pc) { - // Found abc. Insert an auxilary subface s at idfront. - insertauxsubface(front, &spintet); - success = true; - break; // return true; - } - if (!fnextself(spintet)) { - bdrytet = spintet; // Save the boundary face. - hitbdry ++; - if (hitbdry < 2) { - esym(searchtet, spintet); - if (!fnextself(spintet)) { - hitbdry++; - } + if (!fnextself(spintet)) { + hitbdry ++; + if (hitbdry < 2) { + esym(idfront, spintet); + if (!fnextself(spintet)) { + hitbdry ++; } } - if (apex(spintet) == apex(searchtet)) break; - } while (hitbdry < 2); - - if (success) break; + } + if (apex(spintet) == apex(idfront)) break; + } while (hitbdry < 2); - if (hitbdry > 0) { - // Adjust searchtet to be the boundary face at edge [pa->pb]. - searchtet = bdrytet; - adjustedgering(searchtet, CCW); - pa = org(searchtet); - pb = dest(searchtet); - } - - // Comments: - // Remember that 'front' is the face [a, b, c]. 'spintet' is a tet - // [a, b, d, e] at edge [a->b]. - // We first check if the edge [d, e] intersects face [a, b, c], if it - // does, we try to flip the edge [d, e] away. - // We then check if the edge [b, c] intersects face [a, d, e], if it - // does, we try to flip the face [a, d, e] away. - // We then check if the edge [a, c] intersects face [b, d, e], if it - // does, we try to flip the face [b, d, e] away. - - // Search a crossing edge/face and try to flip it away. - remedge.tet = remface.tet = NULL; - spintet = searchtet; - hitbdry = 0; - do { - pd = apex(spintet); - pe = oppo(spintet); - // Check if edge [d, e] intersects [a, b, c]. - if (tri_edge_test(pa, pb, pc, pd, pe, NULL, 0, NULL, NULL)) { - remedge = spintet; - enextfnextself(remedge); - enextself(remedge); // Edge [pd->pe]. - break; + // Search for a crossing face to flip. + pd = apex(idfront); + assert(pd != pc); + // Decide the orientation of d with abc. + ori = orient3d(pa, pb, pc, pd); + if (ori < 0.0) { + // d is above abc. Rotate downwards. + esym(idfront, starttet); + sign = -1.0; + } else if (ori > 0.0) { + // d is below abc. Rotate upwards. + starttet = idfront; + sign = 1.0; + } else { + assert(ori == 0.0); + // d is coplanar with abc. Do abc and abd intersect? + ref = oppo(idfront); + ori1 = orient3d(pa, pb, ref, pc); + ori2 = orient3d(pa, pb, ref, pd); + assert(ori1 * ori2 != 0.0); + if (ori1 * ori2 > 0) { + // abc and abd intersect. There're two possible intersections: + // ad and bc, or ac and bd. Find it out. + ori1 = orient3d(pb, pc, ref, pd); + ori2 = orient3d(pb, pc, ref, pa); + assert(ori1 * ori2 != 0.0); + if (ori1 * ori2 > 0) { + // ac intersects bd. + enextself(idfront); // go to edge bd. + } else { + // ad intersects bc. + enext2self(idfront); // go to edge ad. } - // Check if [b, c] intersects [a, d, e]. - if (!iscollinear(pa, pd, pe, b->epsilon)) { - if (tri_edge_test(pa, pd, pe, pb, pc, NULL, 0, NULL, NULL)) { - remface = spintet; - enext2fnextself(remface); // Face [a, d, e]. - break; - } + adjustedgering(idfront, CCW); + fnextself(idfront); // face ade or bce need a 4-to-4 flip. + if (b->verbose > 2) { + printf(" Get crossface (%d, %d, %d).\n", pointmark(org(idfront)), + pointmark(dest(idfront)), pointmark(apex(idfront))); + } + if (constrainedflip(&idfront, front, flipque)) { + // A crossface has been flipped. Continue to recover f. + return recoverfront(front, newtetlist, flipque); + } + // Unable to recover f. + return false; // sign = 0.0; + } else { + // Not intersect. We can go either direction. + starttet = idfront; + if (fnextself(starttet)) { + // Choose to rotate upwards. + sign = 1.0; + } else { + // Hit convex hull. Choose to rotate downwrads. + esym(idfront, starttet); + sign = -1.0; + } + } + } + + assert(sign != 0.0); + if (sign == -1) { + // The edge ab has be changed. Reverse it. + pa = org(starttet); + pb = dest(starttet); + // The sign has been reversed as well. + sign = -sign; + } + // Rotate face abd around edge ab. Moreover, we've chosen the rotate + // direction such that no convex hull face will be reach. + spintet = starttet; + while (fnextself(spintet)) { + pd = apex(spintet); + assert(pd != pc); + // Check if the orientation of d (with abc) has changed. + ori = orient3d(pa, pb, pc, pd); + if (ori == 0.0) { + // abc and abd must coplanar intersect (4-to-4 flip is needed). + ref = oppo(spintet); + ori1 = orient3d(pb, pc, ref, pd); + ori2 = orient3d(pb, pc, ref, pa); + assert(ori1 * ori2 != 0.0); + if (ori1 * ori2 > 0) { + // ac intersects bd. + enextself(spintet); // go to edge bd. + } else { + // ad intersects bc. + enext2self(spintet); // go to edge ad. } - if (!iscollinear(pb, pd, pe, b->epsilon)) { - // Check if [a, c] intersects [b, d, e]. - if (tri_edge_test(pb, pd, pe, pa, pc, NULL, 0, NULL, NULL)) { - remface = spintet; - enextfnextself(remface); // Face [b, d, e]. - break; + adjustedgering(spintet, CCW); + fnextself(spintet); // face ade or bce need a 4-to-4 flip. + if (b->verbose > 2) { + printf(" Get crossface (%d, %d, %d).\n", pointmark(org(spintet)), + pointmark(dest(spintet)), pointmark(apex(spintet))); + } + if (constrainedflip(&spintet, front, flipque)) { + // A crossface has been flipped. Continue to recover f. + return recoverfront(front, newtetlist, flipque); + } + // Unable to recover f. + return false; // sign = 0.0; + } else if (ori * sign < 0.0) { + // Sign has changed. The face dea or deb must cross abc. + adjustedgering(spintet, CCW); + enextself(spintet); + for (i = 0; i < 2; i++) { + // Get the face dea or deb. + fnext(spintet, starttet); + if (tritritest(&starttet, pa, pb, pc)) { + if (b->verbose > 2) { + printf(" Get crossface (%d, %d, %d).\n", + pointmark(org(starttet)), pointmark(dest(starttet)), + pointmark(apex(starttet))); + } + if (constrainedflip(&starttet, front, flipque)) { + // A crossface has been flipped. Continue to recover f. + return recoverfront(front, newtetlist, flipque); + } } + enextself(spintet); } - tfnextself(spintet); - if (spintet.tet == dummytet) { - break; // Meet boundary. - } - if (apex(spintet) == apex(searchtet)) break; - } while (hitbdry < 2); - - if (remedge.tet != NULL) { - // Try to remove this crossing edge. - success = removeedgebyflips(&remedge, flipcount); - } else if (remface.tet != NULL) { - /*// Do not flip it if it is a subface. - tspivot(remface, checksh); - if (checksh.sh != dummysh) { - return false; - }*/ - success = removefacebyflips(&remface, flipcount); - } else { - // Haven't found a crossing edge or face. - success = false; + // Unable to recover f. + return false; } + } + // Impossible to be here. + assert(0); + return false; +} - if (!success) break; - - point2tetorg(pa, searchtet); - assert(org(searchtet) == pa); - dir = finddirection2(&searchtet, pb); - assert(dest(searchtet) == pb); - - } // while (1) +/////////////////////////////////////////////////////////////////////////////// +// // +// repairflips() Flip non-Delaunay and non-constrained faces. // +// // +/////////////////////////////////////////////////////////////////////////////// - // Clear the recored flips. - elemfliplist->restart(); - // Clear the fixed edges. - fixededgelist->restart(); +void tetgenmesh::repairflips(queue* flipque) +{ + badface *qface; + triface flipface, symface, spintet; + face checksh; + point pa, pb, pc, pd, pe; + enum fliptype fc; + REAL sign; + long flipcount; + bool doflip; + int ia, ib, ic, id, ie; + int i; - return success; + if (b->verbose > 1) { + printf(" Repair flip %ld faces.\n", flipque->len()); + } + flipcount = flip23s + flip32s + flip22s + flip44s; + // Loop until the queue is empty. + while (!flipque->empty()) { + qface = (badface *) flipque->pop(); + flipface = qface->tt; + // Check the validity of this face. + if (isdead(&flipface) || flipface.tet == dummytet || + (org(flipface) != qface->forg) || + (dest(flipface) != qface->fdest) || + (apex(flipface) != qface->fapex) || + (oppo(flipface) == (point) NULL)) continue; + // (1) Is f protected by an (auxilary) subface? + tspivot(flipface, checksh); + if (checksh.sh != dummysh) continue; + // (2) Is f on the convex hull? + sym(flipface, symface); + if (symface.tet == dummytet) continue; + // For positive orientation that insphere() test requires. + adjustedgering(flipface, CW); + pa = org(flipface); + pb = dest(flipface); + pc = apex(flipface); + pd = oppo(flipface); + pe = oppo(symface); + // if (symbolic) { + ia = pointmark(pa); + ib = pointmark(pb); + ic = pointmark(pc); + id = pointmark(pd); + ie = pointmark(pe); + sign = insphere_sos(pa, pb, pc, pd, pe, ia, ib, ic, id, ie); + assert(sign != 0.0); + // } else { + // sign = insphere(pa, pb, pc, pd, pe); + // } + if (sign > 0.0) { + // f is non-lcally Delaunay. Get the fliptype of f. + checksubfaces = 0; // switch off subface test. + fc = categorizeface(flipface); + checksubfaces = 1; // switch on subface test. + if (fc == T23) { + doflip = true; + // Avoid to create a nearly degenerate tet. + /* pc = oppo(flipface); + pd = oppo(symface); + adjustedgering(flipface, CCW); + for (i = 0; i < 3; i++) { + pa = org(flipface); + pb = dest(flipface); + ori = orient3d(pa, pb, pc, pd); + if (iscoplanar(pa, pb, pc, pd, ori, b->epsilon)) { + doflip = false; break; + } + enextself(flipface); + } */ + if (doflip) { + flip23(&flipface, flipque); + } + } else if (fc == T32) { + // (4) Is abd, or abe protected? + doflip = true; + spintet = flipface; + for (i = 0; i < 2; i++) { + fnextself(spintet); + tspivot(spintet, checksh); + if (checksh.sh != dummysh) { + doflip = false; break; // f is protected. Unflipable. + } + } + if (doflip) { + flip32(&flipface, flipque); + } + } else if (fc == T22 || fc == T44) { + // (5) Is abd, abe, or abf protected? + doflip = true; + if (fc == T22) { + for (i = 0; i < 2; i++) { + spintet = flipface; + if (i == 1) { + esymself(spintet); + } + fnextself(spintet); + tspivot(spintet, checksh); + if (checksh.sh != dummysh) { + doflip = false; break; // f is protected. Unflipable. + } + } + } else if (fc == T44) { + spintet = flipface; + for (i = 0; i < 3; i++) { + fnextself(spintet); + tspivot(spintet, checksh); + if (checksh.sh != dummysh) { + doflip = false; break; // f is protected. Unflipable. + } + } + } + if (doflip) { + flip22(&flipface, flipque); + } + } + } + } + flipcount = flip23s + flip32s + flip22s + flip44s - flipcount; + if (b->verbose > 1) { + printf(" %ld flips.\n", flipcount); + } } /////////////////////////////////////////////////////////////////////////////// @@ -24449,13 +24626,10 @@ bool tetgenmesh::recoverfacebyflips(triface* front, int *flipcount) bool tetgenmesh::constrainedcavity(triface* oldtet, list* floorlist, list* ceillist, list* ptlist, list* frontlist, list* misfrontlist, - list* newtetlist, list* gluetetlist, list* glueshlist, queue* flipque) + list* newtetlist, queue* flipque) { triface misfront, newtet; - point pointptr; // Used with gluetetlist. - bool success; - int misfacecount; - int flipcount, totalflipcount; + long facenum; int i; if (b->verbose > 1) { @@ -24463,53 +24637,58 @@ bool tetgenmesh::constrainedcavity(triface* oldtet, list* floorlist, floorlist->len(), ceillist->len(), ptlist->len()); } + // symbolic = 1; + // Initialize the cavity C. - initializecavity(floorlist, ceillist, frontlist, ptlist, glueshlist); + initializecavity(floorlist, ceillist, frontlist); // Form the D of the vertices of C. - success = delaunizecavvertices(oldtet, ptlist, NULL, newtetlist, flipque); - - if (success) { - // Identify faces of C in D. - if (!identifyfronts(frontlist, misfrontlist, gluetetlist, glueshlist)) { - // Some faces are missing. - totalflipcount = 0; - // Try to recover missing faces by flips. - do { - misfacecount = misfrontlist->len(); - flipcount = 0; - for (i = 0; i < misfrontlist->len(); i++) { - // Get a missing front f. - misfront = * (triface *)(* misfrontlist)[i]; - // Let f face toward the inside of C. - // adjustedgering(misfront, CW); - // if (recoverfront(&misfront, newtetlist, flipque)) { - if (recoverfacebyflips(&misfront, &flipcount)) { - // f has been recovered. - frontlist->append(&misfront); - misfrontlist->del(i, 0); i--; - } - } - totalflipcount += flipcount; - // Have all faces been recovered? - if (misfrontlist->len() == 0) break; - // Continue the loop if some missing faces have been recovered. - } while (misfacecount > misfrontlist->len()); - // Retrieve new tets and purge dead tets in D. - retrievenewtets(newtetlist); - } - success = (misfrontlist->len() == 0); - } // if (success) + delaunizecavvertices(oldtet, ptlist, NULL, newtetlist, flipque); + + // Identify faces of C in D. + if (!identifyfronts(frontlist, misfrontlist, newtetlist)) { + // Some faces are missing. + recenttet = * (triface *)(* newtetlist)[0]; + assert((recenttet.tet != dummytet) && !isdead(&recenttet)); + // Try to recover missing faces by flips. + do { + facenum = misfrontlist->len(); + for (i = 0; i < misfrontlist->len(); i++) { + // Get a missing front f. + misfront = * (triface *)(* misfrontlist)[i]; + // Let f face toward the inside of C. + adjustedgering(misfront, CW); + if (b->verbose > 1) { + printf(" Recover face (%d, %d, %d).\n", pointmark(org(misfront)), + pointmark(dest(misfront)), pointmark(apex(misfront))); + } + if (recoverfront(&misfront, newtetlist, flipque)) { + // f has been recovered. + frontlist->append(&misfront); + misfrontlist->del(i, 0); i--; + } + // Flip non-locally non-constrained Delaunay faces. + repairflips(flipque); + } + // Have all faces been recovered? + if (misfrontlist->len() == 0) break; + // No! There are still un-recovered faces. Continue the loop if any + // face has been recovered. + } while (misfrontlist->len() < facenum); + // Retrieve new tets and purge dead tets in D. + retrievenewtets(newtetlist); + } + + // symbolic = 0; - if (success) { + if (misfrontlist->len() == 0) { // All fronts have identified in D. Get the shape of C by removing out // tets of C. 'misfrontlist' is reused for removing out tets. // Don't do flip since the new tets may get deleted later. - if (carvecavity(newtetlist, misfrontlist, gluetetlist, NULL)) { - return true; - } - } - - // { + carvecavity(newtetlist, misfrontlist, NULL); + // Recover locally Delaunay faces. + // flip(flipque, NULL); + return true; + } else { // Fail to tetrahedralize C. // Remove aux subfaces. detachauxsubfaces(newtetlist); @@ -24525,23 +24704,121 @@ bool tetgenmesh::constrainedcavity(triface* oldtet, list* floorlist, misfront = * (triface *)(* misfrontlist)[i]; frontlist->append(&misfront); } - // Some fronts have been removed from the front list (in gluefronts()). - // Maintain point-to-tet map. - for (i = 0; i < gluetetlist->len(); i++) { - // Get a tet t which lies outside the current cavity. - newtet = * (triface *)(* gluetetlist)[i]; - if (isdead(&newtet)) { - assert(0); - } - pointptr = org(newtet); - setpoint2tet(pointptr, encode(newtet)); - pointptr = dest(newtet); - setpoint2tet(pointptr, encode(newtet)); - pointptr = apex(newtet); - setpoint2tet(pointptr, encode(newtet)); - } return false; - // } + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// expandsteinercavity() Expand the cavity of a Steiner point. // +// // +// Expand the cavity C if there fronts (except fronts having subfaces) which // +// are either (nearly) coplanar or invisible by the Steiner point. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::expandsteinercavity(point steinpt, REAL eps, list* frontlist, + list* oldtetlist) +{ + triface front, symfront, newfront, oldfront; + face frontsh; + point pa, pb, pc; + REAL ori; + bool expflag, newflag; + int i, j; + + do { + expflag = false; + for (i = 0; i < frontlist->len(); i++) { + // Get a front f. + front = * (triface *)(* frontlist)[i]; + // f can be expanded if it is not a subface. + tspivot(front, frontsh); + if (frontsh.sh == dummysh) { + // Let f face to the inside of C. + adjustedgering(front, CW); + pa = org(front); + pb = dest(front); + pc = apex(front); + ori = orient3d(pa, pb, pc, steinpt); + if (ori != 0.0) { + if (iscoplanar(pa, pb, pc, steinpt, ori, eps)) { + ori = 0.0; // f is nearly coplanar with p. + } + } + if (ori >= 0.0) { + // f is either invisible or coplanar with p. + if (b->verbose > 2) { + printf(" Remove front (%d, %d, %d).\n", pointmark(pa), + pointmark(pb), pointmark(pc)); + } + frontlist->del(i, 1); + expflag = true; + break; + } + } + } + if (expflag) { + assert(!infected(front) && (oppo(front) != NULL)); + // Expand C at f by including new fronts. + adjustedgering(front, CCW); + for (i = 0; i < 3; i++) { + newflag = true; + // Get a new boundary n of the cavity. + fnext(front, symfront); + tspivot(symfront, frontsh); + sym(symfront, newfront); + if (frontsh.sh == dummysh) { + assert(newfront.tet != dummytet); + // Is n a front of the unexp. cavity? + if (infected(newfront)) { + for (j = 0; j < frontlist->len(); j++) { + oldfront = * (triface *)(* frontlist)[j]; + if ((oldfront.tet == symfront.tet) && + (oldfront.loc == symfront.loc)) { + // n is not a front anymore. + if (b->verbose > 2) { + printf(" Remove front (%d, %d, %d).\n", + pointmark(org(oldfront)), pointmark(dest(oldfront)), + pointmark(apex(oldfront))); + } + frontlist->del(j, 1); + newflag = false; + break; + } + } + } + } else { + // n is a subface. + if (newfront.tet == dummytet) { + sesymself(frontsh); + // Create a fake tet to hold n. + maketetrahedron(&newfront); + setorg(newfront, sorg(frontsh)); + setdest(newfront, sdest(frontsh)); + setapex(newfront, sapex(frontsh)); + setoppo(newfront, (point) NULL); + tsbond(newfront, frontsh); + } else { + // n should not be a front of cavity yet. + assert(!infected(newfront)); + } + } + if (newflag) { + if (b->verbose > 2) { + printf(" Add front (%d, %d, %d).\n", pointmark(org(newfront)), + pointmark(dest(newfront)), pointmark(apex(newfront))); + } + frontlist->append(&newfront); + } + enextself(front); + } + // Add f into oldtetlist (to be deleted). + infect(front); + oldtetlist->append(&front); + expcavcount++; + } + } while (expflag); } /////////////////////////////////////////////////////////////////////////////// @@ -24553,106 +24830,137 @@ bool tetgenmesh::constrainedcavity(triface* oldtet, list* floorlist, // // /////////////////////////////////////////////////////////////////////////////// -bool tetgenmesh::findrelocatepoint2(point steinpt, point relocatept, - REAL* normal, list* frontlist, list* oldtetlist) +bool tetgenmesh::findrelocatepoint(point sp, point np, REAL* n, + list* frontlist, list* oldtetlist) { - triface oldtet, front; + triface front; point pa, pb, pc; - REAL farpt[3], fcent[3], candpt[3], searchpt[3]; - REAL dist, factor; - REAL ori, stepx, minvol, minvol1; - bool stopflag; - // int iter; - int i, j; + REAL tp[3], tvol, mvol; + REAL ori, eps; + bool visible; + int i, j, k; if (b->verbose > 1) { - printf(" Find new location for point %d.\n", pointmark(steinpt)); + printf(" Find new location for point %d.\n", pointmark(sp)); } - // Calculate a far enough point on the normal direction. - for (i = 0; i < 3; i++) { - farpt[i] = steinpt[i] + longest * normal[i]; - } + // Avoid compilation warnings. + tvol = mvol = 0.0; + visible = false; - // Find the face in oldtet intersecting with the line steinpt->farpt. - for (i = 0; i < oldtetlist->len(); i++) { - oldtet = * (triface *)(* oldtetlist)[i]; - pa = org(oldtet); - pb = dest(oldtet); - pc = apex(oldtet); - if (tri_edge_test(pa, pb, pc, steinpt, farpt, farpt, 0, NULL, NULL)) { - // projpt2face(steinpt, pa, pb, pc, fcent); - // Find the intersected face. Calculate the intersection. - planelineint(pa, pb, pc, steinpt, farpt, fcent, &factor); - if (factor != 0) { // assert(factor != 0); - if (b->verbose > 1) { - printf("p:show_vector(%g, %g, %g, %g, %g, %g) -- L\n", steinpt[0], - steinpt[1], steinpt[2], fcent[0], fcent[1], fcent[2]); + eps = b->epsilon; + // Initialize np far enough from p (outside C). + for (i = 0; i < 3; i++) np[i] = sp[i] + longest * n[i]; + // Let tp = np; + for (i = 0; i < 3; i++) tp[i] = np[i]; + // Interation to adjust np until it is visible by all fronts. + j = 0; + do { + for (i = 0; i < frontlist->len(); i++) { + // Get a front face f. + front = * (triface *)(* frontlist)[i]; + // Let f face to the interior of C. + adjustedgering(front, CW); + pa = org(front); + pb = dest(front); + pc = apex(front); + ori = orient3d(pa, pb, pc, np); + visible = (ori < 0.0); + if (!visible) { + // A front is invisible by np. Move it towards p along the normal. + for (i = 0; i < 3; i++) np[i] = sp[i] + 0.5 * (sp[i] - np[i]); + // Failed if tp = np. + if ((tp[0] == np[0]) && (tp[1] == np[1]) && (tp[2] == np[2])) { + // Try to expand the cavity. + expandsteinercavity(sp, eps, frontlist, oldtetlist); + eps *= 10.0; + if (eps > b->epsilon * 1000.0) { + // printf("Internal error: Fail to relocate pt %d.\n",pointmark(sp)); + // internalerror(); + return false; + } + // Restart the point relocation. + for (i = 0; i < 3; i++) np[i] = sp[i] + longest * n[i]; + } + if (j % 2) { + // Set tp = np (at every 2 steps) to catch the stop state. + for (i = 0; i < 3; i++) tp[i] = np[i]; } break; + } else { + // Save the smallest volume. + if (i == 0) { + mvol = fabs(ori); + } else { + mvol = fabs(ori) < mvol ? fabs(ori) : mvol; + } } } - } - // There must be an interseced face. - if (i == oldtetlist->len()) { - return false; // assert(0); + j++; + } while (!visible); + + if (b->verbose > 1) { + printf(" %d iterations. minvol = %.12g.\n", j, mvol); } - // Start search a relocating point which maximize the min. vol. - dist = distance(steinpt, fcent); - stepx = dist / 100.0; // Divide the segment into 100 pieces. - minvol = 0; - stopflag = false; - for (i = 1; i < 100 && !stopflag; i++) { - // Calculate a candidate point. - for (j = 0; j < 3; j++) { - candpt[j] = steinpt[j] + (stepx * (double) i) * (fcent[j] - steinpt[j]); - } - minvol1 = 0; - for (j = 0; j < frontlist->len(); j++) { - front = * (triface *)(* frontlist)[j]; - // Let f face inside C. (f is a face of tet adjacent to C). - adjustedgering(front, CW); - pa = org(front); - pb = dest(front); - pc = apex(front); - ori = orient3d(pa, pb, pc, candpt); - if (ori >= 0) { - // An invisible front. (1) fails. - stopflag = true; - break; - } - if (j == 0) { - minvol1 = -ori; + // Continue to adjust np until the minimal volume of tets formed by + // fronts and np doesn't increase (all fronts are visible by np). + k = 0; + do { + j = 0; + do { + if (k == 0) { + // Initial tp := np + 0.9 * (p - np). Move toward p. + for (i = 0; i < 3; i++) tp[i] = sp[i] + 0.9 * (np[i] - sp[i]); } else { - if (minvol1 > (-ori)) { - minvol1 = -ori; + // Initial tp := np + 1.1 * (p - np). Move away from p. + for (i = 0; i < 3; i++) tp[i] = sp[i] + 1.1 * (np[i] - sp[i]); + } + // Get the minial volume formed by tp with one of the fronts. + for (i = 0; i < frontlist->len(); i++) { + // Get a front face f. + front = * (triface *)(* frontlist)[i]; + // Let f face to the interior of C. + adjustedgering(front, CW); + pa = org(front); + pb = dest(front); + pc = apex(front); + ori = orient3d(pa, pb, pc, tp); + visible = (ori < 0.0); + if (visible) { + // Save the smallest volume. + if (i == 0) { + tvol = fabs(ori); + } else { + tvol = fabs(ori) < tvol ? fabs(ori) : tvol; + } + } else { + // A front is invisible by tp. Stop. + tvol = 0.0; + break; } } - } // j - if (!stopflag) { - if (minvol < minvol1) { - // The min. vol. is improved by this candidate. Choose it. - for (j = 0; j < 3; j++) searchpt[j] = candpt[j]; - minvol = minvol1; + if (tvol > mvol) { + // Get a larger minimal volume. + for (i = 0; i < 3; i++) np[i] = tp[i]; + mvol = tvol; } else { - // The min. vol. begins to decrease. Stop the search. - stopflag = true; + // Minimal volume decreases. Stop. + break; } - } - } // i + // Continue to adjust np. + j++; + } while (true); + // Has np been adjusted? + if (j > 0) break; + // Try to move np to anoter direction. + k++; + } while (k < 2); - if (minvol > 0) { - // We've found a valid relocation. Choose it. - if (b->verbose > 1) { - printf("p:show_vector(%g, %g, %g, %g, %g, %g) -- Relo\n", steinpt[0], - steinpt[1], steinpt[2], searchpt[0], searchpt[1], searchpt[2]); - } - for (i = 0; i < 3; i++) relocatept[i] = searchpt[i]; - return true; - } else { - return false; + if (b->verbose > 1) { + printf(" %d adjust iterations. minvol = %.12g.\n", j, mvol); } + return true; } /////////////////////////////////////////////////////////////////////////////// @@ -24665,7 +24973,7 @@ bool tetgenmesh::findrelocatepoint2(point steinpt, point relocatept, // // /////////////////////////////////////////////////////////////////////////////// -bool tetgenmesh::relocatepoint(point steinpt, triface* oldtet, list* frontlist, +void tetgenmesh::relocatepoint(point steinpt, triface* oldtet, list* frontlist, list* newtetlist, queue* flipque) { triface front, newtet, newface, neightet; @@ -24708,28 +25016,12 @@ bool tetgenmesh::relocatepoint(point steinpt, triface* oldtet, list* frontlist, setvolumebound(newtet.tet, volume); } } - // Update the point-to-tet map. - pa = org(front); - setpoint2tet(pa, encode(newtet)); - pa = dest(front); - setpoint2tet(pa, encode(newtet)); - pa = apex(front); - setpoint2tet(pa, encode(newtet)); - setpoint2tet(steinpt, encode(newtet)); // 'front' may be a 'fake' tet. tspivot(front, checksh); if (oppo(front) == (point) NULL) { if (checksh.sh != dummysh) { stdissolve(checksh); } - // Detach the fake tet from its old cavity tet. This is necessary - // in case the mesh of other cavities are failed, and we have to - // restore the original status. 2009-07-28. - sym(front, neightet); - if (neightet.tet != dummytet) { - assert(infected(neightet)); - dissolve(neightet); - } // Dealloc the 'fake' tet. tetrahedrondealloc(front.tet); // This side (newtet) is a boundary face, let 'dummytet' bond to it. @@ -24752,7 +25044,6 @@ bool tetgenmesh::relocatepoint(point steinpt, triface* oldtet, list* frontlist, } // Connect new tets in C. All connecting faces must contain 'steinpt'. - // The following code should be re-written. 2009-07-30. for (i = 0; i < newtetlist->len(); i++) { newtet = * (triface *)(* newtetlist)[i]; newtet.ver = 0; @@ -24782,30 +25073,25 @@ bool tetgenmesh::relocatepoint(point steinpt, triface* oldtet, list* frontlist, enextself(neightet); } } - if (!bdflag) { - assert(0); // break; // The relocation failed. - } + assert(bdflag); } enextself(newtet); } - if (j < 3) break; - } - - if (i < newtetlist->len()) { - // Relocation failed. Delete new tets. - for (i = 0; i < newtetlist->len(); i++) { - newtet = * (triface *)(* newtetlist)[i]; - tetrahedrondealloc(newtet.tet); - } - return false; + // Let the corners of newtet point to it for fast searching. + pa = org(newtet); + setpoint2tet(pa, encode(newtet)); + pa = dest(newtet); + setpoint2tet(pa, encode(newtet)); + pa = apex(newtet); + setpoint2tet(pa, encode(newtet)); + pa = oppo(newtet); + setpoint2tet(pa, encode(newtet)); } if (flipque != (queue *) NULL) { // Recover locally Delaunay faces. - lawson3d(flipque); + flip(flipque, NULL); } - - return true; } /////////////////////////////////////////////////////////////////////////////// @@ -24946,12 +25232,6 @@ void tetgenmesh::collapseedge(point suppt, point conpt, list* oldtetlist, setoppo(oldtet, conpt); if ((pa == conpt) || (pb == conpt) || (pc == conpt)) { deadtetlist->append(&oldtet); // a collpased tet. - } else { - // A non-collapse tet. Update point-to-tet map. - setpoint2tet(pa, encode(oldtet)); - setpoint2tet(pb, encode(oldtet)); - setpoint2tet(pc, encode(oldtet)); - setpoint2tet(conpt, encode(oldtet)); } } // Loop in deadtetlist, glue adjacent tets of dead tets. @@ -25061,8 +25341,7 @@ void tetgenmesh::restorepolyhedron(list* oldtetlist) { triface oldtet, neightet, neineitet; face checksh; - point *ppt; - int i, j; + int i; for (i = 0; i < oldtetlist->len(); i++) { // Get an old tet t_o. @@ -25072,7 +25351,6 @@ void tetgenmesh::restorepolyhedron(list* oldtetlist) sym(oldtet, neightet); tspivot(oldtet, checksh); if (neightet.tet != dummytet) { - assert(!isdead(&neightet)); // SELF_CHECK 2009-07-24 sym(neightet, neineitet); if (neineitet.tet != oldtet.tet) { // This face of t_o is a boundary of P. @@ -25083,16 +25361,16 @@ void tetgenmesh::restorepolyhedron(list* oldtetlist) } } else { // t_o has a hull face. It should be the boundary of P. +#ifdef SELF_CHECK + assert(checksh.sh != dummysh); + stpivot(checksh, neineitet); + assert(neineitet.tet != oldtet.tet); +#endif tsbond(oldtet, checksh); // Let dummytet[0] points to it. dummytet[0] = encode(oldtet); } } - // Update the point-to-tet map. - ppt = (point *) &(oldtet.tet[4]); - for (j = 0; j < 4; j++) { - setpoint2tet(ppt[j], encode(oldtet)); - } } } @@ -25117,16 +25395,12 @@ bool tetgenmesh::suppressfacetpoint(face* supsh, list* frontlist, { list *oldtetlist[2], *newtetlist[2]; list *oldshlist, *newshlist; - list *gluetetlist; - list *glueshlist; - triface oldtet, newtet, neightet; + triface oldtet, newtet; face oldsh, newsh; point suppt, newpt[2]; - point *cons, *ppt; - enum interresult dir; + point *cons; REAL norm[3]; bool success; - int bakchecksubfaces; int shmark; int i, j; @@ -25141,15 +25415,10 @@ bool tetgenmesh::suppressfacetpoint(face* supsh, list* frontlist, newtetlist[i] = (list *) NULL; newpt[i] = (point) NULL; } - gluetetlist = new list(sizeof(triface), NULL, 256); - glueshlist = new list(sizeof(face), NULL, 256); oldshlist = new list(sizeof(face), NULL, 256); newshlist = new list(sizeof(face), NULL, 256); success = true; // Assume p can be suppressed. - bakchecksubfaces = checksubfaces; - checksubfaces = 0; - // Find subs of C(p). oldshlist->append(supsh); formstarpolygon(suppt, oldshlist, ptlist); @@ -25173,8 +25442,6 @@ bool tetgenmesh::suppressfacetpoint(face* supsh, list* frontlist, flipque->clear(); viri->restart(); - checksubfaces = bakchecksubfaces; - // B(p) (tets with p as a vertex) has been separated into two parts // (B_0(p) and B_1(p)) by F. Process them individually. for (i = 0; i < 2 && success; i++) { @@ -25199,16 +25466,13 @@ bool tetgenmesh::suppressfacetpoint(face* supsh, list* frontlist, orientnewsubs(newshlist, supsh, norm); // Tetrahedralize old B_i(p). success = constrainedcavity(&oldtet, newshlist, oldtetlist[i], ptlist, - frontlist, misfrontlist, newtetlist[i], gluetetlist, glueshlist, - flipque); + frontlist, misfrontlist, newtetlist[i], flipque); // If p is not suppressed, do relocation if 'noreloc' is not set. if (!success && !noreloc) { // Try to relocate p into the old B_i(p). makepoint(&(newpt[i])); - // success = findrelocatepoint(suppt, newpt[i], norm, frontlist, - // oldtetlist[i]); - success = findrelocatepoint2(suppt, newpt[i], norm, frontlist, - oldtetlist[i]); + success = findrelocatepoint(suppt, newpt[i], norm, frontlist, + oldtetlist[i]); // Initialize newpt = suppt. // for (j = 0; j < 3; j++) newpt[i][j] = suppt[j]; // success = smoothvolpoint(newpt[i], frontlist, true); @@ -25234,8 +25498,6 @@ bool tetgenmesh::suppressfacetpoint(face* supsh, list* frontlist, ptlist->clear(); frontlist->clear(); misfrontlist->clear(); - // Do not clear gluetetlist. gluetetlist->clear(); - // Do not clear glueshlist. flipque->clear(); } @@ -25284,17 +25546,6 @@ bool tetgenmesh::suppressfacetpoint(face* supsh, list* frontlist, } } } - // Insert outside new subfaces (if there are). 2009-07-08. - for (i = 0; i < glueshlist->len(); i++) { - newsh = * (face *)(* glueshlist)[i]; - // Insert it into mesh (it may be already inserted). - newtet.tet = NULL; - // The mesh may be non-convex (set convexflag = 0). - dir = scoutsubface(&newsh, &newtet, 0); - if (dir != SHAREFACE) { - assert(0); - } - } } else { // p is not suppressed. Recover the original state. unsupverts++; @@ -25305,18 +25556,6 @@ bool tetgenmesh::suppressfacetpoint(face* supsh, list* frontlist, newsh = * (face *)(* newshlist)[i]; shellfacedealloc(subfaces, newsh.sh); } - // Delete new subfaces in glueshlist. 2009-07-07 - for (i = 0; i < glueshlist->len(); i++) { - newsh = * (face *)(* glueshlist)[i]; - for (j = 0; j < 2; j++) { - stpivot(newsh, oldtet); - if (oldtet.tet != dummytet) { - tsdissolve(oldtet); - } - sesymself(newsh); - } - shellfacedealloc(subfaces, newsh.sh); - } // Restore old B_i(p). for (i = 0; i < 2; i++) { if (oldtetlist[i] != (list *) NULL) { @@ -25327,7 +25566,7 @@ bool tetgenmesh::suppressfacetpoint(face* supsh, list* frontlist, uninfect(oldtet); } // Has it been re-meshed? - // if (newtetlist[i]->len() > 0) { + if (newtetlist[i]->len() > 0) { // Restore the old B_i(p). restorepolyhedron(oldtetlist[i]); // Delete tets of the new B_i(p); @@ -25338,7 +25577,7 @@ bool tetgenmesh::suppressfacetpoint(face* supsh, list* frontlist, tetrahedrondealloc(newtet.tet); } } - // } + } // Dealloc newpt[i] if it exists. if (newpt[i] != (point) NULL) { pointdealloc(newpt[i]); @@ -25346,28 +25585,6 @@ bool tetgenmesh::suppressfacetpoint(face* supsh, list* frontlist, } } } - // Detach new subfaces attached to glue tets. 2009-07-10. - for (i = 0; i < gluetetlist->len(); i++) { - oldtet = * (triface *)(* gluetetlist)[i]; - if (!isdead(&oldtet)) { - // It contains a new subface which has already been deleted (in above). - tspivot(oldtet, newsh); - assert(isdead(&newsh)); - tsdissolve(oldtet); - sym(oldtet, neightet); - if (neightet.tet != dummytet) { - tsdissolve(neightet); - } - } - } - // Update the point-to-subface map. 2009-07-22. - for (i = 0; i < oldshlist->len(); i++) { - oldsh = * (face *)(* oldshlist)[i]; - ppt = (point *) &(oldsh.sh[3]); - for (j = 0; j < 3; j++) { - setpoint2sh(ppt[j], sencode(oldsh)); - } - } } // Delete work lists. @@ -25379,8 +25596,6 @@ bool tetgenmesh::suppressfacetpoint(face* supsh, list* frontlist, delete newtetlist[i]; } } - delete gluetetlist; - delete glueshlist; return success; } @@ -25404,18 +25619,14 @@ bool tetgenmesh::suppresssegpoint(face* supseg, list* spinshlist, list **oldtetlist, **newtetlist; list **oldshlist, **newshlist; list *pnewshlist, *dnewshlist; - list *gluetetlist; - list *glueshlist; - triface oldtet, newtet, neightet, spintet; - face oldsh, newsh, *worksharray; + triface oldtet, newtet; + face oldsh, newsh; face startsh, spinsh, segsh1, segsh2; face nsupseg, newseg, prevseg, nextseg; point suppt, *newpt; - point pa, pb, pc, pd, *cons, *ppt; - enum interresult dir; - REAL pnorm[2][3], norm[3], len; + point pa, pb, *cons; + REAL pnorm[2][3], norm[3]; bool success; - int bakchecksubfaces; int shmark; int n, i, j, k; @@ -25440,71 +25651,11 @@ bool tetgenmesh::suppresssegpoint(face* supseg, list* spinshlist, spivot(*supseg, startsh); spinsh = startsh; do { - // Adjust spinsh to be the edge [pa, suppt]. - findedge(&spinsh, pa, suppt); // Save it in list. spinshlist->append(&spinsh); // Go to the next facet. spivotself(spinsh); - if (spinsh.sh == dummysh) break; } while (spinsh.sh != startsh.sh); - - n = spinshlist->len(); - - if (n > 2) { - // Order the subfaces to be counterclockwise around edge [pa, suppt]. - worksharray = new face[n]; // Temporarily use it. - for (i = 0; i < n; i++) { - worksharray[i] = * (face *)(* spinshlist)[i]; - sinfect(worksharray[i]); - } - spinshlist->clear(); // Clear the list for the re-ordering. - for (i = 0; i < n; i++) { - worksharray[i] = * (face *)(* spinshlist)[i]; - if (sinfected(worksharray[i])) { - // Collect subfaces at this segment. - startsh = worksharray[i]; - stpivot(startsh, neightet); - if (neightet.tet == dummytet) { - sesymself(startsh); - stpivot(startsh, neightet); - assert(neightet.tet != dummytet); - } - // Adjust neightet to be the edge [pa, suppt]. - findedge(&neightet, pa, suppt); - // Adjust neightet to be the boundary face (if there exists). - spintet = neightet; - while (1) { - if (!fnextself(spintet)) { - esymself(spintet); - break; - } - if (apex(spintet) == apex(neightet)) break; - } - // Start from spintet, collect all subfaces at this segment. - neightet = spintet; - pc = org(spintet); - pd = dest(spintet); - // [pc, pd] is the rotating edge (axis). It may be either - // [pa, suppt] or [suppt, pa]. - while (1) { - tspivot(spintet, spinsh); - if (spinsh.sh != dummysh) { - assert(sinfected(spinsh)); - suninfect(spinsh); - // Let spinsh be the same oriented edge as spintet. - findedge(&spinsh, pc, pd); - spinshlist->append(&spinsh); - } - if (!fnextself(spintet)) break; - if (apex(spintet) == apex(neightet)) break; - } - } - assert(!sinfected(worksharray[i])); - } // i - delete [] worksharray; - } - if (spinshlist->len() == 1) { // This case has not handled yet. // printf("Unhandled case: segment only belongs to one facet.\n"); @@ -25515,7 +25666,7 @@ bool tetgenmesh::suppresssegpoint(face* supseg, list* spinshlist, // Suppose ab is shared by n facets (n > 1), then there are n B(p) (tets // with p as a vertex). Some B(p) may be empty, eg, outside. - // n = spinshlist->len(); + n = spinshlist->len(); oldtetlist = new list*[n]; newtetlist = new list*[n]; oldshlist = new list*[n]; @@ -25528,8 +25679,6 @@ bool tetgenmesh::suppresssegpoint(face* supseg, list* spinshlist, newshlist[i] = (list *) NULL; newpt[i] = (point) NULL; } - gluetetlist = new list(sizeof(triface), NULL, 256); - glueshlist = new list(sizeof(face), NULL, 256); // Create a new segment ab (result in newseg). makeshellface(subsegs, &newseg); @@ -25567,9 +25716,6 @@ bool tetgenmesh::suppresssegpoint(face* supseg, list* spinshlist, newseg.shver = 0; } - bakchecksubfaces = checksubfaces; - checksubfaces = 0; - // Re-triangulate C(p) (subs with p as a vertex) to remove p. for (i = 0; i < spinshlist->len(); i++) { spinsh = * (face *)(* spinshlist)[i]; @@ -25629,8 +25775,6 @@ bool tetgenmesh::suppresssegpoint(face* supseg, list* spinshlist, sbond1(segsh1, segsh2); } - checksubfaces = bakchecksubfaces; - // A work list for keeping subfaces from two facets. dnewshlist = new list(sizeof(face), NULL, 256); success = true; // Assume p is suppressable. @@ -25639,10 +25783,9 @@ bool tetgenmesh::suppresssegpoint(face* supseg, list* spinshlist, for (i = 0; i < spinshlist->len() && success; i++) { // Get an old subface s (ap) of a facet. spinsh = * (face *)(* spinshlist)[i]; - // // Let the edge direction of s be a->b. Hence all subfaces follow - // // the right-hand rule of ab. - // if (sorg(spinsh) != pa) sesymself(spinsh); - // spinsh has been directed. Do not change its orientation now. + // Let the edge direction of s be a->b. Hence all subfaces follow + // the right-hand rule of ab. + if (sorg(spinsh) != pa) sesymself(spinsh); // Get a tet t of B_i(p). stpivot(spinsh, oldtet); // Is B_i(p) empty? @@ -25667,21 +25810,14 @@ bool tetgenmesh::suppresssegpoint(face* supseg, list* spinshlist, pnewshlist = newshlist[0]; segsh1 = * (face *)(* spinshlist)[0]; } - /*// Adjust the orientation of segsh1 to face to the inside of C. + // Adjust the orientation of segsh1 to face to the inside of C. if (k == 0) { if (sorg(segsh1) != pa) sesymself(segsh1); assert(sorg(segsh1) == pa); } else { if (sdest(segsh1) != pa) sesymself(segsh1); assert(sdest(segsh1) == pa); - }*/ - if (k == 0) { - // segsh1 has already been directed pointing to the inside of C. - } else { - // Reverse the direction of segsh1. - sesymself(segsh1); } - // its orientation now. // Preparation for re-tetrahedralzing old B_i(p). orientnewsubs(pnewshlist, &segsh1, pnorm[k]); for (j = 0; j < pnewshlist->len(); j++) { @@ -25690,37 +25826,21 @@ bool tetgenmesh::suppresssegpoint(face* supseg, list* spinshlist, } // Tetrahedralize B_i(p). success = constrainedcavity(&oldtet, dnewshlist, oldtetlist[i], ptlist, - frontlist, misfrontlist, newtetlist[i], gluetetlist, glueshlist, - flipque); + frontlist, misfrontlist, newtetlist[i], flipque); if (!success && !noreloc) { // C must be finished by re-locating the steiner point. makepoint(&(newpt[i])); for (j = 0; j < 3; j++) norm[j] = 0.5 * (pnorm[0][j] + pnorm[1][j]); - // Normialize the normal. - len = sqrt(norm[0] * norm[0] + norm[1] * norm[1] + norm[2] * norm[2]); - assert(len != 0); - for (j = 0; j < 3; j++) norm[j] /= len; - // success = findrelocatepoint(suppt, newpt[i], norm, frontlist, - // oldtetlist[i]); - success = findrelocatepoint2(suppt, newpt[i], norm, frontlist, - oldtetlist[i]); - // success = findrelocatepoint3(suppt, pa, pb, newpt[i], norm, frontlist, - // oldtetlist[i]); + success = findrelocatepoint(suppt, newpt[i], norm, frontlist, + oldtetlist[i]); // for (j = 0; j < 3; j++) newpt[i][j] = suppt[j]; // success = smoothvolpoint(newpt[i], frontlist, true); if (success) { // p is relocated by newpt[i]. Now insert it. Don't do flip since // the new tets may get deleted again. - if (relocatepoint(newpt[i], &oldtet, frontlist, newtetlist[i], NULL)) { - setpointtype(newpt[i], FREEVOLVERTEX); - relverts++; - } else { - // The faked tets are deleted in above route. - pointdealloc(newpt[i]); - newpt[i] = (point) NULL; - newtetlist[i]->clear(); - success = false; - } + relocatepoint(newpt[i], &oldtet, frontlist, newtetlist[i], NULL); + setpointtype(newpt[i], FREEVOLVERTEX); + relverts++; } else { // Fail to relocate p. Clean fake tets and quit this option. deallocfaketets(frontlist); @@ -25738,8 +25858,6 @@ bool tetgenmesh::suppresssegpoint(face* supseg, list* spinshlist, ptlist->clear(); frontlist->clear(); misfrontlist->clear(); - // Do not clear gluetetlist. // gluetetlist->clear(); - // Do not clear glueshlist. flipque->clear(); } @@ -25747,9 +25865,9 @@ bool tetgenmesh::suppresssegpoint(face* supseg, list* spinshlist, // p has been suppressed. (Still in the pool). setpointtype(suppt, UNUSEDVERTEX); unuverts++; - // Update the point-to-seg map. - setpoint2seg(pa, sencode(newseg)); - setpoint2seg(pb, sencode(newseg)); + // Update the segmnet pointers saved in a and b. + setpoint2sh(pa, sencode(newseg)); + setpoint2sh(pb, sencode(newseg)); // Delete old segments ap, pb. shellfacedealloc(subsegs, supseg->sh); shellfacedealloc(subsegs, nsupseg.sh); @@ -25796,17 +25914,6 @@ bool tetgenmesh::suppresssegpoint(face* supseg, list* spinshlist, } } } - // Insert outside new subfaces (if there are). 2009-07-08. - for (i = 0; i < glueshlist->len(); i++) { - newsh = * (face *)(* glueshlist)[i]; - // Insert it into mesh (it may be already inserted). - newtet.tet = NULL; - // The mesh may be non-convex (set convexflag = 0). - dir = scoutsubface(&newsh, &newtet, 0); - if (dir != SHAREFACE) { - assert(0); - } - } } else { // p is not suppressed. Recover the original state. unsupverts++; @@ -25848,21 +25955,6 @@ bool tetgenmesh::suppresssegpoint(face* supseg, list* spinshlist, shellfacedealloc(subfaces, newsh.sh); } } - // Delete new subfaces in glueshlist. 2009-07-07. - for (i = 0; i < glueshlist->len(); i++) { - newsh = * (face *)(* glueshlist)[i]; - if (!isdead(&newsh)) { - // Disconnect adjacent tets. - for (j = 0; j < 2; j++) { - stpivot(newsh, oldtet); - if (oldtet.tet != dummytet) { - tsdissolve(oldtet); - } - sesymself(newsh); - } - shellfacedealloc(subfaces, newsh.sh); - } - } // Restore old B_i(p). for (i = 0; i < spinshlist->len(); i++) { if (oldtetlist[i] != (list *) NULL) { @@ -25873,7 +25965,7 @@ bool tetgenmesh::suppresssegpoint(face* supseg, list* spinshlist, uninfect(oldtet); } // Has it been re-meshed? - // if (newtetlist[i]->len() > 0) { + if (newtetlist[i]->len() > 0) { // Restore the old B_i(p). restorepolyhedron(oldtetlist[i]); // Delete tets of the new B_i(p); @@ -25884,7 +25976,7 @@ bool tetgenmesh::suppresssegpoint(face* supseg, list* spinshlist, tetrahedrondealloc(newtet.tet); } } - // } + } // Dealloc newpt[i] if it exists. if (newpt[i] != (point) NULL) { pointdealloc(newpt[i]); @@ -25892,34 +25984,9 @@ bool tetgenmesh::suppresssegpoint(face* supseg, list* spinshlist, } } } - // Detach new subfaces attached to glue tets. 2009-07-10. - for (i = 0; i < gluetetlist->len(); i++) { - oldtet = * (triface *)(* gluetetlist)[i]; - if (!isdead(&oldtet)) { - // It contains a new subface which has already been deleted (in above). - tspivot(oldtet, newsh); - assert(isdead(&newsh)); - tsdissolve(oldtet); - sym(oldtet, neightet); - if (neightet.tet != dummytet) { - tsdissolve(neightet); - } - } - } - // Update the point-to-subface map. 2009-07-22. - for (i = 0; i < spinshlist->len(); i++) { - for (j = 0; j < oldshlist[i]->len(); j++) { - oldsh = * (face *)(* oldshlist[i])[j]; - ppt = (point *) &(oldsh.sh[3]); - for (k = 0; k < 3; k++) { - setpoint2sh(ppt[k], sencode(oldsh)); - } - } - } } // Delete work lists. - delete [] newpt; // BUG fixed. Thanks dmyan, June 23, 2007. delete dnewshlist; for (i = 0; i < spinshlist->len(); i++) { delete oldshlist[i]; @@ -25935,8 +26002,6 @@ bool tetgenmesh::suppresssegpoint(face* supseg, list* spinshlist, } delete [] oldtetlist; delete [] newtetlist; - delete gluetetlist; - delete glueshlist; // Clear work lists. newsegshlist->clear(); spinshlist->clear(); @@ -25961,20 +26026,16 @@ bool tetgenmesh::suppressvolpoint(triface* suptet, list* frontlist, { list *myfrontlist, *mymisfrontlist, *myptlist; list *oldtetlist, *newtetlist; - list *gluetetlist; - list *glueshlist; list *newshlist; // a dummy list. queue *myflipque; triface oldtet, newtet; - point suppt, conpt, *ppt; + point suppt, conpt; bool success; - int j, k; + int j; // Allocate spaces for storing (old and new) B(p). oldtetlist = new list(sizeof(triface), NULL, 256); newtetlist = new list(sizeof(triface), NULL, 256); - gluetetlist = new list(sizeof(triface), NULL, 256); - glueshlist = new list(sizeof(face), NULL, 256); newshlist = new list(sizeof(face), NULL, 256); // Allocate work lists if user doesn't supply them. myfrontlist = mymisfrontlist = myptlist = (list *) NULL; @@ -25998,120 +26059,290 @@ bool tetgenmesh::suppressvolpoint(triface* suptet, list* frontlist, printf(" Remove point %d in mesh.\n", pointmark(suppt)); } - // Form old B(p) in oldtetlist. - oldtetlist->append(&oldtet); - formstarpolyhedron(suppt, oldtetlist, ptlist, false); - // Infect the tets in old B(p) (they're going to be delete). - for (j = 0; j < oldtetlist->len(); j++) { - oldtet = * (triface *)(* oldtetlist)[j]; - infect(oldtet); - } - // Tetrahedralize old B(p). - success = constrainedcavity(&oldtet, newshlist, oldtetlist, ptlist, - frontlist, misfrontlist, newtetlist, gluetetlist, glueshlist, flipque); - if (!success) { - // Unable to suppress p. - deallocfaketets(frontlist); - // Try to collapse an edge at p. - conpt = (point) NULL; - assert(newtetlist->len() == 0); - if (findcollapseedge(suppt, &conpt, oldtetlist, ptlist)) { - // Collapse the edge suppt->conpt. Re-use newtetlist. - collapseedge(suppt, conpt, oldtetlist, newtetlist); - // The oldtetlist contains newtetlist. - if (optflag) { - assert(newtetlist->len() == 0); - for (j = 0; j < oldtetlist->len(); j++) { - newtet = * (triface *)(* oldtetlist)[j]; - newtetlist->append(&newtet); + // Form old B(p) in oldtetlist. + oldtetlist->append(&oldtet); + formstarpolyhedron(suppt, oldtetlist, ptlist, false); + // Infect the tets in old B(p) (they're going to be delete). + for (j = 0; j < oldtetlist->len(); j++) { + oldtet = * (triface *)(* oldtetlist)[j]; + infect(oldtet); + } + // Tetrahedralize old B(p). + success = constrainedcavity(&oldtet, newshlist, oldtetlist, ptlist, + frontlist, misfrontlist, newtetlist, flipque); + if (!success) { + // Unable to suppress p. + deallocfaketets(frontlist); + // Try to collapse an edge at p. + conpt = (point) NULL; + assert(newtetlist->len() == 0); + if (findcollapseedge(suppt, &conpt, oldtetlist, ptlist)) { + // Collapse the edge suppt->conpt. Re-use newtetlist. + collapseedge(suppt, conpt, oldtetlist, newtetlist); + // The oldtetlist contains newtetlist. + if (optflag) { + assert(newtetlist->len() == 0); + for (j = 0; j < oldtetlist->len(); j++) { + newtet = * (triface *)(* oldtetlist)[j]; + newtetlist->append(&newtet); + } + } + oldtetlist->clear(); // Do not delete them. + collapverts++; + success = true; + } + } + if (success) { + // p has been removed! (Still in the pool). + setpointtype(suppt, UNUSEDVERTEX); + unuverts++; + suprelverts++; + // Delete old B(p). + for (j = 0; j < oldtetlist->len(); j++) { + oldtet = * (triface *)(* oldtetlist)[j]; + assert(!isdead(&oldtet)); + tetrahedrondealloc(oldtet.tet); + } + if (optflag) { + // Check for new bad tets. + for (j = 0; j < newtetlist->len(); j++) { + newtet = * (triface *)(* newtetlist)[j]; + if (!isdead(&newtet)) checktet4opt(&newtet, true); + } + } + } else { + // p is not suppressed. Recover the original state. + // Uninfect tets of old B(p). + for (j = 0; j < oldtetlist->len(); j++) { + oldtet = * (triface *)(* oldtetlist)[j]; + assert(infected(oldtet)); + uninfect(oldtet); + } + } + + // Clear work lists. + ptlist->clear(); + frontlist->clear(); + misfrontlist->clear(); + flipque->clear(); + // Deallocate work lists. + if (myfrontlist != (list *) NULL) { + delete myfrontlist; + delete mymisfrontlist; + delete myptlist; + delete myflipque; + } + delete oldtetlist; + delete newtetlist; + delete newshlist; + + return success; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// smoothpoint() Smooth a volume/segment point. // +// // +// 'smthpt' (p) is inside the polyhedron (C) bounded by faces in 'starlist'. // +// This routine moves p inside C until an object function is maximized. // +// // +// Default, the CCW edge ring of the faces on C points to p. If 'invtori' is // +// TRUE, the orientation is inversed. // +// // +// If 'key' != NULL, it contains an object value to be improved. Current it // +// means the cosine of the largest dihedral angle. In such case, the point // +// is smoothed only if the final configuration improves the object value, it // +// is returned by the 'key'. // +// // +/////////////////////////////////////////////////////////////////////////////// + +bool tetgenmesh::smoothpoint(point smthpt, point e1, point e2, list *starlist, + bool invtori, REAL *key) +{ + triface starttet; + point pa, pb, pc; + REAL fcent[3], startpt[3], nextpt[3], bestpt[3]; + REAL iniTmax, oldTmax, newTmax; + REAL ori, aspT, aspTmax, imprate; + REAL cosd, maxcosd; + bool segflag, randflag; //, subflag; + int numdirs; + int iter, i, j; + + // Is p a segment vertex? + segflag = (e1 != (point) NULL); + // Decide the number of moving directions. + numdirs = segflag ? 2 : starlist->len(); + randflag = numdirs > 10; + if (randflag) { + numdirs = 10; // Maximum 10 directions. + } + + // Calculate the initial object value (the largest aspect ratio). + for (i = 0; i < starlist->len(); i++) { + starttet = * (triface *)(* starlist)[i]; + adjustedgering(starttet, !invtori ? CCW : CW); + pa = org(starttet); + pb = dest(starttet); + pc = apex(starttet); + aspT = tetaspectratio(pa, pb, pc, smthpt); + if (i == 0) { + aspTmax = aspT; + } else { + aspTmax = aspT > aspTmax ? aspT : aspTmax; + } + } + iniTmax = aspTmax; + + if (b->verbose > 1) { + printf(" Smooth %s point %d (%g, %g, %g).\n", segflag ? "seg" : "vol", + pointmark(smthpt), smthpt[0], smthpt[1], smthpt[2]); + printf(" Initial max L/h = %g.\n", iniTmax); + } + for (i = 0; i < 3; i++) { + bestpt[i] = startpt[i] = smthpt[i]; + } + + // Do iteration until the new aspTmax does not decrease. + newTmax = iniTmax; + iter = 0; + while (true) { + // Find the best next location. + oldTmax = newTmax; + for (i = 0; i < numdirs; i++) { + // Calculate the moved point (saved in 'nextpt'). + if (!segflag) { + if (randflag) { + // Randomly pick a direction. + j = (int) randomnation(starlist->len()); + } else { + j = i; + } + starttet = * (triface *)(* starlist)[j]; + adjustedgering(starttet, !invtori ? CCW : CW); + pa = org(starttet); + pb = dest(starttet); + pc = apex(starttet); + for (j = 0; j < 3; j++) { + fcent[j] = (pa[j] + pb[j] + pc[j]) / 3.0; + } + } else { + for (j = 0; j < 3; j++) { + fcent[j] = (i == 0 ? e1[j] : e2[j]); + } + } + for (j = 0; j < 3; j++) { + nextpt[j] = startpt[j] + 0.01 * (fcent[j] - startpt[j]); + } + // Get the largest object value for the new location. + for (j = 0; j < starlist->len(); j++) { + starttet = * (triface *)(* starlist)[j]; + adjustedgering(starttet, !invtori ? CCW : CW); + pa = org(starttet); + pb = dest(starttet); + pc = apex(starttet); + ori = orient3d(pa, pb, pc, nextpt); + if (ori < 0.0) { + aspT = tetaspectratio(pa, pb, pc, nextpt); + if (j == 0) { + aspTmax = aspT; + } else { + aspTmax = aspT > aspTmax ? aspT : aspTmax; + } + } else { + // An invalid new tet. Discard this point. + aspTmax = newTmax; + } // if (ori < 0.0) + // Stop looping when the object value is bigger than before. + if (aspTmax >= newTmax) break; + } // for (j = 0; j < starlist->len(); j++) + if (aspTmax < newTmax) { + // Save the improved object value and the location. + newTmax = aspTmax; + for (j = 0; j < 3; j++) bestpt[j] = nextpt[j]; + } + } // for (i = 0; i < starlist->len(); i++) + // Does the object value improved much? + imprate = fabs(oldTmax - newTmax) / oldTmax; + if (imprate < 1e-3) break; + // Yes, move p to the new location and continue. + for (j = 0; j < 3; j++) startpt[j] = bestpt[j]; + iter++; + } // while (true) + + if (iter > 0) { + // The point is moved. + if (key) { + // Check if the quality is improved by the smoothed point. + maxcosd = 0.0; // = cos(90). + for (j = 0; j < starlist->len(); j++) { + starttet = * (triface *)(* starlist)[j]; + adjustedgering(starttet, !invtori ? CCW : CW); + pa = org(starttet); + pb = dest(starttet); + pc = apex(starttet); + tetalldihedral(pa, pb, pc, startpt, NULL, &cosd, NULL); + if (cosd < *key) { + // This quality will not be improved. Stop. + iter = 0; break; + } else { + // Remeber the worst quality value (of the new configuration). + maxcosd = maxcosd < cosd ? maxcosd : cosd; } } - oldtetlist->clear(); // Do not delete them. - collapverts++; - success = true; + if (iter > 0) *key = maxcosd; } } - if (success) { - // p has been removed! (Still in the pool). - setpointtype(suppt, UNUSEDVERTEX); - unuverts++; - suprelverts++; - // Delete old B(p). - for (j = 0; j < oldtetlist->len(); j++) { - oldtet = * (triface *)(* oldtetlist)[j]; - assert(!isdead(&oldtet)); - tetrahedrondealloc(oldtet.tet); - } - if (optflag) { - // Check for new bad tets. - for (j = 0; j < newtetlist->len(); j++) { - newtet = * (triface *)(* newtetlist)[j]; - if (!isdead(&newtet)) checktet4opt(&newtet, true); + + if (iter > 0) { + segflag ? smoothsegverts++ : smoothvolverts++; + for (i = 0; i < 3; i++) smthpt[i] = startpt[i]; + if (b->verbose > 1) { + printf(" Move to new location (%g, %g, %g).\n", smthpt[0], smthpt[1], + smthpt[2]); + printf(" Final max L/h = %g. (%d iterations)\n", newTmax, iter); + if (key) { + printf(" Max. dihed = %g (degree).\n", acos(*key) / PI * 180.0); } } + return true; } else { - // p is not suppressed. Recover the original state. - // Uninfect tets of old B(p). - for (j = 0; j < oldtetlist->len(); j++) { - oldtet = * (triface *)(* oldtetlist)[j]; - assert(infected(oldtet)); - uninfect(oldtet); - // Update the point-to-tet map. - ppt = (point *) &(oldtet.tet[4]); - for (k = 0; k < 4; k++) { - setpoint2tet(ppt[k], encode(oldtet)); - } + if (b->verbose > 1) { + printf(" Not smoothed.\n"); } + return false; } - - // Clear work lists. - ptlist->clear(); - frontlist->clear(); - misfrontlist->clear(); - gluetetlist->clear(); - glueshlist->clear(); - flipque->clear(); - // Deallocate work lists. - if (myfrontlist != (list *) NULL) { - delete myfrontlist; - delete mymisfrontlist; - delete myptlist; - delete myflipque; - } - delete oldtetlist; - delete newtetlist; - delete gluetetlist; - delete glueshlist; - delete newshlist; - - return success; } /////////////////////////////////////////////////////////////////////////////// // // -// removesteiners2() Remove Steiner points. // +// removesteiners() Delete or relocate Steiner points on facets. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::removesteiners2() +void tetgenmesh::removesteiners(bool coarseflag) { list *frontlist, *misfrontlist; list *spinshlist, *newsegshlist; list *ptlist, *conlist; memorypool *viri; queue *flipque; - triface searchtet, checktet; - face searchsh; - face searchseg; - point pa, pt; - enum verttype vtype; - bool remflag, success; //, optflag; + triface checktet; + face shloop; + face segloop, nextseg; + point pa, neipt; + REAL len; + bool remflag; + int *worklist; int oldnum, rmstein; - int unsupbdrycount; // Count the unsuppressed boundary Steiner points. - int iter, i, j; + int i, j; if (!b->quiet) { - printf("Removing Steiner points.\n"); + if (!coarseflag) { + printf("Removing Steiner points.\n"); + } else { + printf("Coarsening mesh.\n"); + } } // Initialize work lists. @@ -26123,301 +26354,368 @@ void tetgenmesh::removesteiners2() conlist = new list(sizeof(point *) * 2, NULL); flipque = new queue(sizeof(badface)); viri = new memorypool(sizeof(shellface *), 1024, POINTER, 0); - - caveshlist = new arraypool(sizeof(face), 10); - caveshbdlist = new arraypool(sizeof(face), 10); - oldnum = unuverts; relverts = suprelverts = collapverts = unsupverts; + smoothvolverts = 0; + expcavcount = 0; - iter = 0; - i = 0; - - do { // iter - unsupbdrycount = 0; - // Initialize the two arrays (global values). - fixededgelist = new arraypool(sizeof(point) * 2, 8); - elemfliplist = new arraypool(sizeof(elemflip), 8); - - do { // i - rmstein = unuverts; - points->traversalinit(); - pa = pointtraverse(); - while (pa != NULL) { - j = pointmark(pa) - in->firstnumber; - if (j >= in->numberofpoints) { - // pa is not an input points. - vtype = pointtype(pa); - if ((vtype == FREESEGVERTEX) || (vtype == FREESUBVERTEX) || - (vtype == FREEVOLVERTEX)) { - i++; - if (b->verbose > 1) { - printf(" Removing %d-th Steiner point %d.\n", i, pointmark(pa)); - } - } - if (vtype == FREESEGVERTEX) { - remflag = false; - // pa is not an input point. - if (b->nobisect == 1) { - point2segorg(pa, searchseg); - sstpivot(&searchseg, &checktet); - assert(checktet.tet != dummytet); - pt = apex(checktet); - do { - if (!fnextself(checktet)) { - // Meet a boundary face - p is on the hull. - remflag = true; - break; + // Suppress Steiner points inside facets. + do { + rmstein = unuverts; + subfaces->traversalinit(); + shloop.sh = shellfacetraverse(subfaces); + while (shloop.sh != (shellface *) NULL) { + remflag = false; + // Is s contains a Steiner point? + shloop.shver = 0; + for (i = 0; i < 3; i++) { + pa = sapex(shloop); + if (pointtype(pa) == FREESUBVERTEX) { + if (!coarseflag) { + // Remove it if it is not an input point. + j = pointmark(pa) - in->firstnumber; + if (j >= in->numberofpoints) { + if (b->nobisect == 1) { + // '-Y'. Remove p if s is a hull face. + stpivot(shloop, checktet); + if (checktet.tet != dummytet) { + sesymself(shloop); + stpivot(shloop, checktet); } - } while (apex(checktet) != pt); - } else { - // '-YY'. Remove p whatever s is a hull face or not. - remflag = true; - } - if (remflag) { - point2segorg(pa, searchseg); - sesymself(searchseg); // pa = sdest(); - success = suppresssegpoint(&searchseg, spinshlist, newsegshlist, - frontlist, misfrontlist, ptlist, conlist, viri, flipque, - false, false); - } - } else if (vtype == FREESUBVERTEX) { - remflag = false; - // pa is not an input point. - if (b->nobisect == 1) { - // '-Y'. Remove p if s is a hull face. - point2shorg(pa, searchsh); - stpivot(searchsh, checktet); - if (checktet.tet != dummytet) { - sesymself(searchsh); - stpivot(searchsh, checktet); + remflag = (checktet.tet == dummytet); + } else { + // '-YY'. Remove p whatever s is a hull face or not. + remflag = true; } - remflag = (checktet.tet == dummytet); - } else { - // '-YY'. Remove p whatever s is a hull face or not. - remflag = true; - } - if (remflag) { - point2shorg(pa, searchsh); - senextself(searchsh); // pa = sapex(); - success = suppressfacetpoint(&searchsh, frontlist, misfrontlist, - ptlist, conlist, viri, flipque, false, false); } - } else if (vtype == FREEVOLVERTEX) { - // pa is not an input point. - point2tetorg(pa, searchtet); - success = suppressvolpoint(&searchtet, frontlist, misfrontlist, - ptlist, flipque, false); - } - } // if (j >= in->numberofpoints) - pa = pointtraverse(); - } - // Continue if any Steiner point has been removed. - } while (unuverts > rmstein); - - delete fixededgelist; - delete elemfliplist; - fixededgelist = NULL; - elemfliplist = NULL; - - if (b->optlevel > 0) { // b->optlevel is set by -s. - // Improve the local mesh quality at relocated Steiner points. - b_steinerflag = true; - optimizemesh2(true); - b_steinerflag = false; - - // Smooth the relocated vertices (also count unsupressed vertices). - points->traversalinit(); - pa = pointtraverse(); - while (pa != NULL) { - j = pointmark(pa) - in->firstnumber; - if (j >= in->numberofpoints) { - // pa is not an input point. - vtype = pointtype(pa); - if (vtype == FREEVOLVERTEX) { - point2tetorg(pa, searchtet); - frontlist->append(&searchtet); - formstarpolyhedron(pa, frontlist, NULL, false); - smoothpoint(pa, NULL, NULL, frontlist, false, NULL); - frontlist->clear(); - } else if (vtype == FREESEGVERTEX) { - remflag = false; - // pa is not an input point. - if (b->nobisect == 1) { - point2segorg(pa, searchseg); - sstpivot(&searchseg, &checktet); - assert(checktet.tet != dummytet); - pt = apex(checktet); - do { - if (!fnextself(checktet)) { - // Meet a boundary face - p is on the hull. - remflag = true; - break; + } else { + // Check if this vertex can be coarsed. + if (b->nobisect == 0) { + // Is a background mesh available? + if (b->metric) { + // assert(pa[pointmtrindex] > 0.0); + // Form the star of pa. + spinshlist->append(&shloop); + formstarpolygon(pa, spinshlist, ptlist); + len = 0.0; + for (j = 0; j < ptlist->len(); j++) { + neipt = * (point *)(* ptlist)[j]; + len += distance(pa, neipt); } - } while (apex(checktet) != pt); - } else { - // '-YY'. Remove p whatever s is a hull face or not. - remflag = true; - } - if (remflag) { - unsupbdrycount++; + len /= ptlist->len(); + // Carse it if the average edge length is small. + remflag = len < pa[pointmtrindex]; + spinshlist->clear(); + ptlist->clear(); + } else { + // Coarse it if (1) it is an input point and its pointmarker + // is zero, or (2) it is a Steiner point. + remflag = true; + j = pointmark(pa) - in->firstnumber; + if (j < in->numberofpoints) { + remflag = (in->pointmarkerlist[j] == 0); + } + } // if (b->metric) + } // if (b->nobisect == 0) + } // if (!coarseflag) + if (remflag) break; + } // if (pointtype(pa) == FREESUBVERTEX) + senextself(shloop); + } // for (i = 0; i < 3; i++) + if (remflag) { + suppressfacetpoint(&shloop, frontlist, misfrontlist, ptlist, conlist, + viri, flipque, coarseflag, false); + } + shloop.sh = shellfacetraverse(subfaces); + } + // Continue if any Steiner point has been removed. + } while (unuverts > rmstein); + + if (coarseflag) { + shellface **segsperverlist; + int *idx2seglist; + face seg1, seg2; + point e1, e2; + // Connecting collinear segments. Hence the segment vertices may be + // removed. In fact, this should be done by reconstructmesh(). + makesegmentmap(idx2seglist, segsperverlist); + subsegs->traversalinit(); + segloop.sh = shellfacetraverse(subsegs); + while (segloop.sh != (shellface *) NULL) { + for (i = 0; i < 2; i++) { + segloop.shver = i; + senext(segloop, nextseg); + spivotself(nextseg); + if ((nextseg.sh == dummysh) || (nextseg.sh > segloop.sh)) { + // No neighbor segment connection or haven't been processed yet. + pa = sdest(segloop); + j = pointmark(pa) - in->firstnumber; + if (idx2seglist[j + 1] - idx2seglist[j] == 2) { + // pa is shared by only two segments. Get the other one. + nextseg.sh = segsperverlist[idx2seglist[j]]; + if (nextseg.sh == segloop.sh) { + nextseg.sh = segsperverlist[idx2seglist[j] + 1]; } - } else if (vtype == FREESUBVERTEX) { - remflag = false; - // pa is not an input point. - if (b->nobisect == 1) { - // '-Y'. Remove p if s is a hull face. - point2shorg(pa, searchsh); - stpivot(searchsh, checktet); - if (checktet.tet != dummytet) { - sesymself(searchsh); - stpivot(searchsh, checktet); + nextseg.shver = 0; + if (sorg(nextseg) != pa) sesymself(nextseg); + // Check if the two segments are collinear. + e1 = sorg(segloop); + e2 = sdest(nextseg); + if (iscollinear(e1, pa, e2, b->epsilon)) { + // Connect the two segments together. + if (b->verbose > 1) { + printf(" Glue two insegs (%d, %d) at %d.\n", pointmark(e1), + pointmark(e2), pointmark(pa)); } - remflag = (checktet.tet == dummytet); - } else { - // '-YY'. Remove p whatever s is a hull face or not. - remflag = true; - } - if (remflag) { - unsupbdrycount++; + senext(segloop, seg1); + senext2(nextseg, seg2); + sbond(seg1, seg2); } } - } - pa = pointtraverse(); - } - } - - if (unsupbdrycount == 0) { - break; // No unsupressed boundary points left. + } // if (nextseg.sh == dummysh) + } // for (i = 0; + segloop.sh = shellfacetraverse(subsegs); } - iter++; - } while ((b->optlevel > 0) && (iter < b->optpasses)); - // Comment: default b->optpasses is 3, it can be set by -ss option. - - if (b->verbose > 0) { - printf(" %d points removed from boundary.\n", unuverts - oldnum); - // if (relverts > 0) { - printf(" %d points relocated (%d suppressed, %d collapsed).\n", - relverts, suprelverts - collapverts, collapverts); - if (unsupverts > 0) { - printf(" %d points were unsuppressed.\n", unsupverts); - } - if (unsupbdrycount > 0) { - printf(" !! %d points remain in the boundary.\n", unsupbdrycount); - } - printf(" %d points remain in the interior.\n", relverts-suprelverts); - // } + delete [] segsperverlist; + delete [] idx2seglist; } - /*// DEBUG Dump extremly bad tets. - badtetrahedrons = new memorypool(sizeof(badface), ELEPERBLOCK, POINTER, 0); - cosmaxdihed = cos(179.999 * PI / 180.0); - cosmindihed = cos(0.1 * PI / 180.0); - tallslivers(true); - dumpbadtets(); - delete badtetrahedrons; - badtetrahedrons = NULL; - // DEBUG END */ - - delete caveshlist; - delete caveshbdlist; - caveshlist = NULL; - caveshbdlist = NULL; - - // Delete work lists. - delete frontlist; - delete misfrontlist; - delete spinshlist; - delete newsegshlist; - delete ptlist; - delete conlist; - delete flipque; - delete viri; -} - -//// //// -//// //// -//// steiner_cxx ////////////////////////////////////////////////////////////// - -//// reconstruct_cxx ////////////////////////////////////////////////////////// -//// //// -//// //// - -/////////////////////////////////////////////////////////////////////////////// -// // -// transfernodes() Transfer nodes from 'io->pointlist' to 'this->points'. // -// // -// 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. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::transfernodes() -{ - point pointloop; - REAL x, y, z; - int coordindex; - int attribindex; - int mtrindex; - int i, j; - - // Read the points. - coordindex = 0; - attribindex = 0; - mtrindex = 0; - for (i = 0; i < in->numberofpoints; i++) { - makepoint(&pointloop); - // Read the point coordinates. - x = pointloop[0] = in->pointlist[coordindex++]; - y = pointloop[1] = in->pointlist[coordindex++]; - z = pointloop[2] = in->pointlist[coordindex++]; - // Read the point attributes. - for (j = 0; j < in->numberofpointattributes; j++) { - pointloop[3 + j] = in->pointattributelist[attribindex++]; + // Suppress Steiner points on segments. + do { + rmstein = unuverts; + subsegs->traversalinit(); + segloop.sh = shellfacetraverse(subsegs); + while (segloop.sh != (shellface *) NULL) { + remflag = false; + // for (i = 0; i < 2; i++) { + // Don't check the poinytype of pa, it may be a Steiner point but + // has type NACUTEVERTEX due to splitting a type-3 segment. + segloop.shver = 0; // segloop.shver = i; + senext(segloop, nextseg); + spivotself(nextseg); + if (nextseg.sh != dummysh) { + pa = sdest(segloop); // p is going to be checked for removal. + nextseg.shver = 0; + if (sorg(nextseg) != pa) sesymself(nextseg); + assert(sorg(nextseg) == pa); + if (!coarseflag) { + // try to remove it if it is not an input point. + j = pointmark(pa) - in->firstnumber; + if (j >= in->numberofpoints) { + if (b->nobisect == 1) { + // '-Y'. Remove p if it is on the hull. + sstpivot(&segloop, &checktet); + assert(checktet.tet != dummytet); + pa = apex(checktet); + do { + if (!fnextself(checktet)) { + // Meet a boundary face - p is on the hull. + remflag = true; break; + } + } while (pa != apex(checktet)); + } else { + // '-YY'. Remove p whatever it is on the hull or not. + remflag = true; + } + } + } else { + // Check if this vertex can be coarsed. + if (b->nobisect == 0) { + if (b->metric) { + // assert(pa[pointmtrindex] > 0.0); + len = 0.0; + neipt = sorg(segloop); + for (j = 0; j < 2; j++) { + len += distance(pa, neipt); + /*// Is neipt inside the sparse ball of pa? + if (len < pa[pointmtrindex]) { + // Yes, the local of pa is too dense, corse it. + remflag = true; break; + } */ + neipt = sdest(nextseg); + } + len /= 2.0; + // Carse it if the average edge lengh is small. + remflag = len < pa[pointmtrindex]; + } else { + // Coarse it if (1) it is an input point and its pointmarker + // is zero, or (2) it is a Steiner point. + remflag = true; + j = pointmark(pa) - in->firstnumber; + if (j < in->numberofpoints) { + remflag = (in->pointmarkerlist[j] == 0); + } + } // if (b->metric) + } // if (b->nobisect == 0) + } // if (!coarseflag) + } // if (nextseg.sh != dummysh) + // if (remflag) break; + // } // for (i = 0; i < 2; i++) + if (remflag) { + suppresssegpoint(&segloop, spinshlist, newsegshlist, frontlist, + misfrontlist, ptlist, conlist, viri, flipque, coarseflag, false); + } + segloop.sh = shellfacetraverse(subsegs); } - // Read the point metric tensor. - for (j = 0; j < in->numberofpointmtrs; j++) { - pointloop[pointmtrindex + j] = in->pointmtrlist[mtrindex++]; + // Continue if any Steiner point has been removed. + } while (unuverts > rmstein); + + if ((relverts > 0) || coarseflag) { + worklist = new int[points->items + 1]; + // Suppress relocated points & coarse free mesh points. + do { + // Initialize the work list. Each entry of the list counts how many + // times the point has been processed. + for (i = 0; i < points->items + 1; i++) worklist[i] = 0; + rmstein = unuverts; + tetrahedrons->traversalinit(); + checktet.tet = tetrahedrontraverse(); + while (checktet.tet != (tetrahedron *) NULL) { + remflag = false; + for (i = 0; i < 4; i++) { + pa = (point) checktet.tet[4 + i]; + if (pointtype(pa) == FREEVOLVERTEX) { + // NOTE. Chenge the number 3 will change the number n of removed + // Steiner points. In my test, n is larger when it is 1. 3 + // reduces n in a reasonable way (see example, mech_part, + // thepart), 5 results a larger n than 3 does. While the best + // result is no limit of this number, but it makes the code + // extremely slow. + if (worklist[pointmark(pa)] < 3) { + worklist[pointmark(pa)]++; + if (!coarseflag) { + // Remove p if it is a Steiner point. + if (pointmark(pa) >= (in->numberofpoints + in->firstnumber)) { + remflag = true; + } + } else { + if (b->metric) { + // assert(pa[pointmtrindex] > 0.0); + // Form the star of pa. + frontlist->append(&checktet); + formstarpolyhedron(pa, frontlist, ptlist, true); + len = 0.0; + for (j = 0; j < ptlist->len(); j++) { + neipt = * (point *)(* ptlist)[j]; + len += distance(pa, neipt); + } + len /= ptlist->len(); + // Carse it if the average edge length is small. + remflag = len < pa[pointmtrindex]; + frontlist->clear(); + ptlist->clear(); + } else { + // Coarse it if (1) it is an input point and its pointmarker + // is zero, or (2) it is a Steiner point. + remflag = true; + j = pointmark(pa) - in->firstnumber; + if (j < in->numberofpoints) { + remflag = (in->pointmarkerlist[j] == 0); + } + } // if (b->metric) + } // if (!coarseflag) + if (remflag) break; + } // if (worklist[pointmark(pa)] == 0) + } // if (pointtype(pa) == FREEVOLVERTEX) + } // for (i = 0; i < 4; i++) + if (remflag) { + findorg(&checktet, pa); + assert(org(checktet) == pa); + suppressvolpoint(&checktet, frontlist, misfrontlist, ptlist, flipque, + false); + } + checktet.tet = tetrahedrontraverse(); + } + // Continue if any relocated point has been suppressed. + } while (unuverts > rmstein); + + + // Smooth the unsuppressed points if it is not coarse mesh. + if (!coarseflag && (relverts > suprelverts)) { + if (b->verbose) { + printf(" Smoothing relocated points.\n"); + } + for (i = 0; i < points->items + 1; i++) worklist[i] = 0; + tetrahedrons->traversalinit(); + checktet.tet = tetrahedrontraverse(); + while (checktet.tet != (tetrahedron *) NULL) { + for (i = 0; i < 4; i++) { + pa = (point) checktet.tet[4 + i]; + if (pointtype(pa) == FREEVOLVERTEX) { + if (worklist[pointmark(pa)] == 0) { + worklist[pointmark(pa)] = 1; + if (pointmark(pa) >= (in->numberofpoints + in->firstnumber)) { + // Smooth pa. + findorg(&checktet, pa); + frontlist->append(&checktet); + formstarpolyhedron(pa, frontlist, NULL, false); + smoothpoint(pa, NULL, NULL, frontlist, false, NULL); + frontlist->clear(); + } + } // if (worklist[pointmark(pa)] == 0) + } // if (pointtype(pa) == FREEVOLVERTEX) + } // for (i = 0; i < 4; i++) + checktet.tet = tetrahedrontraverse(); + } } - // Determine the smallest and largests x, y and z coordinates. - if (i == 0) { - xmin = xmax = x; - ymin = ymax = y; - zmin = zmax = z; + delete [] worklist; + } + + if (b->verbose > 0) { + if (!coarseflag) { + printf(" %d points removed from boundary", unuverts - oldnum); + if (expcavcount > 0) { + printf(" (%d cavity corrections)", expcavcount); + } + printf("\n"); + if (relverts > 0) { + printf(" %d points relocated (%d suppressed, %d collapsed).\n", + relverts, suprelverts - collapverts, collapverts); + if (smoothvolverts > 0) { + printf(" %d points are smoothed.\n", smoothvolverts); + } + } + if (unsupverts > 0) { + printf(" !! %d points are unsuppressed.\n", unsupverts); + } } else { - xmin = (x < xmin) ? x : xmin; - xmax = (x > xmax) ? x : xmax; - ymin = (y < ymin) ? y : ymin; - ymax = (y > ymax) ? y : ymax; - zmin = (z < zmin) ? z : zmin; - zmax = (z > zmax) ? z : zmax; + printf(" %d points are removed.\n", unuverts - oldnum); } } - // 'longest' is the largest possible edge length formed by input vertices. - x = xmax - xmin; - y = ymax - ymin; - z = zmax - zmin; - longest = sqrt(x * x + y * y + z * z); - if (longest == 0.0) { - printf("Error: The point set is trivial.\n"); - terminatetetgen(3); - } - // Two identical points are distinguished by 'lengthlimit'. - lengthlimit = longest * b->epsilon * 1e+2; + + // Delete work lists. + delete frontlist; + delete misfrontlist; + delete spinshlist; + delete newsegshlist; + delete ptlist; + delete conlist; + delete flipque; + delete viri; } +// +// End of boundary Steiner points removing routines +// + /////////////////////////////////////////////////////////////////////////////// // // -// reconstructmesh() Reconstruct a tetrahedral mesh. // +// reconstructmesh() Reconstruct a tetrahedral mesh from a list of // +// tetrahedra and possibly a list of boundary faces. // +// // +// The list of tetrahedra is stored in 'in->tetrahedronlist', the list of // +// boundary faces is stored in 'in->trifacelist'. The tetrahedral mesh is // +// reconstructed in memorypool 'tetrahedrons', its boundary faces (subfaces) // +// are reconstructed in 'subfaces', its boundary edges (subsegments) are // +// reconstructed in 'subsegs'. If the -a switch is used, this procedure will // +// also read a list of REALs from 'in->tetrahedronvolumelist' and set a // +// maximum volume constraint on each tetrahedron. // // // -// The list of tetrahedra will be read from 'in->tetrahedronlist'. If 'in-> // -// trifacelist' is not empty, boundary faces (faces with a non-zero marker) // -// from this list will be inserted into the mesh. In addition, this routine // -// automatically detects boundary faces (subfaces): all hull faces will be // +// If the user has provided the boundary faces in 'in->trifacelist', they // +// will be inserted the mesh. Otherwise subfaces will be identified from the // +// mesh. All hull faces (including faces of the internal holes) will be // // recognized as subfaces, internal faces between two tetrahedra which have // -// different region attributes will also be recognized as subfaces. // +// different attributes will also be recognized as subfaces. // // // // Subsegments will be identified after subfaces are reconstructed. Edges at // // the intersections of non-coplanar subfaces are recognized as subsegments. // @@ -26442,12 +26740,11 @@ long tetgenmesh::reconstructmesh() tetrahedron **tetsperverlist; shellface **facesperverlist; triface tetloop, neightet, neineightet, spintet; - face subloop, neighsh, neineighsh; + face subloop, neighsh, neineighsh, subseg; face sface1, sface2; - face checkseg, subseg; point *idx2verlist; point torg, tdest, tapex, toppo; - point norg, napex; + point norg, ndest, napex; list *neighshlist, *markerlist; REAL sign, attrib, volume; REAL da1, da2; @@ -26457,9 +26754,8 @@ long tetgenmesh::reconstructmesh() int *worklist; int facetidx, marker; int iorg, idest, iapex, ioppo; - int pivot, ipivot, isum; - int maxbandwidth; - int index, i, j, k; + int inorg, indest, inapex; + int index, i, j; if (!b->quiet) { printf("Reconstructing mesh.\n"); @@ -26526,7 +26822,6 @@ long tetgenmesh::reconstructmesh() // Initialize the worklist. worklist = new int[points->items]; for (i = 0; i < points->items; i++) worklist[i] = 0; - maxbandwidth = 0; // Loop all tetrahedra, bond two tetrahedra if they share a common face. tetrahedrons->traversalinit(); @@ -26545,53 +26840,27 @@ long tetgenmesh::reconstructmesh() worklist[iorg] = 1; worklist[idest] = 1; worklist[iapex] = 1; - // Pick the vertex which has the lowest degree. - if ((idx2tetlist[iorg + 1] - idx2tetlist[iorg]) > - (idx2tetlist[idest + 1] - idx2tetlist[idest])) { - if ((idx2tetlist[idest + 1] - idx2tetlist[idest]) > - (idx2tetlist[iapex + 1] - idx2tetlist[iapex])) { - pivot = iapex; - } else { - pivot = idest; - } - } else { - if ((idx2tetlist[iorg + 1] - idx2tetlist[iorg]) > - (idx2tetlist[iapex + 1] - idx2tetlist[iapex])) { - pivot = iapex; - } else { - pivot = iorg; - } - } - if ((idx2tetlist[pivot + 1] - idx2tetlist[pivot]) > maxbandwidth) { - maxbandwidth = idx2tetlist[pivot + 1] - idx2tetlist[pivot]; - } bondflag = false; - // Search its neighbor in the adjacent tets of the pivoted vertex. - for (j = idx2tetlist[pivot]; j < idx2tetlist[pivot + 1] && !bondflag; + // Search its neighbor in the adjacent tets of torg. + for (j = idx2tetlist[iorg]; j < idx2tetlist[iorg + 1] && !bondflag; j++) { - // Quickly check if this tet contains the neighbor. - isum = 0; - for (k = 0; k < 4; k++) { - norg = (point) tetsperverlist[j][4 + k]; - ipivot = pointmark(norg) - in->firstnumber; - isum += worklist[ipivot]; - } - if (isum != 3) continue; if (tetsperverlist[j] == tetloop.tet) continue; // Skip myself. - // This tet contains its neighbor, find the face and bond them. neightet.tet = tetsperverlist[j]; for (neightet.loc = 0; neightet.loc < 4; neightet.loc++) { - norg = oppo(neightet); - ipivot = pointmark(norg) - in->firstnumber; - if (worklist[ipivot] == 0) { - // Find! Bond them together and break the loop. -#ifdef SELF_CHECK - sym(neightet, neineightet); - assert(neineightet.tet == dummytet); -#endif - bond(tetloop, neightet); - bondflag = true; - break; + sym(neightet, neineightet); + if (neineightet.tet == dummytet) { + norg = org(neightet); + ndest = dest(neightet); + napex = apex(neightet); + inorg = pointmark(norg) - in->firstnumber; + indest = pointmark(ndest) - in->firstnumber; + inapex = pointmark(napex) - in->firstnumber; + if ((worklist[inorg] + worklist[indest] + worklist[inapex]) == 3) { + // Find! Bond them together and break the loop. + bond(tetloop, neightet); + bondflag = true; + break; + } } } } @@ -26619,10 +26888,6 @@ long tetgenmesh::reconstructmesh() tetloop.tet = tetrahedrontraverse(); } - if (b->verbose) { - printf(" Maximal vertex degree = %d.\n", maxbandwidth); - } - // Subfaces will be inserted into the mesh. It has two phases: // (1) Insert subfaces provided by user (in->trifacelist); // (2) Create subfaces for hull faces (if they're not subface yet) and @@ -26640,40 +26905,19 @@ long tetgenmesh::reconstructmesh() worklist[iorg] = 1; worklist[idest] = 1; worklist[iapex] = 1; - // Pick the vertex which has the lowest degree. - if ((idx2tetlist[iorg + 1] - idx2tetlist[iorg]) > - (idx2tetlist[idest + 1] - idx2tetlist[idest])) { - if ((idx2tetlist[idest + 1] - idx2tetlist[idest]) > - (idx2tetlist[iapex + 1] - idx2tetlist[iapex])) { - pivot = iapex; - } else { - pivot = idest; - } - } else { - if ((idx2tetlist[iorg + 1] - idx2tetlist[iorg]) > - (idx2tetlist[iapex + 1] - idx2tetlist[iapex])) { - pivot = iapex; - } else { - pivot = iorg; - } - } bondflag = false; // Search its neighbor in the adjacent tets of torg. - for (j = idx2tetlist[pivot]; j < idx2tetlist[pivot + 1] && !bondflag; + for (j = idx2tetlist[iorg]; j < idx2tetlist[iorg + 1] && !bondflag; j++) { - // Quickly check if this tet contains the neighbor. - isum = 0; - for (k = 0; k < 4; k++) { - norg = (point) tetsperverlist[j][4 + k]; - ipivot = pointmark(norg) - in->firstnumber; - isum += worklist[ipivot]; - } - if (isum != 3) continue; neightet.tet = tetsperverlist[j]; for (neightet.loc = 0; neightet.loc < 4; neightet.loc++) { - norg = oppo(neightet); - ipivot = pointmark(norg) - in->firstnumber; - if (worklist[ipivot] == 0) { + norg = org(neightet); + ndest = dest(neightet); + napex = apex(neightet); + inorg = pointmark(norg) - in->firstnumber; + indest = pointmark(ndest) - in->firstnumber; + inapex = pointmark(napex) - in->firstnumber; + if ((worklist[inorg] + worklist[indest] + worklist[inapex]) == 3) { bondflag = true; // Find! break; } @@ -26822,7 +27066,7 @@ long tetgenmesh::reconstructmesh() } } else { // No neighbor subface be found, bond 'subloop' to itself. - sdissolve(subloop); // sbond(subloop, subloop); + sbond(subloop, subloop); } neighshlist->clear(); } @@ -26831,7 +27075,6 @@ long tetgenmesh::reconstructmesh() subloop.sh = shellfacetraverse(subfaces); } - // Segments will be introudced. Each segment has a unique marker (1-based). marker = 1; subfaces->traversalinit(); @@ -26883,9 +27126,6 @@ long tetgenmesh::reconstructmesh() do { ssbond(neighsh, subseg); spivotself(neighsh); - if (neighsh.sh == dummysh) { - break; // Only one facet case. - } } while (neighsh.sh != subloop.sh); } } @@ -26893,13 +27133,12 @@ long tetgenmesh::reconstructmesh() } subloop.sh = shellfacetraverse(subfaces); } - // Remember the number of input segments. insegments = subsegs->items; // Find the acute vertices and set them be type ACUTEVERTEX. // Indentify facets and set the facet marker (1-based) for subfaces. - markerlist = new list(sizeof(int), NULL, 256); + markerlist = new list("int"); subfaces->traversalinit(); subloop.sh = shellfacetraverse(subfaces); @@ -26965,7 +27204,7 @@ long tetgenmesh::reconstructmesh() // The mesh is nonconvex now. nonconvex = 1; - /*// Is there periodic boundary confitions? + // Is there periodic boundary confitions? if (checkpbcs) { tetgenio::pbcgroup *pg; pbcdata *pd; @@ -27003,7 +27242,7 @@ long tetgenmesh::reconstructmesh() } // Create the global array 'segpbcgrouptable'. createsegpbcgrouptable(); - }*/ + } delete markerlist; delete neighshlist; @@ -27039,7 +27278,6 @@ void tetgenmesh::insertconstrainedpoints(tetgenio *addio) if (!b->quiet) { printf("Insert additional points into mesh.\n"); } - // Initialize 'flipqueue'. flipqueue = new queue(sizeof(badface)); recenttet.tet = dummytet; @@ -27096,7 +27334,6 @@ void tetgenmesh::insertconstrainedpoints(tetgenio *addio) insertflag = false; } else { setpointtype(newpoint, FREESUBVERTEX); - setpoint2sh(newpoint, sencode(checksh)); } } else { setpointtype(newpoint, FREEVOLVERTEX); @@ -27105,7 +27342,6 @@ void tetgenmesh::insertconstrainedpoints(tetgenio *addio) splittetface(newpoint, &searchtet, flipqueue); } break; - case ENCSEGMENT: case ONEDGE: tsspivot(&searchtet, &checkseg); if (checkseg.sh != dummysh) { @@ -27113,7 +27349,7 @@ void tetgenmesh::insertconstrainedpoints(tetgenio *addio) insertflag = false; } else { setpointtype(newpoint, FREESEGVERTEX); - setpoint2seg(newpoint, sencode(checkseg)); + setpoint2sh(newpoint, sencode(checkseg)); } } else { tspivot(searchtet, checksh); @@ -27122,7 +27358,6 @@ void tetgenmesh::insertconstrainedpoints(tetgenio *addio) insertflag = false; } else { setpointtype(newpoint, FREESUBVERTEX); - setpoint2sh(newpoint, sencode(checksh)); } } else { setpointtype(newpoint, FREEVOLVERTEX); @@ -27146,7 +27381,7 @@ void tetgenmesh::insertconstrainedpoints(tetgenio *addio) if (!insertflag) { pointdealloc(newpoint); } else { - lawson3d(flipqueue); + flip(flipqueue, NULL); } } @@ -27185,7 +27420,7 @@ bool tetgenmesh::p1interpolatebgm(point pt, triface* bgmtet, long *scount) loc = bgm->hullwalk(pt, bgmtet); if (loc == OUTSIDE) { // Perform a brute-force search. - if (!b->quiet && b->verbose) { + if (b->verbose) { printf("Warning: Global point location.\n"); } if (scount) (*scount)++; @@ -27374,7 +27609,7 @@ void tetgenmesh::duplicatebgmesh() if (bploop[bgm->pointmtrindex + i] <= 0.0) { printf("Error: Point %d has non-positive size %g (-m option).\n", bgm->pointmark(bploop), bploop[bgm->pointmtrindex + i]); - terminatetetgen(3); + terminatetetgen(1); } } // Remember the point for searching. @@ -27470,13 +27705,9 @@ void tetgenmesh::duplicatebgmesh() delete [] idx2bplist; } -//// //// -//// //// -//// reconstruct_cxx ////////////////////////////////////////////////////////// - -//// refine_cxx /////////////////////////////////////////////////////////////// -//// //// -//// //// +// +// Begin of Delaunay refinement routines +// /////////////////////////////////////////////////////////////////////////////// // // @@ -27488,7 +27719,6 @@ void tetgenmesh::duplicatebgmesh() // (2) There are two facets f1 and f2 intersecting at s. The internal // // dihedral angle (*) between f1 and f2 is acute. // // This routine finds the sharp segments and marked them as type SHARP. // -// // // The minimum angle between segments (minfaceang) and the minimum dihedral // // angle between facets (minfacetdihed) are calulcated. // // // @@ -27525,6 +27755,7 @@ void tetgenmesh::marksharpsegments(REAL sharpangle) spivotself(prevseg); if (prevseg.sh == dummysh) { // Operate on this seg s. + assert(shelltype(segloop) != SHARP); // It should be unmarked. issharp = false; spivot(segloop, startsh); if (startsh.sh != dummysh) { @@ -27542,7 +27773,7 @@ void tetgenmesh::marksharpsegments(REAL sharpangle) // Get the subface on the adjacent facet. spivot(spinsh, neighsh); // Do not calculate if it is self-bonded. - if ((neighsh.sh != dummysh) && (neighsh.sh != spinsh.sh)) { + if (neighsh.sh != spinsh.sh) { // Calculate the dihedral angle between the two subfaces. ang = facedihedral(eorg, edest, sapex(spinsh), sapex(neighsh)); // Only do check if a sharp angle has not been found. @@ -27553,7 +27784,6 @@ void tetgenmesh::marksharpsegments(REAL sharpangle) } // Go to the next facet. spivotself(spinsh); - if (spinsh.sh == dummysh) break; // A single subface case. } while (spinsh.sh != startsh.sh); // if (!issharp) { // Second check if s forms an acute angle with another seg. @@ -27587,26 +27817,17 @@ void tetgenmesh::marksharpsegments(REAL sharpangle) minfaceang = minfaceang < ang ? minfaceang : ang; // Go to the next facet. spivotself(spinsh); - if (spinsh.sh == dummysh) break; // A single subface case. } while (spinsh.sh != startsh.sh); // } } if (issharp) { setshelltype(segloop, SHARP); // Set the type for all subsegments at forwards. - edest = sdest(segloop); senext(segloop, nextseg); spivotself(nextseg); while (nextseg.sh != dummysh) { - setshelltype(nextseg, SHARP); - // Adjust the direction of nextseg. nextseg.shver = 0; - if (sorg(nextseg) != edest) { - sesymself(nextseg); - } - assert(sorg(nextseg) == edest); - edest = sdest(nextseg); - // Go the next connected subsegment at edest. + setshelltype(nextseg, SHARP); senextself(nextseg); spivotself(nextseg); } @@ -27660,25 +27881,16 @@ void tetgenmesh::marksharpsegments(REAL sharpangle) minfaceang = minfaceang < ang ? minfaceang : ang; // Go to the next facet. spivotself(spinsh); - if (spinsh.sh == dummysh) break; // A single subface case. } while (spinsh.sh != startsh.sh); } if (issharp) { setshelltype(segloop, SHARP); // Set the type for all subsegments at backwards. - eorg = sorg(segloop); senext2(segloop, prevseg); spivotself(prevseg); while (prevseg.sh != dummysh) { - setshelltype(prevseg, SHARP); - // Adjust the direction of prevseg. prevseg.shver = 0; - if (sdest(prevseg) != eorg) { - sesymself(prevseg); - } - assert(sdest(prevseg) == eorg); - eorg = sorg(prevseg); - // Go to the next connected subsegment at eorg. + setshelltype(prevseg, SHARP); senext2self(prevseg); spivotself(prevseg); } @@ -27743,6 +27955,27 @@ void tetgenmesh::decidefeaturepointsizes() maxlen = pow(6.0 * b->maxvolume, 1.0/3.0); } + if (!b->refine) { + // Initially correct types for Steiner points. + featurecount = 0; + points->traversalinit(); + ploop = pointtraverse(); + while (ploop != (point) NULL) { + if (pointtype(ploop) == NACUTEVERTEX) { + if (point2sh(ploop) != (shellface) NULL) { + setpointtype(ploop, FREESEGVERTEX); + featurecount++; + } + } + ploop = pointtraverse(); + } +#ifdef SELF_CHECK + if ((b->verbose > 0) && (featurecount > 0)) { + printf(" %d Steiner points correction.\n", featurecount); + } +#endif + } + // 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. @@ -27760,7 +27993,7 @@ void tetgenmesh::decidefeaturepointsizes() } // Decide the size of p if it is on a sharp segment. if (isfeature) { - // Find a tet containing p; + // Find a tet containing p (checkseg is a sharp seg which contains p). sstpivot(&checkseg, &starttet); // Form star(p). tetlist->append(&starttet); @@ -27773,45 +28006,29 @@ void tetgenmesh::decidefeaturepointsizes() adjpt = * (point *)(* verlist)[i]; if (pointtype(adjpt) == FREESEGVERTEX) { // A Steiner point q. Find the seg it lies on. - sdecode(point2seg(adjpt), checkseg); + sdecode(point2sh(adjpt), checkseg); assert(checkseg.sh != dummysh); checkseg.shver = 0; // Find the origin of this seg. prevseg = checkseg; - e1 = sorg(prevseg); do { senext2(prevseg, testseg); spivotself(testseg); if (testseg.sh == dummysh) break; - // Go to the previous subseg. - prevseg = testseg; - // Adjust the direction of the previous subsegment. + prevseg = testseg; // Go to the previous subseg. prevseg.shver = 0; - if (sdest(prevseg) != e1) { - sesymself(prevseg); - } - assert(sdest(prevseg) == e1); - e1 = sorg(prevseg); } while (true); // Find the dest of this seg. nextseg = checkseg; - e2 = sdest(nextseg); do { senext(nextseg, testseg); spivotself(testseg); if (testseg.sh == dummysh) break; - // Go to the next subseg. - nextseg = testseg; - // Adjust the direction of the nextseg. + nextseg = testseg; // Go to the next subseg. nextseg.shver = 0; - if (sorg(nextseg) != e2) { - sesymself(nextseg); - } - assert(sorg(nextseg) == e2); - e2 = sdest(nextseg); } while (true); - // e1 = sorg(prevseg); - // e2 = sdest(nextseg); + e1 = sorg(prevseg); + e2 = sdest(nextseg); // Check if p is the origin or the dest of this seg. if (ploop == e1) { // Set q to be the dest of this seg. @@ -27857,7 +28074,7 @@ void tetgenmesh::decidefeaturepointsizes() ploop = pointtraverse(); } - if (b->verbose > 1) { + if (b->verbose > 0) { printf(" %d feature points.\n", featurecount); } @@ -27871,44 +28088,30 @@ void tetgenmesh::decidefeaturepointsizes() while (ploop != (point) NULL) { if (pointtype(ploop) == FREESEGVERTEX) { if (ploop[pointmtrindex] == 0.0) { - sdecode(point2seg(ploop), checkseg); + sdecode(point2sh(ploop), checkseg); assert(checkseg.sh != dummysh); if (shelltype(checkseg) == SHARP) { checkseg.shver = 0; // Find the origin of this seg. prevseg = checkseg; - e1 = sorg(prevseg); do { senext2(prevseg, testseg); spivotself(testseg); if (testseg.sh == dummysh) break; prevseg = testseg; // Go the previous subseg. - // Adjust the direction of this subsegmnt. prevseg.shver = 0; - if (sdest(prevseg) != e1) { - sesymself(prevseg); - } - assert(sdest(prevseg) == e1); - e1 = sorg(prevseg); } while (true); // Find the dest of this seg. nextseg = checkseg; - e2 = sdest(nextseg); do { senext(nextseg, testseg); spivotself(testseg); if (testseg.sh == dummysh) break; nextseg = testseg; // Go the next subseg. - // Adjust the direction of this subsegment. nextseg.shver = 0; - if (sorg(nextseg) != e2) { - sesymself(nextseg); - } - assert(sorg(nextseg) == e2); - e2 = sdest(nextseg); } while (true); - // e1 = sorg(prevseg); - // e2 = sdest(nextseg); + e1 = sorg(prevseg); + e2 = sdest(nextseg); len = distance(e1, e2); lfs_0 = distance(e1, ploop); // The following assert() happens when -Y option is used. @@ -27926,7 +28129,7 @@ void tetgenmesh::decidefeaturepointsizes() } // if (pointtype(ploop) != FREESEGVERTEX) ploop = pointtraverse(); } - if ((b->verbose > 1) && (featurecount > 0)) { + if ((b->verbose > 0) && (featurecount > 0)) { printf(" %d Steiner feature points.\n", featurecount); } } @@ -28010,35 +28213,24 @@ void tetgenmesh::enqueueencsub(face* testsub, point encpt, int quenumber, badface *encsub; int i; - if (!smarktested(*testsub)) { - if (!shell2badface(*testsub)) { - encsub = (badface *) badsubfaces->alloc(); - encsub->ss = *testsub; - encsub->forg = sorg(*testsub); - encsub->fdest = sdest(*testsub); - encsub->fapex = sapex(*testsub); - encsub->foppo = (point) encpt; - for (i = 0; i < 3; i++) encsub->cent[i] = cent[i]; - encsub->nextitem = (badface *) NULL; - // Set the pointer of 'encsubseg' into 'testsub'. It has two purposes: - // (1) We can regonize it is encroached; (2) It is uniquely queued. - setshell2badface(encsub->ss, encsub); - // Add the subface to the end of a queue (quenumber = 2, high priority). - *subquetail[quenumber] = encsub; - // Maintain a pointer to the NULL pointer at the end of the queue. - subquetail[quenumber] = &encsub->nextitem; - if (b->verbose > 2) { - printf(" Queuing subface (%d, %d, %d) [%d].\n", - pointmark(encsub->forg), pointmark(encsub->fdest), - pointmark(encsub->fapex), quenumber); - } - } - } else { - if (b->verbose > 2) { - printf(" Ignore an encroached subface (%d, %d, %d).\n", - pointmark(sorg(*testsub)), pointmark(sdest(*testsub)), - pointmark(sapex(*testsub))); - } + encsub = (badface *) badsubfaces->alloc(); + encsub->ss = *testsub; + encsub->forg = sorg(*testsub); + encsub->fdest = sdest(*testsub); + encsub->fapex = sapex(*testsub); + encsub->foppo = (point) encpt; + for (i = 0; i < 3; i++) encsub->cent[i] = cent[i]; + encsub->nextitem = (badface *) NULL; + // Set the pointer of 'encsubseg' into 'testsub'. It has two purposes: + // (1) We can regonize it is encroached; (2) It is uniquely queued. + setshell2badface(encsub->ss, encsub); + // Add the subface to the end of a queue (quenumber = 2, high priority). + *subquetail[quenumber] = encsub; + // Maintain a pointer to the NULL pointer at the end of the queue. + subquetail[quenumber] = &encsub->nextitem; + if (b->verbose > 2) { + printf(" Queuing subface (%d, %d, %d) [%d].\n", pointmark(encsub->forg), + pointmark(encsub->fdest), pointmark(encsub->fapex), quenumber); } } @@ -28199,7 +28391,7 @@ void tetgenmesh::dequeuebadtet() // checkseg4encroach() Check a subsegment to see if it is encroached. // // // // A segment s is encroached if there is a vertex lies inside or on its dia- // -// metral circumsphere, i.e., s faces an angle theta > 90 degrees. // +// metral circumsphere, i.e., s faces an angle theta >= 90 degrees. // // // // If 'testpt' (p) != NULL, only test if 'testseg' (s) is encroached by it, // // else, check all apexes of faces around s. Return TRUE if s is encroached. // @@ -28281,29 +28473,18 @@ bool tetgenmesh::checkseg4encroach(face* testseg, point testpt, point* prefpt, } if (enq && enqflag) { - // This segment is encroached and should be repaired. - if (!smarktested(*testseg)) { - if (!shell2badface(*testseg)) { // Is it not queued yet? - if (b->verbose > 2) { - printf(" Queuing encroaching subsegment (%d, %d).\n", - pointmark(eorg), pointmark(edest)); - } - encsubseg = (badface *) badsubsegs->alloc(); - encsubseg->ss = *testseg; - encsubseg->forg = eorg; - encsubseg->fdest = edest; - encsubseg->foppo = (point) NULL; // Not used. - // Set the pointer of 'encsubseg' into 'testseg'. It has two purposes: - // (1) We can regonize it is encroached; (2) It is uniquely queued. - setshell2badface(encsubseg->ss, encsubseg); - } - } else { - // This segment has been rejected for splitting. Do not queue it. - if (b->verbose > 2) { - printf(" Ignore a rejected encroaching subsegment (%d, %d).\n", - pointmark(eorg), pointmark(edest)); - } - } + if (b->verbose > 2) { + printf(" Queuing encroaching subsegment (%d, %d).\n", + pointmark(eorg), pointmark(edest)); + } + encsubseg = (badface *) badsubsegs->alloc(); + encsubseg->ss = *testseg; + encsubseg->forg = eorg; + encsubseg->fdest = edest; + encsubseg->foppo = (point) NULL; // Not used. + // Set the pointer of 'encsubseg' into 'testseg'. It has two purposes: + // (1) We can regonize it is encroached; (2) It is uniquely queued. + setshell2badface(encsubseg->ss, encsubseg); } return enq; @@ -28332,7 +28513,7 @@ bool tetgenmesh::checksub4encroach(face* testsub, point testpt, bool enqflag) bool enq; int indx[4]; int quenumber; - + enq = false; radius = 0.0; encpt = (point) NULL; @@ -28407,7 +28588,7 @@ bool tetgenmesh::checksub4encroach(face* testsub, point testpt, bool enqflag) } if (enq && enqflag) { - enqueueencsub(testsub, encpt, quenumber, cent); + enqueueencsub(testsub, encpt, quenumber, cent); } return enq; @@ -28564,25 +28745,16 @@ bool tetgenmesh::checktet4badqual(triface* testtet, bool enqflag) if (!enq) { // Check if the user-defined sizing function is satisfied. if (b->metric) { - if (in->tetunsuitable != NULL) { - // Execute the user-defined meshing sizing evaluation. - pa = (point) testtet->tet[4]; - pb = (point) testtet->tet[5]; - pc = (point) testtet->tet[6]; - pd = (point) testtet->tet[7]; - enq = (*(in->tetunsuitable))(pa, pb, pc, pd, elen, volume); - } else { - // assert(b->alpha1 > 0.0); - sdist = sqrt(radius2) / b->alpha1; - for (i = 0; i < 4; i++) { - pa = (point) testtet->tet[4 + i]; - // Get the indicated size of p. - dist = pa[pointmtrindex]; // dist = b->alpha1 * pa[pointmtrindex]; - enq = ((dist < sdist) && (dist > 0.0)); - if (enq) break; // It is bad wrt. a node constraint. - // *** Experiment ! Stop test if c is inside H(a). - // if ((dist > 0.0) && (dist > sdist)) break; - } + // assert(b->alpha1 > 0.0); + sdist = sqrt(radius2) / b->alpha1; + for (i = 0; i < 4; i++) { + pa = (point) testtet->tet[4 + i]; + // Get the indicated size of p. + dist = pa[pointmtrindex]; // dist = b->alpha1 * pa[pointmtrindex]; + enq = ((dist < sdist) && (dist > 0.0)); + if (enq) break; // It is bad wrt. a node constraint. + // *** Experiment ! Stop test if c is inside H(a). + // if ((dist > 0.0) && (dist > sdist)) break; } // *** Experiment ! // enq = (i == 4); // Does c lies outside all sparse-ball? @@ -28622,9 +28794,6 @@ bool tetgenmesh::acceptsegpt(point segpt, point refpt, face* splitseg) REAL L, lfs; int i, j; - // This segment must have not been checked (and rejected) yet. - assert(!smarktested(*splitseg)); - if (b->nobisect == 1) { // '-Y'. It can not be split if it is on the hull. triface spintet; @@ -28809,7 +28978,7 @@ bool tetgenmesh::acceptvolpt(point volpt, list* ceillist, list* verlist) void tetgenmesh::getsplitpoint(point e1, point e2, point refpt, point newpt) { point ei, ej; - REAL split, L, d1, d2; + REAL split, L, d1, d2, ps, rs; bool acutea, acuteb; int i; @@ -28849,14 +29018,69 @@ void tetgenmesh::getsplitpoint(point e1, point e2, point refpt, point newpt) if (b->verbose > 1) { printf(" center %d, split = %.12g.\n", pointmark(ei), split); } + // Add a random perturbation on newpt. + d1 = distance(ei, newpt); + d2 = distance(newpt, refpt); + ps = randgenerator(d2 * b->epsilon2 * 1e+2); + rs = ps / d1; + // Perturb newpt away from ei. + for (i = 0; i < 3; i++) newpt[i] = ei[i] + (1.0+rs) * (newpt[i] - ei[i]); } else { // Both endpoints are acute or not. Split it at the middle. for (i = 0; i < 3; i++) newpt[i] = 0.5 * (e1[i] + e2[i]); + // Add a random perturbation on newpt. + d1 = 0.5 * distance(e1, e2); + ps = randgenerator(d1 * b->epsilon2 * 1e+2); + rs = ps / d1; + for (i = 0; i < 3; i++) newpt[i] = e1[i] + (1.0+rs) * (newpt[i] - e1[i]); } } else { // Split the segment at its midpoint. for (i = 0; i < 3; i++) newpt[i] = 0.5 * (e1[i] + e2[i]); + // Add a random perturbation on newpt. + d1 = 0.5 * distance(e1, e2); + ps = randgenerator(d1 * b->epsilon2 * 1e+2); + rs = ps / d1; + for (i = 0; i < 3; i++) newpt[i] = e1[i] + (1.0+rs) * (newpt[i] - e1[i]); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// shepardinterpolation() Interpolate the local size of a newpoint. // +// // +// The classical Shepard interoplation (inversed weighted distance) is used. // +// (With the choice p = 2). // +// // +// 'verlist' contains a list vertices neighboring to 'newpt'. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::shepardinterpolate(point newpt, list *verlist) +{ + point neipt; + REAL *weights, sumweight; + REAL vec[3]; + int i, j; + + weights = new REAL[verlist->len()]; + sumweight = 0.0; + + // Calculate the weight of each point. + for (i = 0; i < verlist->len(); i++) { + neipt = * (point *)(* verlist)[i]; + for (j = 0; j < 3; j++) vec[j] = neipt[j] - newpt[j]; + weights[i] = 1.0 / dot(vec, vec); + sumweight += weights[i]; + } + // Interpolate. + newpt[pointmtrindex] = 0.0; + for (i = 0; i < verlist->len(); i++) { + neipt = * (point *)(* verlist)[i]; + newpt[pointmtrindex] += (weights[i] * neipt[pointmtrindex]) / sumweight; } + + delete [] weights; } /////////////////////////////////////////////////////////////////////////////// @@ -28900,7 +29124,7 @@ void tetgenmesh::setnewpointsize(point newpt, point e1, point e2) // // /////////////////////////////////////////////////////////////////////////////// -bool tetgenmesh::splitencseg(point newpt, face* splitseg, list* tetlist, +void tetgenmesh::splitencseg(point newpt, face* splitseg, list* tetlist, list* sublist, list* verlist, queue* flipque, bool chkencsub, bool chkbadtet, bool optflag) { @@ -28921,23 +29145,16 @@ bool tetgenmesh::splitencseg(point newpt, face* splitseg, list* tetlist, splitseg->shver = 0; // Insert p, this should always success. sstpivot(splitseg, &starttet); - if (splittetedge(newpt, &starttet, flipque)) { - // Remove locally non-Delaunay faces by flipping. - lawson3d(flipque); - } else { - if (optflag) { - delete mytetlist; - delete myflipque; - } - return false; - } - + splittetedge(newpt, &starttet, flipque); + // Remove locally non-Delaunay faces by flipping. + flip(flipque, NULL); // lawson(NULL, flipque); + if (!optflag) { // Check the two new subsegs to see if they're encroached (not by p). for (i = 0; i < 2; i++) { - //if (!shell2badface(*splitseg)) { + if (!shell2badface(*splitseg)) { checkseg4encroach(splitseg, NULL, NULL, true); - //} + } if (i == 1) break; // Two new segs have been checked. senextself(*splitseg); spivotself(*splitseg); @@ -28955,16 +29172,13 @@ bool tetgenmesh::splitencseg(point newpt, face* splitseg, list* tetlist, formstarpolygon(newpt, sublist, verlist); for (i = 0; i < sublist->len(); i++) { checksh = * (face *)(* sublist)[i]; - //if (!shell2badface(checksh)) { + if (!shell2badface(checksh)) { checksub4encroach(&checksh, NULL, true); - //} + } } sublist->clear(); if (verlist) verlist->clear(); spivotself(spinsh); - if (spinsh.sh == dummysh) { - break; // There's only one facet having this segment. - } } while (spinsh.sh != startsh.sh); } } // if (!optflag) @@ -28998,8 +29212,6 @@ bool tetgenmesh::splitencseg(point newpt, face* splitseg, list* tetlist, delete mytetlist; delete myflipque; } - - return true; } /////////////////////////////////////////////////////////////////////////////// @@ -29020,14 +29232,11 @@ bool tetgenmesh::tallencsegs(point testpt, int n, list **ceillists) list *ceillist; triface ceiltet; face checkseg; - int enccount; // long oldencnum; + long oldencnum; int i, j, k; // Remember the current number of encroached segments. - // oldencnum = badsubsegs->items; - - // Count the number of encroached segments. - enccount = 0; + oldencnum = badsubsegs->items; if (ceillists != (list **) NULL) { for (k = 0; k < n; k++) { @@ -29040,11 +29249,9 @@ bool tetgenmesh::tallencsegs(point testpt, int n, list **ceillists) tsspivot(&ceiltet, &checkseg); if (checkseg.sh != dummysh) { // Found a segment. Test it if it isn't in enc-list. - // if (!shell2badface(checkseg)) { - if (checkseg4encroach(&checkseg, testpt, NULL, true)) { - enccount++; - } - // } + if (!shell2badface(checkseg)) { + checkseg4encroach(&checkseg, testpt, NULL, true); + } } enextself(ceiltet); } @@ -29056,17 +29263,14 @@ bool tetgenmesh::tallencsegs(point testpt, int n, list **ceillists) checkseg.sh = shellfacetraverse(subsegs); while (checkseg.sh != (shellface *) NULL) { // Test it if it isn't in enc-list. - // if (!shell2badface(checkseg)) { - if (checkseg4encroach(&checkseg, testpt, NULL, true)) { - enccount++; - } - // } + if (!shell2badface(checkseg)) { + checkseg4encroach(&checkseg, testpt, NULL, true); + } checkseg.sh = shellfacetraverse(subsegs); } } - // return (badsubsegs->items > oldencnum); - return enccount > 0; + return (badsubsegs->items > oldencnum); } /////////////////////////////////////////////////////////////////////////////// @@ -29087,13 +29291,11 @@ bool tetgenmesh::tallencsubs(point testpt, int n, list** ceillists) list *ceillist; triface ceiltet; face checksh; - int enccount; //long oldencnum; + long oldencnum; int i, k; // Remember the current number of encroached segments. - // oldencnum = badsubfaces->items; - - enccount = 0; // Count the number encroached subfaces. + oldencnum = badsubfaces->items; if (ceillists != (list **) NULL) { for (k = 0; k < n; k++) { @@ -29104,11 +29306,9 @@ bool tetgenmesh::tallencsubs(point testpt, int n, list** ceillists) tspivot(ceiltet, checksh); if (checksh.sh != dummysh) { // Found a subface. Test it if it isn't in enc-list. - //if (!shell2badface(checksh)) { - if (checksub4encroach(&checksh, testpt, true)) { - enccount++; - } - //} + if (!shell2badface(checksh)) { + checksub4encroach(&checksh, testpt, true); + } } } } @@ -29118,17 +29318,14 @@ bool tetgenmesh::tallencsubs(point testpt, int n, list** ceillists) checksh.sh = shellfacetraverse(subfaces); while (checksh.sh != (shellface *) NULL) { // Test it if it isn't in enc-list. - // if (!shell2badface(checksh)) { - if (checksub4encroach(&checksh, testpt, true)) { - enccount++; - } - // } + if (!shell2badface(checksh)) { + checksub4encroach(&checksh, testpt, true); + } checksh.sh = shellfacetraverse(subfaces); } } - //return (badsubfaces->items > oldencnum); - return enccount > 0; + return (badsubfaces->items > oldencnum); } /////////////////////////////////////////////////////////////////////////////// @@ -29170,10 +29367,11 @@ void tetgenmesh::repairencsegs(bool chkencsub, bool chkbadtet) list *tetlist, *sublist; queue *flipque; badface *encloop; - face splitseg; - point newpt, refpt; + face splitseg, symsplitseg; + point newpt, sympt, refpt; point e1, e2; - int nmax, n; + enum locateresult symloc; + int nmax, n, i, j; n = 0; nmax = 128; @@ -29213,9 +29411,70 @@ void tetgenmesh::repairencsegs(bool chkencsub, bool chkbadtet) makepoint(&newpt); getsplitpoint(encloop->forg, encloop->fdest, refpt, newpt); setpointtype(newpt, FREESEGVERTEX); - setpoint2seg(newpt, sencode(splitseg)); + setpoint2sh(newpt, sencode(splitseg)); // Decide whether p can be inserted or not. if (acceptsegpt(newpt, refpt, &splitseg)) { + // Is there periodic boundary condition? + if (checkpbcs) { + // Insert points on other segments of incident pbcgroups. + i = shellmark(splitseg) - 1; + for (j = idx2segpglist[i]; j < idx2segpglist[i + 1]; j++) { + makepoint(&sympt); + symloc = getsegpbcsympoint(newpt, &splitseg, sympt, &symsplitseg, + segpglist[j]); + if (symloc == ONEDGE) { + if (symsplitseg.sh != splitseg.sh) { + // Insert sympt. + setpointtype(sympt, FREESEGVERTEX); + setpoint2sh(sympt, sencode(symsplitseg)); + // Save the endpoints of the seg for size interpolation. + e1 = sorg(symsplitseg); + if (shelltype(symsplitseg) == SHARP) { + e2 = sdest(symsplitseg); + } else { + e2 = (point) NULL; // No need to do size interpolation. + } + if (!b->fliprepair) { + // Form BC(symp), B(symp), CBC(symp)s, C(symp)s. + formbowatcavity(sympt, &symsplitseg, NULL, &n, &nmax, + sublists, subceillists, tetlists, ceillists); + // Validate BC(symp), B(symp), CBC(symp)s, C(symp)s. + if (trimbowatcavity(sympt, &symsplitseg, n, sublists, + subceillists, tetlists, ceillists, -1.0)) { + bowatinsertsite(sympt, &symsplitseg, n, sublists, + subceillists, tetlists, ceillists, NULL, flipque, + true, chkencsub, chkbadtet); + setnewpointsize(sympt, e1, e2); + if (steinerleft > 0) steinerleft--; + } else { + // p did not insert for invalid BC(symp). + pointdealloc(sympt); + } + // Free the memory allocated in formbowatcavity(). + releasebowatcavity(&symsplitseg, n, sublists, subceillists, + tetlists, ceillists); + } else { + splitencseg(sympt, &symsplitseg, tetlist, sublist, NULL, + flipque, chkencsub, chkbadtet, false); + setnewpointsize(sympt, e1, e2); + if (steinerleft > 0) steinerleft--; + } + } else { + // The sympt are on the same segment. It is possible when + // splitseg is the symmetric rotating axes. + pointdealloc(sympt); + } + } else if (symloc == ONVERTEX) { + // The sympt already exists. It is possible when two pbc + // groups are exactly the same. Omit this point. + pointdealloc(sympt); + } else { + // Do not isnert symp for unknown cases: ONFACE, OUTSIDE. + // assert(0); + pointdealloc(sympt); + } + } // for (j = idx2segpglist[i]; j < idx2segpglist[i + 1]; j++) + } // if (checkpbcs) // Save the endpoints of the seg for size interpolation. e1 = sorg(splitseg); if (shelltype(splitseg) == SHARP) { @@ -29243,22 +29502,12 @@ void tetgenmesh::repairencsegs(bool chkencsub, bool chkbadtet) releasebowatcavity(&splitseg, n, sublists, subceillists, tetlists, ceillists); } else { - if (splitencseg(newpt, &splitseg, tetlist, sublist, NULL, flipque, - chkencsub, chkbadtet, false)) { - setnewpointsize(newpt, e1, e2); - if (steinerleft > 0) steinerleft--; - } else { - // Fail to split the segment. It MUST be caused by a very flat - // tet connected at the splitting segment. We do not handle - // this case yet. Hopefully, the later repairs will remove - // the flat tet and hence the segment can be split later. - pointdealloc(newpt); - } + splitencseg(newpt, &splitseg, tetlist, sublist, NULL, flipque, + chkencsub, chkbadtet, false); + setnewpointsize(newpt, e1, e2); + if (steinerleft > 0) steinerleft--; } } else { - // This segment can not be split for not meeting the rules in - // acceptsegpt(). Mark it to avoid re-checking it later. - smarktest(splitseg); // p did not accept for insertion. pointdealloc(newpt); } // if (checkseg4splitting(newpt, &splitseg)) @@ -29300,11 +29549,10 @@ void tetgenmesh::repairencsubs(bool chkbadtet) list *verlist; badface *encloop; face splitsub, symsplitsub; - point newpt, e1; - enum locateresult loc; - REAL normal[3], len; + point newpt, sympt, e1; + enum locateresult loc, symloc; bool reject; - long oldptnum, oldencsegnum; + long oldptnum; int quenumber, n, i; n = 0; @@ -29338,21 +29586,7 @@ void tetgenmesh::repairencsubs(bool chkbadtet) // Set the abovepoint of f for point location. abovepoint = facetabovepointarray[shellmark(splitsub)]; if (abovepoint == (point) NULL) { - // getfacetabovepoint(&splitsub); - // Calculate an abovepoint in dummypoint. - facenormal2(encloop->forg, encloop->fdest, encloop->fapex, normal, 1); - len = sqrt(DOT(normal, normal)); - normal[0] /= len; - normal[1] /= len; - normal[2] /= len; - len = DIST(encloop->forg, encloop->fdest); - len += DIST(encloop->fdest, encloop->fapex); - len += DIST(encloop->fapex, encloop->forg); - len /= 3.0; - dummypoint[0] = encloop->forg[0] + len * normal[0]; - dummypoint[1] = encloop->forg[1] + len * normal[1]; - dummypoint[2] = encloop->forg[2] + len * normal[2]; - abovepoint = dummypoint; + getfacetabovepoint(&splitsub); } // Locate p, start from f, stop at segment (1), use a tolerance to // detect ONVERTEX or OUTSIDE case. Update f on return. @@ -29362,22 +29596,10 @@ void tetgenmesh::repairencsubs(bool chkbadtet) formbowatcavity(newpt, NULL, &splitsub, &n, NULL, &sublist, &subceillist, tetlists, ceillists); // Check for encroached subsegments (on B(p)). - oldencsegnum = badsubsegs->items; reject = tallencsegs(newpt, 2, ceillists); - if (reject && (oldencsegnum == badsubsegs->items)) { - // 'newpt' encroaches upon some subsegments. But none of them can - // be split. So this subface can't be split as well. Mark it to - // avoid re-checking it later. - smarktest(encloop->ss); - } // Execute point accept rule if p does not encroach upon any segment. if (!reject) { reject = !acceptfacpt(newpt, subceillist, verlist); - if (reject) { - // 'newpt' lies in some protecting balls. This subface can't be - // split. Mark it to avoid re-checking it later. - smarktest(encloop->ss); - } } if (!reject) { // Validate/update cavity. @@ -29392,6 +29614,69 @@ void tetgenmesh::repairencsubs(bool chkbadtet) reject = !sinfected(encloop->ss); } if (!reject) { + if (checkpbcs) { + if (shellpbcgroup(splitsub) >= 0) { + // Check for splitting of the symmetric subface of f. + makepoint(&sympt); + symloc = getsubpbcsympoint(newpt,&splitsub,sympt,&symsplitsub); + if (symloc != ONVERTEX) { + // Release CBC(p) and BC(p) and free the memory.. + releasebowatcavity(NULL, 2, &sublist, &subceillist, tetlists, + ceillists); + // Form CBC(symp), C(symp), BC(sympt) and B(sympt). + formbowatcavity(sympt, NULL, &symsplitsub, &n, NULL, &sublist, + &subceillist, tetlists, ceillists); + reject = tallencsegs(sympt, 2, ceillists); + if (!reject) { + reject = !acceptfacpt(sympt, subceillist, verlist); + } + if (!reject) { + reject = !trimbowatcavity(sympt,NULL,n,&sublist,&subceillist, + tetlists, ceillists, -1.0); + } + if (!reject) { + // Insert sympt. + setpoint2pbcpt(newpt, sympt); + setpoint2pbcpt(sympt, newpt); + setpointtype(sympt, FREESUBVERTEX); + setpoint2sh(sympt, sencode(symsplitsub)); + // Save a point for size interpolation. + e1 = sorg(symsplitsub); + bowatinsertsite(sympt, NULL, n, &sublist, &subceillist, + tetlists,ceillists,NULL,NULL,false,true,chkbadtet); + setnewpointsize(sympt, e1, NULL); + if (steinerleft > 0) steinerleft--; + // Release CBC(symp) and BC(symp) and free the memory.. + releasebowatcavity(NULL, n, &sublist, &subceillist, tetlists, + ceillists); + } else { + // symp is rejected for one of the following reasons: + // (1) BC(symp) is not valid; or + // (2) symp encroaches upon some subsegments (queued); or + // (3) symp is rejected by point accepting rule. + pointdealloc(sympt); + // Cavity will be released by the following code. + } + } else { + // Do not insert sympt for invalid PBC data. + pointdealloc(sympt); + // p is rejected due to symp. + reject = true; + } + } + } // if (checkpbcs) + } + if (!reject) { + // Insert p. + if (checkpbcs) { + if (shellpbcgroup(splitsub) >= 0) { + // Form CBC(p), C(p), BC(p) and B(p). + formbowatcavity(newpt, NULL, &splitsub, &n, NULL, &sublist, + &subceillist, tetlists, ceillists); + trimbowatcavity(newpt, NULL, n, &sublist, &subceillist, tetlists, + ceillists, -1.0); + } + } // Save a point for size interpolation. e1 = sorg(splitsub); bowatinsertsite(newpt, NULL, n, &sublist, &subceillist, tetlists, @@ -29415,7 +29700,7 @@ void tetgenmesh::repairencsubs(bool chkbadtet) // Repair enc-subsegments. oldptnum = points->items; repairencsegs(true, chkbadtet); - /*if (points->items > oldptnum) { + if (points->items > oldptnum) { // Some enc-subsegments got split. Try to repair f later. splitsub = encloop->ss; if (!isdead(&splitsub)) { @@ -29423,7 +29708,7 @@ void tetgenmesh::repairencsubs(bool chkbadtet) checksub4encroach(&splitsub, NULL, true); } } - }*/ + } } } } else { @@ -29443,24 +29728,19 @@ void tetgenmesh::repairencsubs(bool chkbadtet) pointmark(encloop->fapex)); printf(" New point %d is coincident with an existing vertex %d\n", pointmark(newpt), pointmark(sorg(splitsub))); - terminatetetgen(2); + internalerror(); } - assert(loc == OUTSIDE); - // The circumcenter lies outside of the facet. Mark it to avoid - // rechecking it later. - smarktest(encloop->ss); // Case (2) can happen when thers is a segment s which is close to f // and is non-conforming Delaunay. The circumcenter of f encroaches // upon s, but the circumcenter of s is rejected for insertion. pointdealloc(newpt); } // if ((loc != ONVERTEX) && (loc != OUTSIDE)) - } /*else { + } else { if (!isdead(&splitsub)) { // The subface has been changed, re-check it. checksub4encroach(&splitsub, NULL, true); } } // if (!isdead(&splitsub) && (sorg(splitsub) == encloop->forg) && - */ // Remove this entry from list. badfacedealloc(badsubfaces, encloop); } // while ((badsubfaces->items > 0) && (steinerleft != 0)) @@ -29484,7 +29764,6 @@ void tetgenmesh::repairbadtets() { list *tetlist, *ceillist; list *verlist; - arraypool *histtetarray; badface *badtet; triface starttet; point newpt, e1; @@ -29497,8 +29776,6 @@ void tetgenmesh::repairbadtets() ceillist = new list(sizeof(triface), NULL, 1024); verlist = new list(sizeof(point *), NULL, 256); - histtetarray = new arraypool(sizeof(triface), 8); - // Loop until pool 'badtetrahedrons' is empty. Note that steinerleft == -1 // if an unlimited number of Steiner points is allowed. while ((badtetrahedrons->items > 0) && (steinerleft != 0)) { @@ -29522,11 +29799,7 @@ void tetgenmesh::repairbadtets() setpointtype(newpt, FREEVOLVERTEX); // Locate p. starttet = badtet->tt; - //loc = preciselocate(newpt, &starttet, tetrahedrons->items); - loc = locate2(newpt, &starttet, histtetarray); - if (b->verbose > 1) { - printf(" loc = %d.\n", (int) loc); - } + loc = preciselocate(newpt, &starttet, tetrahedrons->items); if ((loc != ONVERTEX) && (loc != OUTSIDE)) { // For BC(p) and B(p). infect(starttet); @@ -29614,7 +29887,7 @@ void tetgenmesh::repairbadtets() pointmark(badtet->fapex), pointmark(badtet->foppo)); printf(" New point %d is coincident with an existing vertex %d\n", pointmark(newpt), pointmark(org(starttet))); - terminatetetgen(2); + internalerror(); } // Case (2) can happen when there is a segment s (or subface f) which // is close to f and is non-conforming Delaunay. The circumcenter @@ -29630,7 +29903,6 @@ void tetgenmesh::repairbadtets() delete tetlist; delete ceillist; delete verlist; - delete histtetarray; } /////////////////////////////////////////////////////////////////////////////// @@ -29732,13 +30004,40 @@ void tetgenmesh::enforcequality() delete badsubsegs; } -//// //// -//// //// -//// refine_cxx /////////////////////////////////////////////////////////////// +// +// End of Delaunay refinement routines +// + +// +// Begin of mesh optimization routines +// -//// optimize_cxx ///////////////////////////////////////////////////////////// -//// //// -//// //// +void tetgenmesh::dumpbadtets() +{ + FILE *fout; + badface *remtet; + + // Write out a file of remaining bad tets. + printf(" Writing bad tets to file bad-dump.lua.\n"); + fout = fopen("bad-dump.lua", "w"); + fprintf(fout, "-- %ld remaining bad tets (> %g degree).\n", + badtetrahedrons->items, b->maxdihedral); + badtetrahedrons->traversalinit(); + remtet = badfacetraverse(badtetrahedrons); + while (remtet != (badface *) NULL) { + if (!isdead(&remtet->tt) && org(remtet->tt) == remtet->forg && + dest(remtet->tt) == remtet->fdest && + apex(remtet->tt) == remtet->fapex && + oppo(remtet->tt) == remtet->foppo) { + fprintf(fout, "p:draw_tet(%d, %d, %d, %d) -- %g\n", + pointmark(remtet->forg), pointmark(remtet->fdest), + pointmark(remtet->fapex), pointmark(remtet->foppo), + acos(remtet->key) * 180.0 / PI); + } + remtet = badfacetraverse(badtetrahedrons); + } + fclose(fout); +} /////////////////////////////////////////////////////////////////////////////// // // @@ -29816,8 +30115,8 @@ bool tetgenmesh::checktet4ill(triface* testtet, bool enqflag) // // // A tet t needs to be optimized if it fails to certain quality measures. // // The only quality measure currently used is the maximal dihedral angle at // -// edges. The desired maximal dihedral angle is 'b->maxdihedal' (set by the // -// '-qqq' option. // +// edges. The desired maximal dihedral angle is b->maxdihed (set by the '-s' // +// option. // // // // A tet may have one, two, or three big dihedral angles. Examples: Let the // // tet t = abcd, and its four corners are nearly co-planar. Then t has one // @@ -29834,9 +30133,10 @@ bool tetgenmesh::checktet4opt(triface* testtet, bool enqflag) point pa, pb, pc, pd; REAL N[4][3], len; REAL cosd; - int count; + bool enq; int i, j; + enq = false; pa = (point) testtet->tet[4]; pb = (point) testtet->tet[5]; pc = (point) testtet->tet[6]; @@ -29850,10 +30150,7 @@ bool tetgenmesh::checktet4opt(triface* testtet, bool enqflag) for (j = 0; j < 3; j++) N[i][j] /= len; } } - - count = 0; - - // Find all large dihedral angles. + // Find all large dihedral angles. for (i = 0; i < 6; i++) { // Locate the edge i and calculate the dihedral angle at the edge. testtet->loc = 0; @@ -29888,7 +30185,6 @@ bool tetgenmesh::checktet4opt(triface* testtet, bool enqflag) } if (cosd < cosmaxdihed) { // A bigger dihedral angle. - count++; if (enqflag) { // Allocate space for the bad tetrahedron. newbadtet = (badface *) badtetrahedrons->alloc(); @@ -29907,10 +30203,11 @@ bool tetgenmesh::checktet4opt(triface* testtet, bool enqflag) acos(cosd) * 180.0 / PI); } } + enq = true; } } - return count > 0; + return enq; } /////////////////////////////////////////////////////////////////////////////// @@ -29940,23 +30237,20 @@ bool tetgenmesh::removeedge(badface* remedge, bool optflag) { triface abcd, badc; // Tet configuration at edge ab. triface baccasing, abdcasing; - triface abtetlist[21]; // Old configuration at ab, save maximum 20 tets. - triface bftetlist[21]; // Old configuration at bf, save maximum 20 tets. - triface newtetlist[90]; // New configuration after removing ab. + triface abtetlist[11]; // Old configuration at ab, save maximum 10 tets. + triface bftetlist[11]; // Old configuration at bf, save maximum 10 tets. + triface newtetlist[33]; // New configuration after removing ab. face checksh; - //enum fliptype fty; + enum fliptype fty; REAL key; bool remflag, subflag; - int n, n1, m, i, j, k; - - triface newtet; - point *ppt; + int n, n1, m, i, j; // First try to strip abcd from the mesh. This needs to check either ab // or cd is on the hull. Try to strip it whichever is true. abcd = remedge->tt; adjustedgering(abcd, CCW); - k = 0; + i = 0; do { sym(abcd, baccasing); // Is the tet on the hull? @@ -29965,28 +30259,11 @@ bool tetgenmesh::removeedge(badface* remedge, bool optflag) sym(badc, abdcasing); if (abdcasing.tet == dummytet) { // Strip the tet from the mesh -> ab is removed as well. - if (removetetbypeeloff(&abcd, newtetlist)) { + if (removetetbypeeloff(&abcd)) { if (b->verbose > 1) { - printf(" Stripped tet from the mesh.\n"); - } - optcount[0]++; - opt_tet_peels++; - // edge is removed. Test new tets for further optimization. - for (i = 0; i < 2; i++) { - if (optflag) { - checktet4opt(&(newtetlist[i]), true); - } else { - checktet4ill(&(newtetlist[i]), true); - } - } - // Update the point-to-tet map - for (i = 0; i < 2; i++) { - newtet = newtetlist[i]; - ppt = (point *) &(newtet.tet[4]); - for (j = 0; j < 4; j++) { - setpoint2tet(ppt[j], encode(newtet)); - } + printf(" Stripped tet from the mesh.\n"); } + optcount[0]++; return true; } } @@ -29995,9 +30272,9 @@ bool tetgenmesh::removeedge(badface* remedge, bool optflag) enext2fnextself(abcd); enext2self(abcd); esymself(abcd); // --> cdab - k++; - } while (k < 2); - + i++; + } while (i < 2); + // Get the tets configuration at ab. Collect maximum 10 tets. subflag = false; abcd = remedge->tt; @@ -30006,7 +30283,7 @@ bool tetgenmesh::removeedge(badface* remedge, bool optflag) abtetlist[n] = abcd; do { // Is the list full? - if (n == 20) break; + if (n == 10) break; // Stop if a subface appears. tspivot(abtetlist[n], checksh); if (checksh.sh != dummysh) { @@ -30023,7 +30300,7 @@ bool tetgenmesh::removeedge(badface* remedge, bool optflag) key = remedge->key; if (subflag && optflag) { - /*abcd = remedge->tt; + abcd = remedge->tt; adjustedgering(abcd, CCW); // Try to flip face cda or cdb to improve quality. for (j = 0; j < 2; j++) { @@ -30071,29 +30348,19 @@ bool tetgenmesh::removeedge(badface* remedge, bool optflag) for (i = 0; i < m; i++) { checktet4opt(&(newtetlist[i]), true); } - // Update the point-to-tet map - for (i = 0; i < m; i++) { - newtet = newtetlist[i]; - ppt = (point *) &(newtet.tet[4]); - for (j = 0; j < 4; j++) { - setpoint2tet(ppt[j], encode(newtet)); - } - } optcount[1]++; - opt_face_flips++; return true; } - } // j - */ + } // if (j = 0; j < 2; j++) // Faces are not flipable. Return. return false; } - // 2 < n < 20. + // 2 <= n <= 10. if (n == 3) { // There are three tets at ab. Try to do a flip32 at ab. remflag = removeedgebyflip32(&key, abtetlist, newtetlist, NULL); - } else if ((n > 3) && (n <= b->maxflipedgelinksize)) { + } else if ((n == 4) || (n == 5) || (n == 6)) { // Four tets case. Try to do edge transformation. remflag = removeedgebytranNM(&key,n,abtetlist,newtetlist,NULL,NULL,NULL); } else { @@ -30116,9 +30383,9 @@ bool tetgenmesh::removeedge(badface* remedge, bool optflag) } printf("\n"); } - } + } - if (!remflag && (key == remedge->key) && (n <= b->maxflipedgelinksize)) { + if (!remflag && (key == remedge->key) && (n < 7)) { // Try to do a combination of flips. n1 = 0; remflag = removeedgebycombNM(&key, n, abtetlist, &n1, bftetlist, @@ -30155,195 +30422,11 @@ bool tetgenmesh::removeedge(badface* remedge, bool optflag) checktet4ill(&(newtetlist[i]), true); } } - // Update the point-to-tet map - for (i = 0; i < m; i++) { - newtet = newtetlist[i]; - ppt = (point *) &(newtet.tet[4]); - for (j = 0; j < 4; j++) { - setpoint2tet(ppt[j], encode(newtet)); - } - } - opt_edge_flips++; } return remflag; } -/////////////////////////////////////////////////////////////////////////////// -// // -// smoothpoint() Smooth a volume/segment point. // -// // -// 'smthpt' (p) is inside the polyhedron (C) bounded by faces in 'starlist'. // -// This routine moves p inside C until an object function is maximized. // -// // -// Default, the CCW edge ring of the faces on C points to p. If 'invtori' is // -// TRUE, the orientation is inversed. // -// // -// If 'key' != NULL, it contains an object value to be improved. Current it // -// means the cosine of the largest dihedral angle. In such case, the point // -// is smoothed only if the final configuration improves the object value, it // -// is returned by the 'key'. // -// // -/////////////////////////////////////////////////////////////////////////////// - -bool tetgenmesh::smoothpoint(point smthpt, point e1, point e2, list *starlist, - bool invtori, REAL *key) -{ - triface starttet; - point pa, pb, pc; - REAL fcent[3], startpt[3], nextpt[3], bestpt[3]; - REAL iniTmax, oldTmax, newTmax; - REAL ori, aspT, aspTmax, imprate; - REAL cosd, maxcosd; - bool segflag, randflag; //, subflag; - int numdirs; - int iter, i, j; - - // Is p a segment vertex? - segflag = (e1 != (point) NULL); - // Decide the number of moving directions. - numdirs = segflag ? 2 : starlist->len(); - randflag = numdirs > 10; - if (randflag) { - numdirs = 10; // Maximum 10 directions. - } - - // Calculate the initial object value (the largest aspect ratio). - for (i = 0; i < starlist->len(); i++) { - starttet = * (triface *)(* starlist)[i]; - adjustedgering(starttet, !invtori ? CCW : CW); - pa = org(starttet); - pb = dest(starttet); - pc = apex(starttet); - aspT = tetaspectratio(pa, pb, pc, smthpt); - if (i == 0) { - aspTmax = aspT; - } else { - aspTmax = aspT > aspTmax ? aspT : aspTmax; - } - } - iniTmax = aspTmax; - - if (b->verbose > 1) { - printf(" Smooth %s point %d (%g, %g, %g).\n", segflag ? "seg" : "vol", - pointmark(smthpt), smthpt[0], smthpt[1], smthpt[2]); - printf(" Initial max L/h = %g.\n", iniTmax); - } - for (i = 0; i < 3; i++) { - bestpt[i] = startpt[i] = smthpt[i]; - } - - // Do iteration until the new aspTmax does not decrease. - newTmax = iniTmax; - iter = 0; - while (true) { - // Find the best next location. - oldTmax = newTmax; - for (i = 0; i < numdirs; i++) { - // Calculate the moved point (saved in 'nextpt'). - if (!segflag) { - if (randflag) { - // Randomly pick a direction. - j = (int) randomnation(starlist->len()); - } else { - j = i; - } - starttet = * (triface *)(* starlist)[j]; - adjustedgering(starttet, !invtori ? CCW : CW); - pa = org(starttet); - pb = dest(starttet); - pc = apex(starttet); - for (j = 0; j < 3; j++) { - fcent[j] = (pa[j] + pb[j] + pc[j]) / 3.0; - } - } else { - for (j = 0; j < 3; j++) { - fcent[j] = (i == 0 ? e1[j] : e2[j]); - } - } - for (j = 0; j < 3; j++) { - nextpt[j] = startpt[j] + 0.01 * (fcent[j] - startpt[j]); - } - // Get the largest object value for the new location. - for (j = 0; j < starlist->len(); j++) { - starttet = * (triface *)(* starlist)[j]; - adjustedgering(starttet, !invtori ? CCW : CW); - pa = org(starttet); - pb = dest(starttet); - pc = apex(starttet); - ori = orient3d(pa, pb, pc, nextpt); - if (ori < 0.0) { - aspT = tetaspectratio(pa, pb, pc, nextpt); - if (j == 0) { - aspTmax = aspT; - } else { - aspTmax = aspT > aspTmax ? aspT : aspTmax; - } - } else { - // An invalid new tet. Discard this point. - aspTmax = newTmax; - } // if (ori < 0.0) - // Stop looping when the object value is bigger than before. - if (aspTmax >= newTmax) break; - } // for (j = 0; j < starlist->len(); j++) - if (aspTmax < newTmax) { - // Save the improved object value and the location. - newTmax = aspTmax; - for (j = 0; j < 3; j++) bestpt[j] = nextpt[j]; - } - } // for (i = 0; i < starlist->len(); i++) - // Does the object value improved much? - imprate = fabs(oldTmax - newTmax) / oldTmax; - if (imprate < 1e-3) break; - // Yes, move p to the new location and continue. - for (j = 0; j < 3; j++) startpt[j] = bestpt[j]; - iter++; - } // while (true) - - if (iter > 0) { - // The point is moved. - if (key) { - // Check if the quality is improved by the smoothed point. - maxcosd = 0.0; // = cos(90). - for (j = 0; j < starlist->len(); j++) { - starttet = * (triface *)(* starlist)[j]; - adjustedgering(starttet, !invtori ? CCW : CW); - pa = org(starttet); - pb = dest(starttet); - pc = apex(starttet); - tetalldihedral(pa, pb, pc, startpt, NULL, &cosd, NULL); - if (cosd < *key) { - // This quality will not be improved. Stop. - iter = 0; break; - } else { - // Remeber the worst quality value (of the new configuration). - maxcosd = maxcosd < cosd ? maxcosd : cosd; - } - } - if (iter > 0) *key = maxcosd; - } - } - - if (iter > 0) { - if (segflag) smoothsegverts++; - for (i = 0; i < 3; i++) smthpt[i] = startpt[i]; - if (b->verbose > 1) { - printf(" Move to new location (%g, %g, %g).\n", smthpt[0], smthpt[1], - smthpt[2]); - printf(" Final max L/h = %g. (%d iterations)\n", newTmax, iter); - if (key) { - printf(" Max. dihed = %g (degree).\n", acos(*key) / PI * 180.0); - } - } - return true; - } else { - if (b->verbose > 1) { - printf(" Not smoothed.\n"); - } - return false; - } -} - /////////////////////////////////////////////////////////////////////////////// // // // smoothsliver() Remove a sliver by smoothing a vertex of it. // @@ -30444,26 +30527,24 @@ bool tetgenmesh::splitsliver(badface *remedge, list *tetlist, list *ceillist) bool remflag; int i; - // Let 'remedge->tt' be the edge [a, b]. starttet = remedge->tt; - // Go to the opposite edge [c, d]. + // Check if cd is a segment. adjustedgering(starttet, CCW); enextfnextself(starttet); enextself(starttet); - - // Check if cd is a segment. tsspivot(&starttet, &checkseg); if (b->nobisect == 0) { if (checkseg.sh != dummysh) { - // cd is a segment. The seg will be split. + // cd is a segment. The seg will be split. BUT do not flip! Due to the + // exact predicates, lot of slivers ay be rsulted and hard to remove. checkseg.shver = 0; pt[0] = sorg(checkseg); pt[1] = sdest(checkseg); makepoint(&newpt); getsplitpoint(pt[0], pt[1], NULL, newpt); setpointtype(newpt, FREESEGVERTEX); - setpoint2seg(newpt, sencode(checkseg)); + setpoint2sh(newpt, sencode(checkseg)); // Insert p, this should always success. sstpivot(&checkseg, &starttet); splittetedge(newpt, &starttet, NULL); @@ -30485,19 +30566,14 @@ bool tetgenmesh::splitsliver(badface *remedge, list *tetlist, list *ceillist) } } - // Create the new point p (at the circumcenter of t). - makepoint(&newpt); - /*// Get the four corners. + // Get the four corners. for (i = 0; i < 4; i++) { pt[i] = (point) starttet.tet[4 + i]; } + // Create the new point p (at the circumcenter of t). + makepoint(&newpt); for (i = 0; i < 3; i++) { newpt[i] = 0.25 * (pt[0][i] + pt[1][i] + pt[2][i] + pt[3][i]); - }*/ - pt[0] = org(starttet); - pt[1] = dest(starttet); - for (i = 0; i < 3; i++) { - newpt[i] = 0.5 * (pt[0][i] + pt[1][i]); } setpointtype(newpt, FREEVOLVERTEX); @@ -30562,7 +30638,7 @@ void tetgenmesh::tallslivers(bool optflag) /////////////////////////////////////////////////////////////////////////////// // // -// optimizemesh() Improving the mesh quality. // +// optimizemesh() Improve mesh quality by mesh optimizations. // // // // Available mesh optimizing operations are: (1) multiple edge flips (3-to-2,// // 4-to-4, 5-to-6, etc), (2) free vertex deletion, (3) new vertex insertion. // @@ -30574,218 +30650,261 @@ void tetgenmesh::tallslivers(bool optflag) // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::optimizemesh2(bool optflag) +void tetgenmesh::optimizemesh(bool optflag) { list *splittetlist, *tetlist, *ceillist; - badface *remtet, *newbadtet; - REAL maxdihed, objdihed, cosobjdihed; - long oldflipcount, newflipcount; - long oldpointcount; - int slivercount; - int optpasscount; + badface *remtet, *lastentry; + REAL maxdihed, objdihed, curdihed; + long oldnum; int iter, i; - // Cosines of the six dihedral angles of the tet [a, b, c, d]. - // From cosdd[0] to cosdd[5]: ab, bc, ca, ad, bd, cd. - REAL cosdd[6]; - //int j; - if (!b->quiet) { if (optflag) { - if (b_steinerflag) { - // This routine is called from removesteiners2(); - } else { - printf("Optimizing mesh.\n"); - } + printf("Optimizing mesh.\n"); } else { printf("Repairing mesh.\n"); } } - if (optflag) { - if (b_steinerflag) { - // This routine is called from removesteiners2(); - cosmaxdihed = cos(179.0 * PI / 180.0); - cosmindihed = cos(1.0 * PI / 180.0); - // The radian of the maximum dihedral angle. - maxdihed = 179.0 / 180.0 * PI; - } else { - cosmaxdihed = cos(b->maxdihedral * PI / 180.0); - cosmindihed = cos(b->mindihedral * PI / 180.0); - // The radian of the maximum dihedral angle. - maxdihed = b->maxdihedral / 180.0 * PI; - // A sliver has an angle large than 'objdihed' will be split. - objdihed = b->maxdihedral + 5.0; - if (objdihed < 175.0) objdihed = 175.0; - objdihed = objdihed / 180.0 * PI; - cosobjdihed = cos(objdihed); - } +#ifdef SELF_CHECK + if (optflag && (b->verbose)) { + printf(" level = %d.\n", b->optlevel); } +#endif // Initialize the pool of bad tets. badtetrahedrons = new memorypool(sizeof(badface), ELEPERBLOCK, POINTER, 0); + if (optflag) { + cosmaxdihed = cos(b->maxdihedral * PI / 180.0); + cosmindihed = cos(b->mindihedral * PI / 180.0); + // The radian of the maximum dihedral angle. + maxdihed = b->maxdihedral / 180.0 * PI; + // A sliver has an angle large than 'objdihed' will be split. + objdihed = b->maxdihedral + 5.0; + if (objdihed < 170.0) objdihed = 170.0; + objdihed = objdihed / 180.0 * PI; + } // Looking for non-optimal tets. tallslivers(optflag); - oldpointcount = points->items; - opt_tet_peels = opt_face_flips = opt_edge_flips = 0l; - oldflipcount = newflipcount = 0l; - smoothsegverts = 0l; - optpasscount = 0; - - if (optflag && (b->verbose)) { - printf(" level = %d.\n", b->optlevel); - } - - // Start the mesh optimization iteration. - do { - - if (optflag && (b->verbose > 1)) { - printf(" level = %d.\n", b->optlevel); - } - - // Improve the mesh quality by flips. - iter = 0; - do { - oldflipcount = newflipcount; - // Loop in the list of bad tets. - badtetrahedrons->traversalinit(); - remtet = badfacetraverse(badtetrahedrons); - while (remtet != (badface *) NULL) { - if (!isdead(&remtet->tt) && (org(remtet->tt) == remtet->forg) && - (dest(remtet->tt) == remtet->fdest) && - (apex(remtet->tt) == remtet->fapex) && - (oppo(remtet->tt) == remtet->foppo)) { - if (b->verbose > 1) { - printf(" Repair tet (%d, %d, %d, %d) %g (degree).\n", - pointmark(remtet->forg), pointmark(remtet->fdest), - pointmark(remtet->fapex), pointmark(remtet->foppo), - acos(remtet->key) / PI * 180.0); - } - if (removeedge(remtet, optflag)) { - // Remove the badtet from the list. - badfacedealloc(badtetrahedrons, remtet); + optcount[0] = 0l; // tet strip count. + optcount[1] = 0l; // face (2-3) and edge (2-2) flip count. + optcount[3] = optcount[4] = optcount[5] = optcount[6] = 0l; // edge flips. + optcount[9] = 0l; // combined flip count. + + // Perform edge flip to improve quality. + lastentry = (badface *) NULL; + // Loop until pool 'badtetrahedrons' is empty. + while (badtetrahedrons->items > 0) { + badtetrahedrons->traversalinit(); + remtet = badfacetraverse(badtetrahedrons); + while (remtet != (badface *) NULL) { + // Make sure that the tet is still the same one when it was tested. + // Subsequent transformations may have made it a different tet. + if (!isdead(&remtet->tt) && org(remtet->tt) == remtet->forg && + dest(remtet->tt) == remtet->fdest && + apex(remtet->tt) == remtet->fapex && + oppo(remtet->tt) == remtet->foppo) { + if (b->verbose > 1) { + printf(" Repair tet (%d, %d, %d, %d) %g (degree).\n", + pointmark(remtet->forg), pointmark(remtet->fdest), + pointmark(remtet->fapex), pointmark(remtet->foppo), + acos(remtet->key) / PI * 180.0); + } + if (!removeedge(remtet, optflag)) { + // An unremoveable tet. Check if it forms a loop. + if (lastentry != (badface *) NULL) { + if (remtet == lastentry) break; + } else { + // Remember this tet as a breakpoint. + lastentry = remtet; } } else { - // Remove the badtet from the list. + // Clear the breakpoint. + lastentry = (badface *) NULL; + // Remove the entry from the queue. badfacedealloc(badtetrahedrons, remtet); } - remtet = badfacetraverse(badtetrahedrons); + } else { + // Remove the entry from the queue. + badfacedealloc(badtetrahedrons, remtet); } - iter++; - if (iter > 10) break; // Stop at 10th iterations. - // Count the total number of flips. - newflipcount = opt_tet_peels + opt_face_flips + opt_edge_flips; - // Continue if there are bad tets and new flips. - } while ((badtetrahedrons->items > 0) && (newflipcount > oldflipcount)); + remtet = badfacetraverse(badtetrahedrons); + } + // Stop if the above loop was out by force. + if (remtet != (badface *) NULL) break; + } - if (b_steinerflag) { - // This routine was called from removesteiner2(). Do not repair - // the bad tets by splitting. - badtetrahedrons->restart(); + if (b->verbose) { + if (optcount[0] > 0l) { + printf(" %ld tets are peeled off.\n", optcount[0]); + } + if (optcount[1] > 0l) { + printf(" %ld faces are flipped.\n", optcount[1]); } + if (optcount[3] + optcount[4] + optcount[5] + optcount[6] + + optcount[9] > 0l) { + printf(" %ld edges are flipped.\n", optcount[3] + optcount[4] + + optcount[5] + optcount[6] + optcount[9]); + } + // if (badtetrahedrons->items > 0l) { + // printf(" %ld edges remain.\n", badtetrahedrons->items); + // } + } - if ((badtetrahedrons->items > 0l) && optflag && (b->optlevel > 2)) { - // Get a list of slivers and try to split them. - splittetlist = new list(sizeof(badface), NULL, 256); - tetlist = new list(sizeof(triface), NULL, 256); - ceillist = new list(sizeof(triface), NULL, 256); + if ((badtetrahedrons->items > 0l) && optflag && (b->optlevel > 2)) { + splittetlist = new list(sizeof(badface), NULL, 256); + tetlist = new list(sizeof(triface), NULL, 256); + ceillist = new list(sizeof(triface), NULL, 256); + oldnum = points->items; + smoothsegverts = smoothvolverts = 0; + optcount[1] = 0l; + optcount[3] = optcount[4] = optcount[5] = optcount[6] = 0l; // edge flips. + optcount[9] = 0l; // combined flip count. + iter = 0; + do { // Form a list of slivers to be split and clean the pool. badtetrahedrons->traversalinit(); remtet = badfacetraverse(badtetrahedrons); while (remtet != (badface *) NULL) { splittetlist->append(remtet); + // Remove the entry from the queue. + badfacedealloc(badtetrahedrons, remtet); remtet = badfacetraverse(badtetrahedrons); } - // Clean the pool of bad tets. - badtetrahedrons->restart(); - slivercount = 0; for (i = 0; i < splittetlist->len(); i++) { remtet = (badface *)(* splittetlist)[i]; + // Make sure that the tet is still the same one when it was tested. + // Subsequent transformations may have made it a different tet. if (!isdead(&remtet->tt) && org(remtet->tt) == remtet->forg && dest(remtet->tt) == remtet->fdest && apex(remtet->tt) == remtet->fapex && oppo(remtet->tt) == remtet->foppo) { - // Calculate the six dihedral angles of this tet. - adjustedgering(remtet->tt, CCW); - remtet->forg = org(remtet->tt); - remtet->fdest = dest(remtet->tt); - remtet->fapex = apex(remtet->tt); - remtet->foppo = oppo(remtet->tt); - tetalldihedral(remtet->forg, remtet->fdest, remtet->fapex, - remtet->foppo, cosdd, NULL, NULL); + // The sliver may get smoothed due to a neighboring tet. + curdihed = facedihedral(remtet->forg, remtet->fdest, remtet->fapex, + remtet->foppo); + // The dihedral angle of a tet must less than PI, correct it. + if (curdihed > PI) curdihed = 2 * PI - curdihed; // Is it a large angle? - if (cosdd[0] < cosobjdihed) { - slivercount++; - remtet->key = cosdd[0]; + if (curdihed > objdihed) { + remtet->key = cos(curdihed); if (b->verbose > 1) { - printf(" Split tet (%d, %d, %d, %d) %g (degree).\n", - pointmark(remtet->forg), pointmark(remtet->fdest), - pointmark(remtet->fapex), pointmark(remtet->foppo), - acos(remtet->key) / PI * 180.0); - } - /*if (b->verbose && ((acos(cosdd[0]) / PI * 180) > 179)) { - // For DEBUG only. - printf(" p:draw_tet(%d, %d, %d, %d) -- %d (", + printf(" Get sliver (%d, %d, %d, %d) %g (degree).\n", pointmark(remtet->forg), pointmark(remtet->fdest), pointmark(remtet->fapex), pointmark(remtet->foppo), - slivercount); - // Print the 6 dihedral angles. - for (j = 0; j < 5; j++) { - printf("%4.1f, ", acos(cosdd[j]) / PI * 180.0); - } - printf("%4.1f)\n", acos(cosdd[5]) / PI * 180.0); - }*/ - // Queue this tet. - newbadtet = (badface *) badtetrahedrons->alloc(); - *newbadtet = *remtet; - // Try to remove this tet. - if (!smoothsliver(remtet, tetlist)) { - splitsliver(remtet, tetlist, ceillist); - } - } - } - } // i - - delete splittetlist; - delete tetlist; - delete ceillist; - } - - optpasscount++; - } while ((badtetrahedrons->items > 0) && (optpasscount < b->optpasses)); - - if (b->verbose) { - if (opt_tet_peels > 0l) { - printf(" %ld tet removals.\n", opt_tet_peels); - } - if (opt_face_flips > 0l) { - printf(" %ld face flips.\n", opt_face_flips); - } - if (opt_edge_flips > 0l) { - printf(" %ld edge flips.\n", opt_edge_flips); - } - if ((points->items - oldpointcount) > 0l) { - printf(" %ld point insertions", points->items - oldpointcount); - if (smoothsegverts > 0) { - printf(" (%d on segment)", smoothsegverts); + acos(remtet->key) / PI * 180.0); + } + if (!removeedge(remtet, optflag)) { + if (!smoothsliver(remtet, tetlist)) { + splitsliver(remtet, tetlist, ceillist); + } + } + } + } } - printf("\n"); + iter++; + } while ((badtetrahedrons->items > 0l) && (iter < b->optpasses)); + + if (b->verbose) { + printf(" %d passes.\n", iter); + if ((points->items - oldnum) > 0l) { + printf(" %ld points are inserted (%d on segment).\n", + points->items - oldnum, smoothsegverts); + } + if (optcount[1] > 0l) { + printf(" %ld faces are flipped.\n", optcount[1]); + } + if (optcount[3] + optcount[4] + optcount[5] + optcount[6] + + optcount[9] > 0l) { + printf(" %ld edges are flipped.\n", optcount[3] + optcount[4] + + optcount[5] + optcount[6] + optcount[9]); + } + // if (badtetrahedrons->items > 0l) { + // printf(" %ld edges remain.\n", badtetrahedrons->items); + // } } + delete tetlist; + delete ceillist; + delete splittetlist; } delete badtetrahedrons; badtetrahedrons = (memorypool *) NULL; } -//// //// -//// //// -//// optimize_cxx ///////////////////////////////////////////////////////////// +// +// End of mesh optimization routines +// + +// +// Begin of I/O rouitnes +// + +/////////////////////////////////////////////////////////////////////////////// +// // +// transfernodes() Transfer nodes from 'io->pointlist' to 'this->points'. // +// // +// 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. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::transfernodes() +{ + point pointloop; + REAL x, y, z; + int coordindex; + int attribindex; + int mtrindex; + int i, j; -//// output_cxx /////////////////////////////////////////////////////////////// -//// //// -//// //// + // Read the points. + coordindex = 0; + attribindex = 0; + mtrindex = 0; + for (i = 0; i < in->numberofpoints; i++) { + makepoint(&pointloop); + // Read the point coordinates. + x = pointloop[0] = in->pointlist[coordindex++]; + y = pointloop[1] = in->pointlist[coordindex++]; + z = pointloop[2] = in->pointlist[coordindex++]; + // Read the point attributes. + 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++]; + } + // Determine the smallest and largests x, y and z coordinates. + if (i == 0) { + xmin = xmax = x; + ymin = ymax = y; + zmin = zmax = z; + } else { + xmin = (x < xmin) ? x : xmin; + xmax = (x > xmax) ? x : xmax; + ymin = (y < ymin) ? y : ymin; + ymax = (y > ymax) ? y : ymax; + zmin = (z < zmin) ? z : zmin; + zmax = (z > zmax) ? z : zmax; + } + } + // 'longest' is the largest possible edge length formed by input vertices. + x = xmax - xmin; + y = ymax - ymin; + z = zmax - zmin; + longest = sqrt(x * x + y * y + z * z); + if (longest == 0.0) { + printf("Error: The point set is trivial.\n"); + terminatetetgen(1); + } + // Two identical points are distinguished by 'lengthlimit'. + lengthlimit = longest * b->epsilon * 1e+2; +} /////////////////////////////////////////////////////////////////////////////// // // @@ -30881,6 +31000,7 @@ void tetgenmesh::highorder() // Initialize the 'highordertable'. highordertable = new point[tetrahedrons->items * 6]; if (highordertable == (point *) NULL) { + printf("Error: Out of memory.\n"); terminatetetgen(1); } @@ -30966,62 +31086,6 @@ void tetgenmesh::highorder() } } -/////////////////////////////////////////////////////////////////////////////// -// // -// numberedges() Count the number of edges, save in "meshedges". // -// // -// 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. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::numberedges() -{ - triface tetloop, worktet, spintet; - int hitbdry, i; - - if (!b->plc && !b->refine) { - // Using the Euler formula (V-E+F-T=1) to get the total number of edges. - long faces = (4l * tetrahedrons->items + hullsize) / 2l; - meshedges = points->items + faces - tetrahedrons->items - 1l; - return; - } - - meshedges = 0l; - tetrahedrons->traversalinit(); - tetloop.tet = tetrahedrontraverse(); - while (tetloop.tet != (tetrahedron *) NULL) { - // Count the number of Voronoi faces. Look at the six edges of each - // tetrahedron. Count the edge only if the tetrahedron's pointer is - // smaller than those of all other tetrahedra that share the edge. - worktet.tet = tetloop.tet; - for (i = 0; i < 6; i++) { - worktet.loc = edge2locver[i][0]; - worktet.ver = edge2locver[i][1]; - adjustedgering(worktet, CW); - spintet = worktet; - hitbdry = 0; - while (hitbdry < 2) { - if (fnextself(spintet)) { - if (apex(spintet) == apex(worktet)) break; - if (spintet.tet < worktet.tet) break; - } else { - hitbdry++; - if (hitbdry < 2) { - esym(worktet, spintet); - fnextself(spintet); // In the same tet. - } - } - } - // Count this edge if no adjacent tets are smaller than this tet. - if (spintet.tet >= worktet.tet) { - meshedges++; - } - } - tetloop.tet = tetrahedrontraverse(); - } -} - /////////////////////////////////////////////////////////////////////////////// // // // outnodes() Output the points to a .node file or a tetgenio structure. // @@ -31069,7 +31133,7 @@ void tetgenmesh::outnodes(tetgenio* out) outfile = fopen(outnodefilename, "w"); if (outfile == (FILE *) NULL) { printf("File I/O Error: Cannot create file %s.\n", outnodefilename); - terminatetetgen(3); + terminatetetgen(1); } // Number of points, number of dimensions, number of point attributes, // and number of boundary markers (zero or one). @@ -31078,12 +31142,14 @@ void tetgenmesh::outnodes(tetgenio* out) // Allocate space for 'pointlist'; out->pointlist = new REAL[points->items * 3]; if (out->pointlist == (REAL *) NULL) { + printf("Error: Out of memory.\n"); terminatetetgen(1); } // Allocate space for 'pointattributelist' if necessary; if (nextras > 0) { out->pointattributelist = new REAL[points->items * nextras]; if (out->pointattributelist == (REAL *) NULL) { + printf("Error: Out of memory.\n"); terminatetetgen(1); } } @@ -31091,6 +31157,7 @@ void tetgenmesh::outnodes(tetgenio* out) if (bmark) { out->pointmarkerlist = new int[points->items]; if (out->pointmarkerlist == (int *) NULL) { + printf("Error: Out of memory.\n"); terminatetetgen(1); } } @@ -31267,7 +31334,7 @@ void tetgenmesh::outmetrics(tetgenio* out) outfile = fopen(outmtrfilename, "w"); if (outfile == (FILE *) NULL) { printf("File I/O Error: Cannot create file %s.\n", outmtrfilename); - terminatetetgen(3); + terminatetetgen(1); } // Number of points, number of point metrices, // fprintf(outfile, "%ld %d\n", points->items, sizeoftensor + 3); @@ -31277,6 +31344,7 @@ void tetgenmesh::outmetrics(tetgenio* out) // out->pointmtrlist = new REAL[points->items * (sizeoftensor + 3)]; out->pointmtrlist = new REAL[points->items]; if (out->pointmtrlist == (REAL *) NULL) { + printf("Error: Out of memory.\n"); terminatetetgen(1); } out->numberofpointmtrs = 1; // (sizeoftensor + 3); @@ -31375,7 +31443,6 @@ void tetgenmesh::outelements(tetgenio* out) FILE *outfile; char outelefilename[FILENAMESIZE]; tetrahedron* tptr; - triface worktet, spintet; int *tlist; REAL *talist; int firstindex, shift; @@ -31385,7 +31452,7 @@ void tetgenmesh::outelements(tetgenio* out) point *extralist; int elementnumber; int eextras; - int hitbdry, i; + int i; if (out == (tetgenio *) NULL) { strcpy(outelefilename, b->outfilename); @@ -31411,7 +31478,7 @@ void tetgenmesh::outelements(tetgenio* out) outfile = fopen(outelefilename, "w"); if (outfile == (FILE *) NULL) { printf("File I/O Error: Cannot create file %s.\n", outelefilename); - terminatetetgen(3); + terminatetetgen(1); } // Number of tetras, points per tetra, attributes per tetra. fprintf(outfile, "%ld %d %d\n", tetrahedrons->items, @@ -31421,12 +31488,14 @@ void tetgenmesh::outelements(tetgenio* out) out->tetrahedronlist = new int[tetrahedrons->items * (b->order == 1 ? 4 : 10)]; if (out->tetrahedronlist == (int *) NULL) { + printf("Error: Out of memory.\n"); terminatetetgen(1); } // Allocate memory for output tetrahedron attributes if necessary. if (eextras > 0) { out->tetrahedronattributelist = new REAL[tetrahedrons->items * eextras]; if (out->tetrahedronattributelist == (REAL *) NULL) { + printf("Error: Out of memory.\n"); terminatetetgen(1); } } @@ -31446,21 +31515,12 @@ void tetgenmesh::outelements(tetgenio* out) shift = 1; // Shift the output indices by 1. } - // Count the total edge numbers. - meshedges = 0l; - tetrahedrons->traversalinit(); tptr = tetrahedrontraverse(); elementnumber = firstindex; // in->firstnumber; while (tptr != (tetrahedron *) NULL) { - if (b->noelewritten == 2) { - // Reverse the orientation, such that Orient3D() > 0. - p1 = (point) tptr[5]; - p2 = (point) tptr[4]; - } else { - p1 = (point) tptr[4]; - p2 = (point) tptr[5]; - } + p1 = (point) tptr[4]; + p2 = (point) tptr[5]; p3 = (point) tptr[6]; p4 = (point) tptr[7]; if (out == (tetgenio *) NULL) { @@ -31502,33 +31562,6 @@ void tetgenmesh::outelements(tetgenio* out) // Remember the index of this element. * (int *) (tptr + elemmarkerindex) = elementnumber; } - // Count the number of Voronoi faces. Look at the six edges of each - // tetrahedron. Count the edge only if the tetrahedron's pointer is - // smaller than those of all other tetrahedra that share the edge. - worktet.tet = tptr; - for (i = 0; i < 6; i++) { - worktet.loc = edge2locver[i][0]; - worktet.ver = edge2locver[i][1]; - adjustedgering(worktet, CW); - spintet = worktet; - hitbdry = 0; - while (hitbdry < 2) { - if (fnextself(spintet)) { - if (apex(spintet) == apex(worktet)) break; - if (spintet.tet < worktet.tet) break; - } else { - hitbdry++; - if (hitbdry < 2) { - esym(worktet, spintet); - fnextself(spintet); // In the same tet. - } - } - } - // Count this edge if no adjacent tets are smaller than this tet. - if (spintet.tet >= worktet.tet) { - meshedges++; - } - } tptr = tetrahedrontraverse(); elementnumber++; } @@ -31594,19 +31627,21 @@ void tetgenmesh::outfaces(tetgenio* out) outfile = fopen(facefilename, "w"); if (outfile == (FILE *) NULL) { printf("File I/O Error: Cannot create file %s.\n", facefilename); - terminatetetgen(3); + terminatetetgen(1); } fprintf(outfile, "%ld %d\n", faces, bmark); } else { // Allocate memory for 'trifacelist'. out->trifacelist = new int[faces * 3]; if (out->trifacelist == (int *) NULL) { + printf("Error: Out of memory.\n"); terminatetetgen(1); } // Allocate memory for 'trifacemarkerlist' if necessary. if (bmark) { out->trifacemarkerlist = new int[faces]; if (out->trifacemarkerlist == (int *) NULL) { + printf("Error: Out of memory.\n"); terminatetetgen(1); } } @@ -31614,6 +31649,7 @@ void tetgenmesh::outfaces(tetgenio* out) // '-nn' switch. out->adjtetlist = new int[subfaces->items * 2]; if (out->adjtetlist == (int *) NULL) { + printf("Error: Out of memory.\n"); terminatetetgen(1); } } @@ -31755,13 +31791,14 @@ void tetgenmesh::outhullfaces(tetgenio* out) outfile = fopen(facefilename, "w"); if (outfile == (FILE *) NULL) { printf("File I/O Error: Cannot create file %s.\n", facefilename); - terminatetetgen(3); + terminatetetgen(1); } fprintf(outfile, "%ld 0\n", hullsize); } else { // Allocate memory for 'trifacelist'. out->trifacelist = new int[hullsize * 3]; if (out->trifacelist == (int *) NULL) { + printf("Error: Out of memory.\n"); terminatetetgen(1); } out->numberoftrifaces = hullsize; @@ -31866,7 +31903,7 @@ void tetgenmesh::outsubfaces(tetgenio* out) outfile = fopen(facefilename, "w"); if (outfile == (FILE *) NULL) { printf("File I/O Error: Cannot create file %s.\n", facefilename); - terminatetetgen(3); + terminatetetgen(1); } // Number of subfaces. fprintf(outfile, "%ld %d\n", subfaces->items, bmark); @@ -31874,12 +31911,14 @@ void tetgenmesh::outsubfaces(tetgenio* out) // Allocate memory for 'trifacelist'. out->trifacelist = new int[subfaces->items * 3]; if (out->trifacelist == (int *) NULL) { + printf("Error: Out of memory.\n"); terminatetetgen(1); } if (bmark) { // Allocate memory for 'trifacemarkerlist'. out->trifacemarkerlist = new int[subfaces->items]; if (out->trifacemarkerlist == (int *) NULL) { + printf("Error: Out of memory.\n"); terminatetetgen(1); } } @@ -31887,6 +31926,7 @@ void tetgenmesh::outsubfaces(tetgenio* out) // '-nn' switch. out->adjtetlist = new int[subfaces->items * 2]; if (out->adjtetlist == (int *) NULL) { + printf("Error: Out of memory.\n"); terminatetetgen(1); } } @@ -31990,8 +32030,9 @@ void tetgenmesh::outedges(tetgenio* out) int *elist, *emlist; int index, index1; triface tetloop, worktet, spintet; - face checkseg; + face checksh; point torg, tdest; + long faces, edges; int firstindex, shift; int edgenumber, faceid, marker; int hitbdry, i; @@ -32016,24 +32057,29 @@ void tetgenmesh::outedges(tetgenio* out) index = index1 = 0; faceid = marker = 0; + // Using the Euler formula (V-E+F-T=1) to get the total number of edges. + faces = (4l * tetrahedrons->items + hullsize) / 2l; + edges = points->items + faces - tetrahedrons->items - 1l; + if (out == (tetgenio *) NULL) { outfile = fopen(edgefilename, "w"); if (outfile == (FILE *) NULL) { printf("File I/O Error: Cannot create file %s.\n", edgefilename); - terminatetetgen(3); + terminatetetgen(1); } // Write the number of edges, boundary markers (0 or 1). - fprintf(outfile, "%ld %d\n", meshedges, !b->nobound); + fprintf(outfile, "%ld %d\n", edges, !b->nobound); } else { // Allocate memory for 'edgelist'. - out->edgelist = new int[meshedges * 2]; + out->edgelist = new int[edges * 2]; if (out->edgelist == (int *) NULL) { + printf("Error: Out of memory.\n"); terminatetetgen(1); } if (!b->nobound) { - out->edgemarkerlist = new int[meshedges]; + out->edgemarkerlist = new int[edges]; } - out->numberofedges = meshedges; + out->numberofedges = edges; elist = out->edgelist; emlist = out->edgemarkerlist; } @@ -32084,15 +32130,19 @@ void tetgenmesh::outedges(tetgenio* out) elist[index++] = pointmark(tdest) - shift; } if (!b->nobound) { - // Check if the edge is a segment. - tsspivot(&worktet, &checkseg); - if (checkseg.sh != dummysh) { - marker = shellmark(checkseg); - if (marker == 0) { // Does it have no marker? - marker = 1; // Set the default marker for this segment. + if (hitbdry > 0) { + // It is a boundary edge. Get the boundary marker of the facet + // containing this edge. Note there may have more than one + // facet, choose one arbitrarily. + if ((b->plc || b->refine) && in->facetmarkerlist) { + tspivot(spintet, checksh); + faceid = shellmark(checksh) - 1; + marker = in->facetmarkerlist[faceid]; + } else { + marker = 1; // Indicate it's a boundary edge. } } else { - marker = 0; // It's not a segment. + marker = 0; } if (out == (tetgenio *) NULL) { fprintf(outfile, " %d", marker); @@ -32154,7 +32204,7 @@ void tetgenmesh::outsubsegments(tetgenio* out) outfile = fopen(edgefilename, "w"); if (outfile == (FILE *) NULL) { printf("File I/O Error: Cannot create file %s.\n", edgefilename); - terminatetetgen(3); + terminatetetgen(1); } // Number of subsegments. fprintf(outfile, "%ld\n", subsegs->items); @@ -32162,6 +32212,7 @@ void tetgenmesh::outsubsegments(tetgenio* out) // Allocate memory for 'edgelist'. out->edgelist = new int[subsegs->items * 2]; if (out->edgelist == (int *) NULL) { + printf("Error: Out of memory.\n"); terminatetetgen(1); } out->numberofedges = subsegs->items; @@ -32238,7 +32289,7 @@ void tetgenmesh::outneighbors(tetgenio* out) outfile = fopen(neighborfilename, "w"); if (outfile == (FILE *) NULL) { printf("File I/O Error: Cannot create file %s.\n", neighborfilename); - terminatetetgen(3); + terminatetetgen(1); } // Number of tetrahedra, four faces per tetrahedron. fprintf(outfile, "%ld %d\n", tetrahedrons->items, 4); @@ -32246,6 +32297,7 @@ void tetgenmesh::outneighbors(tetgenio* out) // Allocate memory for 'neighborlist'. out->neighborlist = new int[tetrahedrons->items * 4]; if (out->neighborlist == (int *) NULL) { + printf("Error: Out of memory.\n"); terminatetetgen(1); } nlist = out->neighborlist; @@ -32306,8 +32358,6 @@ void tetgenmesh::outneighbors(tetgenio* out) // internal Delaunay vertex. It is an unbounded polyhedron for a Delaunay // // vertex belonging to the convex hull. // // // -// Comment: Special thanks to Victor Liu for finding and fixing few bugs. // -// // /////////////////////////////////////////////////////////////////////////////// void tetgenmesh::outvoronoi(tetgenio* out) @@ -32347,15 +32397,14 @@ void tetgenmesh::outvoronoi(tetgenio* out) // The number of Delaunay faces (= the number of Voronoi edges). faces = (4l * tetrahedrons->items + hullsize) / 2l; // The number of Delaunay edges (= the number of Voronoi faces). - // edges = points->items + faces - tetrahedrons->items - 1; - edges = meshedges; + edges = points->items + faces - tetrahedrons->items - 1; outfile = (FILE *) NULL; // Avoid compile warnings. if (out == (tetgenio *) NULL) { outfile = fopen(outfilename, "w"); if (outfile == (FILE *) NULL) { printf("File I/O Error: Cannot create file %s.\n", outfilename); - terminatetetgen(3); + terminatetetgen(1); } // Number of voronoi points, 3 dim, no attributes, no marker. fprintf(outfile, "%ld 3 0 0\n", tetrahedrons->items); @@ -32364,6 +32413,7 @@ void tetgenmesh::outvoronoi(tetgenio* out) out->numberofvpoints = (int) tetrahedrons->items; out->vpointlist = new REAL[out->numberofvpoints * 3]; if (out->vpointlist == (REAL *) NULL) { + printf("Error: Out of memory.\n"); terminatetetgen(1); } } @@ -32421,13 +32471,13 @@ void tetgenmesh::outvoronoi(tetgenio* out) outfile = fopen(outfilename, "w"); if (outfile == (FILE *) NULL) { printf("File I/O Error: Cannot create file %s.\n", outfilename); - terminatetetgen(3); + terminatetetgen(1); } // Number of Voronoi edges, no marker. fprintf(outfile, "%ld 0\n", faces); } else { // Allocate space for 'vpointlist'. - out->numberofvedges = (int) faces; + out->numberofedges = (int) faces; out->vedgelist = new tetgenio::voroedge[out->numberofvedges]; } @@ -32525,7 +32575,7 @@ void tetgenmesh::outvoronoi(tetgenio* out) outfile = fopen(outfilename, "w"); if (outfile == (FILE *) NULL) { printf("File I/O Error: Cannot create file %s.\n", outfilename); - terminatetetgen(3); + terminatetetgen(1); } // Number of Voronoi faces. fprintf(outfile, "%ld 0\n", edges); @@ -32533,6 +32583,7 @@ void tetgenmesh::outvoronoi(tetgenio* out) out->numberofvfacets = edges; out->vfacetlist = new tetgenio::vorofacet[out->numberofvfacets]; if (out->vfacetlist == (tetgenio::vorofacet *) NULL) { + printf("Error: Out of memory.\n"); terminatetetgen(1); } } @@ -32652,7 +32703,7 @@ void tetgenmesh::outvoronoi(tetgenio* out) outfile = fopen(outfilename, "w"); if (outfile == (FILE *) NULL) { printf("File I/O Error: Cannot create file %s.\n", outfilename); - terminatetetgen(3); + terminatetetgen(1); } // Number of Voronoi cells. fprintf(outfile, "%ld\n", points->items); @@ -32660,6 +32711,7 @@ void tetgenmesh::outvoronoi(tetgenio* out) out->numberofvcells = points->items; out->vcelllist = new int*[out->numberofvcells]; if (out->vcelllist == (int **) NULL) { + printf("Error: Out of memory.\n"); terminatetetgen(1); } } @@ -32682,8 +32734,8 @@ void tetgenmesh::outvoronoi(tetgenio* out) fprintf(outfile, "%4d %-2d ", vpointcount + shift, tcount); } else { arraysize = tcount; + vertarray = out->vcelllist[vpointcount]; vertarray = new int[arraysize + 1]; - out->vcelllist[vpointcount] = vertarray; vertarray[0] = arraysize; index = 1; } @@ -32702,31 +32754,231 @@ void tetgenmesh::outvoronoi(tetgenio* out) if (dest(tetloop) == ptloop) break; } } - if (k < 6) break; // Found this edge. - } - assert(j < tetlist->len()); - // k is the right edge number. - end1 = * (int *) (tetloop.tet + elemmarkerindex); - vfacecount = tetedgeindexarray[end1 * 6 + k]; - if (out == (tetgenio *) NULL) { - fprintf(outfile, " %d", vfacecount + shift); - } else { - vertarray[index++] = vfacecount + shift; + if (k < 6) break; // Found this edge. + } + assert(j < tetlist->len()); + // k is the right edge number. + end1 = * (int *) (tetloop.tet + elemmarkerindex); + vfacecount = tetedgeindexarray[end1 * 6 + k]; + if (out == (tetgenio *) NULL) { + fprintf(outfile, " %d", vfacecount + shift); + } else { + vertarray[index++] = vfacecount + shift; + } + } // for (i = 0; i < ptlist->len(); i++) { + if (out == (tetgenio *) NULL) { + fprintf(outfile, "\n"); + } + vpointcount++; + } + tetlist->clear(); + ptlist->clear(); + ptloop = pointtraverse(); + } + delete tetlist; + delete ptlist; + delete [] tetfaceindexarray; + delete [] tetedgeindexarray; + + if (out == (tetgenio *) NULL) { + fprintf(outfile, "# Generated by %s\n", b->commandline); + fclose(outfile); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// outpbcnodes() Output pbc node pairs to a .pbc file or a structure. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::outpbcnodes(tetgenio* out) +{ + FILE *outfile; + char pbcfilename[FILENAMESIZE]; + list *ptpairlist; + tetgenio::pbcgroup *pgi, *pgo; + pbcdata *pd; + face faceloop; + face checkseg, symseg; + point *ptpair, pa, pb; + enum locateresult loc; + REAL sympt[3], d1, d2; + int *worklist; + int firstindex, shift; + int index, idx; + int i, j, k, l; + + if (out == (tetgenio *) NULL) { + strcpy(pbcfilename, b->outfilename); + strcat(pbcfilename, ".pbc"); + } + + if (!b->quiet) { + if (out == (tetgenio *) NULL) { + printf("Writing %s.\n", pbcfilename); + } else { + printf("Writing pbc nodes.\n"); + } + } + + // Avoid compilation warnings. + outfile = (FILE *) NULL; + pgo = (tetgenio::pbcgroup *) NULL; + index = 0; + + if (out == (tetgenio *) NULL) { + outfile = fopen(pbcfilename, "w"); + if (outfile == (FILE *) NULL) { + printf("File I/O Error: Cannot create file %s.\n", pbcfilename); + terminatetetgen(1); + } + // Number of pbc groups. + fprintf(outfile, "# number of PBCs.\n"); + fprintf(outfile, "%d\n\n", in->numberofpbcgroups); + } else { + out->numberofpbcgroups = in->numberofpbcgroups; + // Allocate memory for 'out->pbcgrouplist'. + out->pbcgrouplist = new tetgenio::pbcgroup[in->numberofpbcgroups]; + // (Next line was a bug, reported by Murry Nigel). + if (out->pbcgrouplist == (tetgenio::pbcgroup *) NULL) { + printf("Error: Out of memory.\n"); + terminatetetgen(1); + } + } + + ptpairlist = new list(2 * sizeof(point *), NULL, 256); + worklist = new int[points->items + 1]; + for (i = 0; i < points->items + 1; i++) worklist[i] = 0; + + // Determine the first index (0 or 1). + firstindex = b->zeroindex ? 0 : in->firstnumber; + shift = 0; // Default no shiftment. + if ((in->firstnumber == 1) && (firstindex == 0)) { + shift = 1; // Shift the output indices by 1. + } + + for (i = 0; i < in->numberofpbcgroups; i++) { + // Group i. + pgi = &(in->pbcgrouplist[i]); + if (out == (tetgenio *) NULL) { + fprintf(outfile, "# PBC %d\n", in->firstnumber + i); + // Output facet markers. + fprintf(outfile, "%d %d\n", pgi->fmark1, pgi->fmark2); + // Output transformation matrix. + fprintf(outfile, "[\n"); + for (j = 0; j < 4; j++) { + fprintf(outfile, " %.12g %.12g %.12g %.12g\n", pgi->transmat[j][0], + pgi->transmat[j][1], pgi->transmat[j][2], pgi->transmat[j][3]); + } + fprintf(outfile, "]\n"); + } else { + pgo = &(out->pbcgrouplist[i]); + // Copy data from pgi to pgo. + pgo->fmark1 = pgi->fmark1; + pgo->fmark2 = pgi->fmark2; + for (j = 0; j < 4; j++) { + for (k = 0; k < 4; k++) pgo->transmat[j][k] = pgi->transmat[j][k]; + } + } + + // Find the point pairs of group i. + subfaces->traversalinit(); + faceloop.sh = shellfacetraverse(subfaces); + while (faceloop.sh != (shellface *) NULL) { + if (shellpbcgroup(faceloop) == i) { + // It is in group i. Operate on it if it has pgi->fmark1. + idx = shellmark(faceloop) - 1; + if (in->facetmarkerlist[idx] == pgi->fmark1) { + // Loop three edges of the subface. + for (j = 0; j < 3; j++) { + sspivot(faceloop, checkseg); + // Loop two vertices of the edge. + for (k = 0; k < 2; k++) { + if (k == 0) pa = sorg(faceloop); + else pa = sdest(faceloop); + if (worklist[pointmark(pa)] == 0) { + pb = (point) NULL; + if (checkseg.sh != dummysh) { + // pa is on a segment. Find pb. + // Find the incident pbcgroup of checkseg. + idx = shellmark(checkseg) - 1; + for (l = idx2segpglist[idx]; l < idx2segpglist[idx + 1]; + l++) { + pd = (pbcdata *)(* segpbcgrouptable)[segpglist[l]]; + if (((pd->fmark[0] == pgi->fmark1) && + (pd->fmark[1] == pgi->fmark2)) || + ((pd->fmark[0] == pgi->fmark2) && + (pd->fmark[1] == pgi->fmark1))) break; + } +#ifdef SELF_CHECK + assert(l < idx2segpglist[idx + 1]); +#endif + loc = getsegpbcsympoint(pa, &checkseg, sympt, &symseg, + segpglist[l]); + if (loc != ONVERTEX) { + // Not found a match point! It may be caused by the + // pair of input vertices don't have enough digits. + // Choose a near vertex. + d1 = distance(sympt, sorg(symseg)); + d2 = distance(sympt, sdest(symseg)); + if (d1 > d2) sesymself(symseg); + } + pb = sorg(symseg); + } else { + // Operate on pa if it is inside the facet. + if (pointtype(pa) == FREESUBVERTEX) { + pb = point2pbcpt(pa); + } + } + if (pb != (point) NULL) { + // Add the pair (pa, pb) into list. + ptpair = (point *) ptpairlist->append(NULL); + ptpair[0] = pa; + ptpair[1] = pb; + // Mark pa (avoid to operate on it later). + worklist[pointmark(pa)] = 1; + } + } + } + // Get the next edge. + senextself(faceloop); + } } - } // for (i = 0; i < ptlist->len(); i++) { + } + faceloop.sh = shellfacetraverse(subfaces); + } + + // Output the list of pbc points. + if (out == (tetgenio *) NULL) { + fprintf(outfile, "%d\n", ptpairlist->len()); + } else { + pgo->numberofpointpairs = ptpairlist->len(); + pgo->pointpairlist = new int[pgo->numberofpointpairs * 2]; + index = 0; + } + for (j = 0; j < ptpairlist->len(); j++) { + ptpair = (point *)(* ptpairlist)[j]; + pa = ptpair[0]; + pb = ptpair[1]; if (out == (tetgenio *) NULL) { - fprintf(outfile, "\n"); + fprintf(outfile, " %4d %4d\n", pointmark(pa) - shift, + pointmark(pb) - shift); + } else { + pgo->pointpairlist[index++] = pointmark(pa) - shift; + pgo->pointpairlist[index++] = pointmark(pb) - shift; } - vpointcount++; + // Unmark pa. + worklist[pointmark(pa)] = 0; } - tetlist->clear(); - ptlist->clear(); - ptloop = pointtraverse(); + if (out == (tetgenio *) NULL) { + fprintf(outfile, "\n"); + } + ptpairlist->clear(); } - delete tetlist; - delete ptlist; - delete [] tetfaceindexarray; - delete [] tetedgeindexarray; + + delete [] worklist; + delete ptpairlist; if (out == (tetgenio *) NULL) { fprintf(outfile, "# Generated by %s\n", b->commandline); @@ -33226,141 +33478,39 @@ void tetgenmesh::outmesh2off(char* ofilename) fclose(outfile); } +// +// End of I/O rouitnes +// + +// +// Begin of user interaction routines +// + /////////////////////////////////////////////////////////////////////////////// // // -// outmesh2vtk() Save mesh to file in VTK Legacy format. // -// // -// This function was contributed by Bryn Llyod from ETH, 2007. // +// internalerror() Ask the user to send me the defective product. Exit. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::outmesh2vtk(char* ofilename) +void tetgenmesh::internalerror() { - FILE *outfile; - char vtkfilename[FILENAMESIZE]; - point pointloop; - tetrahedron* tptr; - double x, y, z; - int n1, n2, n3, n4; - int nnodes = 4; - int celltype = 10; - - int NEL = tetrahedrons->items; - int NN = points->items; - - if (ofilename != (char *) NULL && ofilename[0] != '\0') { - strcpy(vtkfilename, ofilename); - } else if (b->outfilename[0] != '\0') { - strcpy(vtkfilename, b->outfilename); - } else { - strcpy(vtkfilename, "unnamed"); - } - strcat(vtkfilename, ".vtk"); - - if (!b->quiet) { - printf("Writing %s.\n", vtkfilename); - } - outfile = fopen(vtkfilename, "w"); - if (outfile == (FILE *) NULL) { - printf("File I/O Error: Cannot create file %s.\n", vtkfilename); - return; - } - - //always write big endian - //bool ImALittleEndian = !testIsBigEndian(); - - fprintf(outfile, "# vtk DataFile Version 2.0\n"); - fprintf(outfile, "Unstructured Grid\n"); - fprintf(outfile, "ASCII\n"); // BINARY - fprintf(outfile, "DATASET UNSTRUCTURED_GRID\n"); - fprintf(outfile, "POINTS %d double\n", NN); - - points->traversalinit(); - pointloop = pointtraverse(); - for(int id=0; idtraversalinit(); - tptr = tetrahedrontraverse(); - //elementnumber = firstindex; // in->firstnumber; - if (b->order == 2) { - printf(" Write VTK not implemented for order 2 elements \n"); - 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]; - n1 = pointmark(p1) - in->firstnumber; - n2 = pointmark(p2) - in->firstnumber; - n3 = pointmark(p3) - in->firstnumber; - n4 = pointmark(p4) - in->firstnumber; - //if(ImALittleEndian){ - // swapBytes((unsigned char *) &nnodes, sizeof(nnodes)); - // swapBytes((unsigned char *) &n1, sizeof(n1)); - // swapBytes((unsigned char *) &n2, sizeof(n2)); - // swapBytes((unsigned char *) &n3, sizeof(n3)); - // swapBytes((unsigned char *) &n4, sizeof(n4)); - //} - //fwrite((char*)(&nnodes),sizeof(int), 1, outfile); - //fwrite((char*)(&n1),sizeof(int), 1, outfile); - //fwrite((char*)(&n2),sizeof(int), 1, outfile); - //fwrite((char*)(&n3),sizeof(int), 1, outfile); - //fwrite((char*)(&n4),sizeof(int), 1, outfile); - fprintf(outfile, "%d %4d %4d %4d %4d\n", nnodes, n1, n2, n3, n4); - tptr = tetrahedrontraverse(); - } - fprintf(outfile, "\n"); - - fprintf(outfile, "CELL_TYPES %d\n", NEL); - for(int tid=0; tidquiet) { printf(" Checking consistency of the mesh boundary...\n"); @@ -33471,27 +33600,6 @@ int tetgenmesh::checkshells() shloop.shver = 0; stpivot(shloop, oppotet); if (oppotet.tet != dummytet) { - // Check if the tet and the face have the same vertices. - for (i = 0; i < 3; i++) { - pinfect((point) shloop.sh[3 + i]); - } - for (j = 0; j < 3; j++) { - if (org(oppotet) == NULL) break; - if (!pinfected(org(oppotet))) break; - enextself(oppotet); - } - for (i = 0; i < 3; i++) { - puninfect((point) shloop.sh[3 + i]); - } - if (j < 3) { - printf(" !! !! Wrong subface-tet connection.\n"); - printf(" p:draw_subface(%d, %d, %d).\n", pointmark(sorg(shloop)), - pointmark(sdest(shloop)), pointmark(sapex(shloop))); - printf(" p:draw_tet(%d, %d, %d, %d).\n", - pointmark(org(oppotet)), pointmark(dest(oppotet)), - pointmark(apex(oppotet)), pointmark(oppo(oppotet))); - horrors++; - } tspivot(oppotet, testsh); if (testsh.sh != shloop.sh) { printf(" !! !! Wrong tetra-subface connection.\n"); @@ -33572,12 +33680,6 @@ int tetgenmesh::checkshells() } spivot(shloop, testsh); if (testsh.sh != dummysh) { - // Check if the subface is self-bonded. - if (testsh.sh == shloop.sh) { - printf(" !! !! Subface is self-bonded.\n"); - printsh(&shloop); - horrors++; - } segorg = sorg(testsh); segdest = sdest(testsh); same = ((shorg == segorg) && (shdest == segdest)) @@ -33616,29 +33718,9 @@ int tetgenmesh::checkshells() } senextself(shloop); } - if (sinfected(shloop)) { - printf(" !! subface (%d, %d, %d) is infected.\n", - pointmark(sorg(shloop)), pointmark(sdest(shloop)), - pointmark(sapex(shloop))); - horrors++; - } - if (!b->quality) { - // During refinement, subfaces/subsegs rejected to be split were - // marktested. In other cases, they should be not. - if (smarktested(shloop)) { - printf(" !! subface (%d, %d, %d) is marktested.\n", - pointmark(sorg(shloop)), pointmark(sdest(shloop)), - pointmark(sapex(shloop))); - horrors++; - } - } shloop.sh = shellfacetraverse(subfaces); } - if (horrors > 0) { - return horrors; - } - // Run through the list of subsegs, checking each one. subsegs->traversalinit(); segloop.sh = shellfacetraverse(subsegs); @@ -33673,24 +33755,20 @@ int tetgenmesh::checkshells() i = 0; do { spivotself(spin); - if (spin.sh != dummysh) { - shorg = sorg(spin); - shdest = sdest(spin); - same = ((shorg == segorg) && (shdest == segdest)) - || ((shorg == segdest) && (shdest == segorg)); - if (!same) { - printf(" !! !! Wrong subsegment-subface connection.\n"); - printf(" Subsegment : "); - printsh(&segloop); - printf(" Subface : "); - printsh(&testsh); - horrors++; - break; - } - i++; - } else { + shorg = sorg(spin); + shdest = sdest(spin); + same = ((shorg == segorg) && (shdest == segdest)) + || ((shorg == segdest) && (shdest == segorg)); + if (!same) { + printf(" !! !! Wrong subsegment-subface connection.\n"); + printf(" Subsegment : "); + printsh(&segloop); + printf(" Subface : "); + printsh(&testsh); + horrors++; break; } + i++; } while (spin.sh != testsh.sh && i < 1000); if (i >= 1000) { printf(" !! !! Wrong subsegment-subface connection.\n"); @@ -33707,95 +33785,8 @@ int tetgenmesh::checkshells() } else { printf(" !! !! !! !! %d boundary connection viewed with horror.\n", horrors); + return; } - return horrors; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// checksegments() Check the connections between tetrahedra and segments. // -// // -/////////////////////////////////////////////////////////////////////////////// - -int tetgenmesh::checksegments() -{ - triface tetloop, neightet; - face sseg, checkseg; - point pa, pb; - int hitbdry; - int horrors, i; - - if (!b->quiet) { - printf(" Checking tet-seg connections...\n"); - } - - horrors = 0; - tetrahedrons->traversalinit(); - tetloop.tet = tetrahedrontraverse(); - while (tetloop.tet != NULL) { - // Loop the six edges of the tet. - if (tetloop.tet[8] != NULL) { - for (i = 0; i < 6; i++) { - tetloop.loc = edge2locver[i][0]; - tetloop.ver = edge2locver[i][1]; - tsspivot1(tetloop, sseg); - if (sseg.sh != dummysh) { - // Check if they are the same edge. - sseg.shver = 0; - pa = (point) sorg(sseg); - pb = (point) sdest(sseg); - if (!(((org(tetloop) == pa) && (dest(tetloop) == pb)) || - ((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) tetloop.tet, pointmark(org(tetloop)), - pointmark(dest(tetloop)), pointmark(apex(tetloop)), - pointmark(oppo(tetloop)), (unsigned long) sseg.sh, - pointmark(pa), pointmark(pb)); - horrors++; - } else { - // Loop all tets sharing at this edge. - neightet = tetloop; - hitbdry = 0; - do { - tsspivot1(neightet, checkseg); - if (checkseg.sh != sseg.sh) { - printf(" !! Wrong tet-seg connection.\n"); - printf(" Tet: x%lx (%d, %d, %d, %d) - ", - (unsigned long) tetloop.tet, pointmark(org(tetloop)), - pointmark(dest(tetloop)), pointmark(apex(tetloop)), - pointmark(oppo(tetloop))); - if (checkseg.sh != NULL) { - printf("Seg x%lx (%d, %d).\n", (unsigned long) checkseg.sh, - pointmark(sorg(checkseg)), pointmark(sdest(checkseg))); - } else { - printf("Seg: NULL.\n"); - } - horrors++; - } - tfnextself(neightet); - if (neightet.tet == dummytet) { - hitbdry++; - if (hitbdry == 2) break; - esym(tetloop, neightet); - tfnextself(neightet); - if (neightet.tet == dummytet) break; - } - } while (neightet.tet != tetloop.tet); - } - } - } - } - tetloop.tet = tetrahedrontraverse(); - } - - if (horrors == 0) { - printf(" Segments are connected properly.\n"); - } else { - printf(" !! !! !! !! Found %d missing connections.\n", horrors); - } - - return horrors; } /////////////////////////////////////////////////////////////////////////////// @@ -33806,13 +33797,14 @@ int tetgenmesh::checksegments() // // /////////////////////////////////////////////////////////////////////////////// -int tetgenmesh::checkdelaunay(REAL eps, queue* flipqueue) +void tetgenmesh::checkdelaunay(REAL eps, queue* flipqueue) { triface tetraloop; triface oppotet; face opposhelle; point tetorg, tetdest, tetapex, tetoppo; point oppooppo; + enum fliptype fc; REAL sign; int shouldbedelaunay; int horrors; @@ -33856,6 +33848,21 @@ int tetgenmesh::checkdelaunay(REAL eps, queue* flipqueue) if (sign > 0.0) { if (flipqueue) { enqueueflipface(tetraloop, flipqueue); + } else { + printf(" !! Non-locally Delaunay face (%d, %d, %d) ", + pointmark(tetorg), pointmark(tetdest), pointmark(tetapex)); + fc = categorizeface(tetraloop); + switch (fc) { + case T23: printf("\"T23\""); break; + case T32: printf("\"T32\""); break; + case T22: printf("\"T22\""); break; + case T44: printf("\"T44\""); break; + case N32: printf("\"N32\""); break; + case N40: printf("\"N40\""); break; + case FORBIDDENFACE:printf("\"FORBIDDENFACE\""); break; + case FORBIDDENEDGE:printf("\"FORBIDDENEDGE\""); break; + } + printf("\n"); } horrors++; } @@ -33863,7 +33870,6 @@ int tetgenmesh::checkdelaunay(REAL eps, queue* flipqueue) } tetraloop.tet = tetrahedrontraverse(); } - if (flipqueue == (queue *) NULL) { if (horrors == 0) { if (!b->quiet) { @@ -33874,8 +33880,6 @@ int tetgenmesh::checkdelaunay(REAL eps, queue* flipqueue) printf(" !! !! !! !! %d obscenities viewed with horror.\n", horrors); } } - - return horrors; } /////////////////////////////////////////////////////////////////////////////// @@ -33936,61 +33940,50 @@ void tetgenmesh::checkconforming() // // /////////////////////////////////////////////////////////////////////////////// +#ifdef SELF_CHECK + void tetgenmesh::algorithmicstatistics() { + /* printf("Algorithmic statistics:\n\n"); - - printf(" Number of orient3d tests: %ld\n", orient3dcount); - printf(" Number of insphere tests: %ld\n", inspherecount); - printf(" Number of symbolic insphere tests: %ld\n", insphere_sos_count); - printf(" Number of visited tets in point location: %ld\n", ptloc_count); - printf(" Maximal number of tets per point location: %ld\n",ptloc_max_count); - printf(" Number of hull sites: %ld\n", inserthullcount); - printf(" Number of 1-to-4 flips: %ld\n", flip14count); - printf(" Number of 2-to-6 flips: %ld\n", flip26count); - printf(" Number of n-t-2n flips: %ld\n", flipn2ncount); - - if (!b->plc) { - if (1) { - printf(" Number of deleted tets: %ld\n", totaldeadtets); - printf(" Number of created tets: %ld\n", totalbowatcavsize); - printf(" Maximum number of tets per new point: %ld\n", maxbowatcavsize); - // printf(" Number of 3-to-2 flips: %ld\n", flip32count); - } else { - // printf(" Number of 3-to-2 flips: %ld\n", flip32count); - // printf(" Number of 2-to-3 flips: %ld\n", flip23count); - // printf(" Number of n-to-m flips: %ld\n", flipnmcount); - // printf(" Total number of primitive flips: %ld\n", - // flip23count + flip32count); - } + printf(" Point location millisecond: %g\n", (REAL) tloctime * 1e+3); + printf(" Flip millisecond: %g\n", (REAL) tfliptime * 1e+3); + if (b->plc || b->refine) { + printf(" Number of facet above points calculations: %ld\n", abovecount); } - if (b->plc) { - printf(" Number of 2-to-2 flips: %ld\n", flip22count); - // printf(" Number of tri-edge inter (coplanar) tests: %ld (%ld)\n", - // triedgcount, triedgcopcount); - printf(" Number of crossed faces (edges) in scout segs: %ld (%ld)\n", - across_face_count, across_edge_count); - printf(" Maximal number of crossed faces per segment: %ld\n", - across_max_count); - printf(" Number of rule-1 points: %ld\n", r1count); - printf(" Number of rule-2 points: %ld\n", r2count); - printf(" Number of rule-3 points: %ld\n", r3count); - printf(" Maximal size of a missing region: %ld\n", maxregionsize); - printf(" Maximal size of a recovered cavity: %ld\n", maxcavsize); - printf(" Number of non-Delaunay edges: %ld\n", ndelaunayedgecount); - printf(" Number of cavity expansions: %ld\n", cavityexpcount); + printf(" Segment split rules: R1 %ld, R2 %ld, R3 %ld\n", r1count, r2count, + r3count); + } + if (b->quality) { + printf(" Bowyer-Watson insertions: seg %ld, sub %ld, vol %ld.\n", + bowatsegcount, bowatsubcount, bowatvolcount); + printf(" Bowyer-Watson corrections: seg %ld, sub %ld, vol %ld\n", + updsegcount, updsubcount, updvolcount); + printf(" Bowyer-Watson failures: seg %ld, sub %ld, vol %ld\n", + failsegcount, failsubcount, failvolcount); + printf(" Number of repair flips: %ld.\n", repairflipcount); + printf(" Number of circumcenters outside Bowat-cav.: %ld.\n", + outbowatcircumcount); + if (b->conformdel) { + printf(" Segment split rules: R2 %ld, R3 %ld\n", r2count, r3count); + printf(" Number of CDT enforcement points: %ld.\n", cdtenforcesegpts); + } + printf(" Number of Rejections: seg %ld, sub %ld, tet %ld.\n", rejsegpts, + rejsubpts, rejtetpts); + if (b->optlevel) { + printf( + " Optimization flips: f32 %ld, f44 %ld, f56 %ld, f68 %ld, fnm %ld.\n", + optcount[3], optcount[4], optcount[5], optcount[6], optcount[9]); + printf(" Optimization segment deletions: %ld.\n", optcount[1]); + } } - - // printf(" Total point location time (millisec): %g\n", tloctime * 1e+3); - // printf(" Total point insertion time (millisec): %g\n",tinserttime*1e+3); - // if (b->bowyerwatson == 0) { - // printf(" Total flip time (millisec): %g\n", tfliptime * 1e+3); - // } - printf("\n"); + */ } +#endif // #ifdef SELF_CHECK + /////////////////////////////////////////////////////////////////////////////// // // // qualitystatistics() Print statistics about the quality of the mesh. // @@ -34009,7 +34002,6 @@ void tetgenmesh::qualitystatistics() REAL edgelength[6], alldihed[6], faceangle[3]; REAL shortest, longest; REAL smallestvolume, biggestvolume; - REAL smallestratio, biggestratio; REAL smallestdiangle, biggestdiangle; REAL smallestfaangle, biggestfaangle; REAL tetvol, minaltitude; @@ -34059,8 +34051,6 @@ void tetgenmesh::qualitystatistics() longest = 0.0; smallestvolume = minaltitude; biggestvolume = 0.0; - smallestratio = minaltitude; - biggestratio = 0.0; smallestdiangle = smallestfaangle = 180.0; biggestdiangle = biggestfaangle = 0.0; @@ -34068,7 +34058,7 @@ void tetgenmesh::qualitystatistics() tetrahedrons->traversalinit(); tetloop.tet = tetrahedrontraverse(); while (tetloop.tet != (tetrahedron *) NULL) { - + // Get four vertices: p0, p1, p2, p3. for (i = 0; i < 4; i++) p[i] = (point) tetloop.tet[4 + i]; // Set the edge vectors: V[0], ..., V[5] @@ -34249,14 +34239,6 @@ void tetgenmesh::qualitystatistics() tetradius = cirradius / sqrt(shortlen); // tetaspect = sqrt(longlen) / (2.0 * insradius); tetaspect = sqrt(longlen) * minheightinv; - // Remember the largest and smallest aspect ratio.. - if (tetaspect < smallestratio) { - smallestratio = tetaspect; - } - if (tetaspect > biggestratio) { - biggestratio = tetaspect; - } - // Accumulate the corresponding number in the aspect ratio histogram. aspectindex = 0; while ((tetaspect > aspectratiotable[aspectindex]) && (aspectindex < 11)) { aspectindex++; @@ -34279,8 +34261,6 @@ void tetgenmesh::qualitystatistics() smallestvolume, biggestvolume); printf(" Shortest edge: %16.5g | Longest edge: %16.5g\n", shortest, longest); - printf(" Smallest aspect ratio: %9.5g | Largest aspect ratio: %9.5g\n", - smallestratio, biggestratio); sprintf(sbuf, "%.17g", biggestfaangle); if (strlen(sbuf) > 8) { sbuf[8] = '\0'; @@ -34388,31 +34368,179 @@ void tetgenmesh::statistics() printf("\n Mesh points: %ld\n", points->items); printf(" Mesh tetrahedra: %ld\n", tetrahedrons->items); - printf(" Mesh faces: %ld\n", (4l * tetrahedrons->items + hullsize) / 2l); - 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\n", subsegs->items); + printf(" Mesh triangles: %ld\n", (4l*tetrahedrons->items+hullsize)/2l); + } + if (b->plc || b->refine) { + printf(" Mesh subfaces: %ld\n", subfaces->items); + printf(" Mesh subsegments: %ld\n\n", subsegs->items); } else { - printf(" Convex hull faces: %ld\n\n", hullsize); + printf(" Convex hull triangles: %ld\n\n", hullsize); } - if (b->verbose > 0) { - if (b->plc || b->refine) { - qualitystatistics(); - } - // algorithmicstatistics(); + qualitystatistics(); + unsigned long totalmeshbytes; + printf("Memory allocation statistics:\n\n"); + printf(" Maximum number of vertices: %ld\n", points->maxitems); + totalmeshbytes = points->maxitems * points->itembytes; + printf(" Maximum number of tetrahedra: %ld\n", tetrahedrons->maxitems); + totalmeshbytes += tetrahedrons->maxitems * tetrahedrons->itembytes; + if (subfaces != (memorypool *) NULL) { + printf(" Maximum number of subfaces: %ld\n", subfaces->maxitems); + totalmeshbytes += subfaces->maxitems * subfaces->itembytes; + } + if (subsegs != (memorypool *) NULL) { + printf(" Maximum number of segments: %ld\n", subsegs->maxitems); + totalmeshbytes += subsegs->maxitems * subsegs->itembytes; + } + printf(" Approximate heap memory used by the mesh (K bytes): %g\n\n", + (double) totalmeshbytes / 1024.0); +#ifdef SELF_CHECK + algorithmicstatistics(); +#endif + } +} + +// +// End of user interaction routines +// + +// +// Begin of constructor and destructor of tetgenmesh +// + +/////////////////////////////////////////////////////////////////////////////// +// // +// ~tetgenmesh() Deallocte memory occupied by a tetgenmesh object. // +// // +/////////////////////////////////////////////////////////////////////////////// + +tetgenmesh::~tetgenmesh() +{ + bgm = (tetgenmesh *) NULL; + in = (tetgenio *) NULL; + b = (tetgenbehavior *) NULL; + + 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 (dummytetbase != (tetrahedron *) NULL) { + delete [] dummytetbase; + } + if (dummyshbase != (shellface *) NULL) { + delete [] dummyshbase; + } + if (facetabovepointarray != (point *) NULL) { + delete [] facetabovepointarray; + } + if (highordertable != (point *) NULL) { + delete [] highordertable; + } + if (subpbcgrouptable != (pbcdata *) NULL) { + delete [] subpbcgrouptable; + } + if (segpbcgrouptable != (list *) NULL) { + delete segpbcgrouptable; + delete [] idx2segpglist; + delete [] segpglist; } } -//// //// -//// //// -//// report_cxx /////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// // +// tetgenmesh() Initialize a tetgenmesh object. // +// // +/////////////////////////////////////////////////////////////////////////////// + +tetgenmesh::tetgenmesh() +{ + bgm = (tetgenmesh *) NULL; + in = (tetgenio *) NULL; + b = (tetgenbehavior *) NULL; -//// main_cxx ///////////////////////////////////////////////////////////////// -//// //// -//// //// + tetrahedrons = (memorypool *) NULL; + subfaces = (memorypool *) NULL; + subsegs = (memorypool *) NULL; + points = (memorypool *) NULL; + badsubsegs = (memorypool *) NULL; + badsubfaces = (memorypool *) NULL; + badtetrahedrons = (memorypool *) NULL; + flipstackers = (memorypool *) NULL; + + dummytet = (tetrahedron *) NULL; + dummytetbase = (tetrahedron *) NULL; + dummysh = (shellface *) NULL; + dummyshbase = (shellface *) NULL; + + facetabovepointarray = (point *) NULL; + abovepoint = (point) NULL; + highordertable = (point *) NULL; + subpbcgrouptable = (pbcdata *) NULL; + segpbcgrouptable = (list *) NULL; + idx2segpglist = (int *) NULL; + segpglist = (int *) NULL; + + xmax = xmin = ymax = ymin = zmax = zmin = 0.0; + longest = 0.0; + hullsize = 0l; + insegments = 0l; + pointmtrindex = 0; + pointmarkindex = 0; + point2simindex = 0; + point2pbcptindex = 0; + highorderindex = 0; + elemattribindex = 0; + volumeboundindex = 0; + shmarkindex = 0; + areaboundindex = 0; + checksubfaces = 0; + checksubsegs = 0; + checkpbcs = 0; + varconstraint = 0; + nonconvex = 0; + dupverts = 0; + unuverts = 0; + relverts = 0; + suprelverts = 0; + collapverts = 0; + unsupverts = 0; + jettisoninverts = 0; + symbolic = 1; + samples = 0l; + randomseed = 1l; + macheps = 0.0; + minfaceang = minfacetdihed = PI; + maxcavfaces = maxcavverts = 0; + expcavcount = 0; + abovecount = 0l; + bowatvolcount = bowatsubcount = bowatsegcount = 0l; + updvolcount = updsubcount = updsegcount = 0l; + repairflipcount = 0l; + outbowatcircumcount = 0l; + failvolcount = failsubcount = failsegcount = 0l; + r1count = r2count = r3count = 0l; + cdtenforcesegpts = 0l; + rejsegpts = rejsubpts = rejtetpts = 0l; + flip23s = flip32s = flip22s = flip44s = 0l; + tloctime = tfliptime = 0.0; +} + +// +// End of constructor and destructor of tetgenmesh +// + +// +// End of class 'tetgenmesh' implementation. +// /////////////////////////////////////////////////////////////////////////////// // // @@ -34443,7 +34571,7 @@ void tetrahedralize(tetgenbehavior *b, tetgenio *in, tetgenio *out, { tetgenmesh m; // Variables for timing the performance of TetGen (defined in time.h). - clock_t tv[17]; + clock_t tv[14]; tv[0] = clock(); @@ -34466,10 +34594,6 @@ void tetrahedralize(tetgenbehavior *b, tetgenio *in, tetgenio *out, m.reconstructmesh(); } else { m.delaunizevertices(); - if (m.hullsize == 0l) { - printf("The input point set does not span a 3D subspace.\n"); - return; - } } tv[2] = clock(); @@ -34506,10 +34630,22 @@ void tetrahedralize(tetgenbehavior *b, tetgenio *in, tetgenio *out, if (b->useshelles && !b->refine) { m.meshsurface(); - tv[14] = clock(); if (b->diagnose != 1) { - m.markacutevertices(60.0); - m.formskeleton(tv[15]); + m.markacutevertices(89.0); + m.incrperturbvertices(b->epsilon); + m.delaunizesegments(); + if (m.checkpbcs) { + long oldnum; + do { + oldnum = m.points->items; + m.incrperturbvertices(b->epsilon); + if (m.points->items > oldnum) { + oldnum = m.points->items; + m.delaunizesegments(); + } + } while (oldnum < m.points->items); + } + m.constrainedfacets(); } else { m.detectinterfaces(); } @@ -34520,19 +34656,11 @@ void tetrahedralize(tetgenbehavior *b, tetgenio *in, tetgenio *out, if (!b->quiet) { if (b->useshelles && !b->refine) { if (b->diagnose != 1) { - printf("Boundary recovery "); + printf("Segment and facet "); } else { printf("Intersection "); } printf("seconds: %g\n", (tv[4] - tv[3]) / (REAL) CLOCKS_PER_SEC); - /*if ((b->diagnose != 1) && (b->verbose > 0)) { - printf(" Surface mesh seconds: %g\n", - (tv[14] - tv[3]) / (REAL) CLOCKS_PER_SEC); - printf(" Segment recovery seconds: %g\n", - (tv[15] - tv[14]) / (REAL) CLOCKS_PER_SEC); - printf(" Facet recovery seconds: %g\n", - (tv[4] - tv[15]) / (REAL) CLOCKS_PER_SEC); - }*/ } } @@ -34549,7 +34677,7 @@ void tetrahedralize(tetgenbehavior *b, tetgenio *in, tetgenio *out, } if ((b->plc || b->refine) && !(b->diagnose == 1)) { - m.optimizemesh2(false); + m.optimizemesh(false); } tv[6] = clock(); @@ -34561,7 +34689,7 @@ void tetrahedralize(tetgenbehavior *b, tetgenio *in, tetgenio *out, } if ((b->plc && b->nobisect) && !(b->diagnose == 1)) { - m.removesteiners2(); + m.removesteiners(false); } tv[7] = clock(); @@ -34601,9 +34729,9 @@ void tetrahedralize(tetgenbehavior *b, tetgenio *in, tetgenio *out, } } - //if (b->coarse) { - // m.removesteiners2(true); - //} + if (b->coarse) { + m.removesteiners(true); + } tv[10] = clock(); @@ -34628,7 +34756,7 @@ void tetrahedralize(tetgenbehavior *b, tetgenio *in, tetgenio *out, } if (b->quality && (b->optlevel > 0)) { - m.optimizemesh2(true); + m.optimizemesh(true); } tv[12] = clock(); @@ -34669,17 +34797,16 @@ void tetrahedralize(tetgenbehavior *b, tetgenio *in, tetgenio *out, } } else { m.outnodes(out); - if (b->quality && b->metric) { - m.outmetrics(out); + if (b->quality || b->metric) { + // m.outmetrics(out); } } } - if (b->noelewritten == 1) { + if (b->noelewritten) { if (!b->quiet) { printf("NOT writing an .ele file.\n"); } - m.numberedges(); } else { if (!(b->diagnose == 1)) { if (m.tetrahedrons->items > 0l) { @@ -34714,9 +34841,9 @@ void tetrahedralize(tetgenbehavior *b, tetgenio *in, tetgenio *out, } } - //if (m.checkpbcs) { - // m.outpbcnodes(out); - //} + if (m.checkpbcs) { + m.outpbcnodes(out); + } if (b->edgesout) { if (b->edgesout > 1) { @@ -34745,10 +34872,6 @@ void tetrahedralize(tetgenbehavior *b, tetgenio *in, tetgenio *out, m.outmesh2off(b->outfilename); } - if (!out && b->vtkview) { - m.outmesh2vtk(b->outfilename); - } - if (b->neighout) { m.outneighbors(out); } @@ -34772,9 +34895,7 @@ void tetrahedralize(tetgenbehavior *b, tetgenio *in, tetgenio *out, m.checkshells(); } if (b->docheck > 1) { - if (m.checkdelaunay(0.0, NULL) > 0) { - assert(0); - } + m.checkdelaunay(0.0, NULL); if (b->docheck > 2) { if (b->quality || b->refine) { m.checkconforming(); @@ -34824,15 +34945,15 @@ void tetrahedralize(char *switches, tetgenio *in, tetgenio *out, tetgenio in, addin, bgmin; if (!b.parse_commandline(argc, argv)) { - terminatetetgen(0); + terminatetetgen(1); } if (b.refine) { if (!in.load_tetmesh(b.infilename)) { - terminatetetgen(3); + terminatetetgen(1); } } else { if (!in.load_plc(b.infilename, (int) b.object)) { - terminatetetgen(3); + terminatetetgen(1); } } if (b.insertaddpoints) { @@ -34863,8 +34984,3 @@ void tetrahedralize(char *switches, tetgenio *in, tetgenio *out, #endif // not TETLIBRARY } - -//// //// -//// //// -//// main_cxx ///////////////////////////////////////////////////////////////// - diff --git a/src/cpp/tetgen.h b/src/cpp/tetgen.h index d451614ca4b8e8048011c2bbfd18bd61a8457411..47ac53521f710837bde0d4515c840a11f32d9b20 100644 --- a/src/cpp/tetgen.h +++ b/src/cpp/tetgen.h @@ -5,12 +5,12 @@ // A Quality Tetrahedral Mesh Generator and 3D Delaunay Triangulator // // // // Version 1.4 // -// September 6, December 13, 2009 // +// April 16, 2007 // // // -// Copyright (C) 2002--2009 // +// Copyright (C) 2002--2007 // // Hang Si // -// Research Group: Numerical Mathematics and Scientific Computing // -// Weierstrass Institute for Applied Analysis and Stochastics (WIAS) // +// Research Group Numerical Mathematics and Scientific Computing // +// Weierstrass Institute for Applied Analysis and Stochastics // // Mohrenstr. 39, 10117 Berlin, Germany // // si@wias-berlin.de // // // @@ -22,9 +22,10 @@ /////////////////////////////////////////////////////////////////////////////// // // -// TetGen is a library to generate tetrahedral meshes for 3D domains. It's // -// main purpose is to generate suitable tetrahedral meshes for numerical // -// simulations using finite element and finite volume methods. // +// TetGen computes Delaunay tetrahedralizations, constrained Delaunay tetra- // +// hedralizations, and quality Delaunay tetrahedral meshes. The latter are // +// nicely graded and whose tetrahedra have radius-edge ratio bounded. Such // +// meshes are suitable for finite element and finite volume methods. // // // // TetGen incorporates a suit of geometrical and mesh generation algorithms. // // A brief description of algorithms used in TetGen is found in the first // @@ -41,8 +42,8 @@ // national Meshing Roundtable. September 2005. // // // // The mesh refinement algorithm is from: Hang Si, "Adaptive Tetrahedral // -// Mesh Generation by Constrained Delaunay Refinement". International // -// Journal for Numerical Methods in Engineering, 75(7): 856--880, 2008. // +// Mesh Generation by Constrained Delaunay Refinement". WIAS Preprint No. // +// 1176, Berlin 2006. // // // // The mesh data structure of TetGen is a combination of two types of mesh // // data structures. The tetrahedron-based mesh data structure introduced // @@ -78,43 +79,39 @@ // // /////////////////////////////////////////////////////////////////////////////// -#ifndef tetgenH -#define tetgenH +// Here are the most general used head files for C/C++ programs. -#include -#include -#include -#include -#include +#include // Standard IO: FILE, NULL, EOF, printf(), ... +#include // Standard lib: abort(), system(), getenv(), ... +#include // String lib: strcpy(), strcat(), strcmp(), ... +#include // Math lib: sin(), sqrt(), pow(), ... +#include // Defined type clock_t, constant CLOCKS_PER_SEC. #include #include -// 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 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 from MIT for the -// following piece of code.) - -// Define the _MSC_VER symbol if you are using Microsoft Visual C++. - -// #define _MSC_VER - -// Define the _WIN64 symbol if you are running TetGen on Win64. - -// #define _WIN64 +/////////////////////////////////////////////////////////////////////////////// +// // +// TetGen Library Overview // +// // +// TetGen library is comprised by several data types and global functions. // +// // +// There are three main data types: tetgenio, tetgenbehavior, and tetgenmesh.// +// Tetgenio is used to pass data into and out of TetGen library; tetgenbeha- // +// vior keeps the runtime options and thus controls the behaviors of TetGen; // +// tetgenmesh, the biggest data type I've ever defined, contains mesh data // +// structures and mesh traversing and transformation operators. The meshing // +// algorithms are implemented on top of it. These data types are defined as // +// C++ classes. // +// // +// There are few global functions. tetrahedralize() is provided for calling // +// TetGen from another program. Two functions: orient3d() and insphere() are // +// incorporated from a public C code provided by Shewchuk. They performing // +// exact geometrical tests. // +// // +/////////////////////////////////////////////////////////////////////////////// -#ifdef _MSC_VER // Microsoft Visual C++ -# ifdef _WIN64 - typedef __int64 intptr_t; - typedef unsigned __int64 uintptr_t; -# else // not _WIN64 - typedef int intptr_t; - typedef unsigned int uintptr_t; -# endif -#else // not Visual C++ -# include -#endif +#ifndef tetgenH +#define tetgenH // To compile TetGen as a library instead of an executable program, define // the TETLIBRARY symbol. @@ -127,7 +124,7 @@ // #define NDEBUG // To insert lots of self-checks for internal errors, define the SELF_CHECK -// symbol. This will slow down the program a bit. +// symbol. This will slow down the program significantly. // #define SELF_CHECK @@ -148,30 +145,7 @@ /////////////////////////////////////////////////////////////////////////////// // // -// TetGen Library Overview // -// // -// TetGen library is comprised by several data types and global functions. // -// // -// There are three main data types: tetgenio, tetgenbehavior, and tetgenmesh.// -// Tetgenio is used to pass data into and out of TetGen library; tetgenbeha- // -// vior keeps the runtime options and thus controls the behaviors of TetGen; // -// tetgenmesh, the biggest data type I've ever defined, contains mesh data // -// structures and mesh traversing and transformation operators. The meshing // -// algorithms are implemented on top of it. These data types are defined as // -// C++ classes. // -// // -// There are few global functions. tetrahedralize() is provided for calling // -// TetGen from another program. Two functions: orient3d() and insphere() are // -// incorporated from a public C code provided by Shewchuk. They performing // -// exact geometrical tests. // -// // -/////////////////////////////////////////////////////////////////////////////// - -/////////////////////////////////////////////////////////////////////////////// -// // -// Class tetgenio // -// // -// The interface for passing data into and out of the library of TetGen. // +// tetgenio Passing data into and out of the library of TetGen. // // // // The tetgenio data structure is actually a collection of arrays of points, // // facets, tetrahedra, and so forth. The library will read and write these // @@ -192,9 +166,15 @@ // (), you should free() them and set the pointers to NULLs before reaching // // deinitialize(). // // // -// tetgenio ontains routines for reading and writing TetGen's files, i.e., // -// .node, .poly, .smesh, .ele, .face, and .edge files. Both the library of // -// TetGen and TetView use these routines to process input files. // +// In all cases, the first item in an array is stored starting at index [0]. // +// However, that item is item number `firstnumber' which may be '0' or '1'. // +// Be sure to set the 'firstnumber' be '1' if your indices pointing into the // +// pointlist is starting from '1'. Default, it is initialized be '0'. // +// // +// Tetgenio also contains routines for reading and writing TetGen's files as // +// well. Both the library of TetGen and TetView use these routines to parse // +// input files, i.e., .node, .poly, .smesh, .ele, .face, and .edge files. // +// Other routines are provided mainly for debugging purpose. // // // /////////////////////////////////////////////////////////////////////////////// @@ -202,595 +182,428 @@ class tetgenio { public: - // Maximum number of characters in a file name (including the null). - enum {FILENAMESIZE = 1024}; - - // Maxi. numbers of chars in a line read from a file (incl. the null). - enum {INPUTLINESIZE = 1024}; - - // The polygon data structure. A "polygon" describes a simple polygon - // (no holes). It is not necessarily convex. Each polygon contains a - // number of corners (points) and the same number of sides (edges). - // 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. - struct polygon : public boost::noncopyable { - int *vertexlist; - int numberofvertices; - - polygon(); - ~polygon(); - }; - - static void init(polygon* p) { - p->vertexlist = (int *) NULL; - p->numberofvertices = 0; - } - - // The facet data structure. A "facet" describes a facet. Each facet is - // a polygonal region possibly with holes, edges, and points in it. - struct facet { - polygon *polygonlist; - int numberofpolygons; - REAL *holelist; - int numberofholes; - - facet(); - ~facet(); - }; - - static void init(facet* f) { - f->polygonlist = (polygon *) NULL; - f->numberofpolygons = 0; - f->holelist = (REAL *) NULL; - f->numberofholes = 0; - } - - // A 'voroedge' is an edge of the Voronoi diagram. It corresponds to a - // Delaunay face. Each voroedge is either a line segment connecting - // two Voronoi vertices or a ray starting from a Voronoi vertex to an - // "infinite vertex". 'v1' and 'v2' are two indices pointing to the - // list of Voronoi vertices. 'v1' must be non-negative, while 'v2' may - // be -1 if it is a ray, in this case, the unit normal of this ray is - // given in 'vnormal'. - typedef struct { - int v1, v2; - REAL vnormal[3]; - } voroedge; - - // A 'vorofacet' is an facet of the Voronoi diagram. It corresponds to a - // Delaunay edge. Each Voronoi facet is a convex polygon formed by a - // list of Voronoi edges, it may not be closed. 'c1' and 'c2' are two - // indices pointing into the list of Voronoi cells, i.e., the two cells - // share this facet. 'elist' is an array of indices pointing into the - // list of Voronoi edges, 'elist[0]' saves the number of Voronoi edges - // (including rays) of this facet. - typedef struct { - int c1, c2; - 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. - struct pbcgroup { - int fmark1, fmark2; - REAL transmat[4][4]; - int numberofpointpairs; - int *pointpairlist; - - pbcgroup(); - ~pbcgroup(); - }; - - // 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; - - // Dimension of the mesh (2 or 3), default is 3. - int mesh_dim; - - // Does the lines in .node file contain index or not, default is TRUE. - bool useindex; - - // 'pointlist': An array of point coordinates. The first point's x - // coordinate is at index [0] and its y coordinate at index [1], its - // z coordinate is at index [2], followed by the coordinates of the - // remaining points. Each point occupies three REALs. - // 'pointattributelist': An array of point attributes. Each point's - // 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 int per point. - REAL *pointlist; - REAL *pointattributelist; - REAL *pointmtrlist; - int *pointmarkerlist; - 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; - REAL *tetrahedronattributelist; - REAL *tetrahedronvolumelist; - 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. - 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. - 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' - // switch, and each volume constraint is used only if you select the - // `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. - 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. - 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. - int *trifacelist; - int *adjtetlist; - int *trifacemarkerlist; - int numberoftrifaces; - - // `edgelist': An array of edge endpoints. The first edge's endpoints - // are at indices [0] and [1], followed by the remaining edges. Two - // ints per edge. - // `edgemarkerlist': An array of edge markers; one int per edge. - int *edgelist; - int *edgemarkerlist; - int numberofedges; - - // 'vpointlist': An array of Voronoi vertex coordinates (like pointlist). - // 'vedgelist': An array of Voronoi edges. Each entry is a 'voroedge'. - // 'vfacetlist': An array of Voronoi facets. Each entry is a 'vorofacet'. - // 'vcelllist': An array of Voronoi cells. Each entry is an array of - // indices pointing into 'vfacetlist'. The 0th entry is used to store - // the length of this array. - REAL *vpointlist; - voroedge *vedgelist; - vorofacet *vfacetlist; - int **vcelllist; - int numberofvpoints; - int numberofvedges; - int numberofvfacets; - int numberofvcells; - - // A callback function. - TetSizeFunc tetunsuitable; - - // Input & output routines. - bool load_node_call(FILE* infile, int markers, char* nodefilename); - bool load_node(char* filebasename); - bool load_var(char*); - bool load_mtr(char*); - bool load_poly(char*); - bool load_pbc(char*); - bool load_off(char*); - bool load_ply(char*); - bool load_stl(char*); - bool load_medit(char*); - bool load_vtk(char*); - bool load_plc(char*, int); - bool load_tetmesh(char*); - void save_nodes(char*); - void save_elements(char*); - void save_faces(char*); - void save_edges(char*); - void save_neighbors(char*); - void save_poly(char*); - - // Read line and parse string functions. - char *readline(char* string, FILE* infile, int *linenumber); - char *findnextfield(char* string); - char *readnumberline(char* string, FILE* infile, char* infilename); - char *findnextnumber(char* string); - - // Initialize routine. - void initialize() - { - firstnumber = 0; // Default item index is numbered from Zero. - mesh_dim = 3; // Default mesh dimension is 3. - useindex = true; - - pointlist = (REAL *) NULL; - pointattributelist = (REAL *) NULL; - pointmtrlist = (REAL *) NULL; - pointmarkerlist = (int *) NULL; - numberofpoints = 0; - numberofpointattributes = 0; - numberofpointmtrs = 0; - - tetrahedronlist = (int *) NULL; - tetrahedronattributelist = (REAL *) NULL; - tetrahedronvolumelist = (REAL *) NULL; - neighborlist = (int *) NULL; - numberoftetrahedra = 0; - numberofcorners = 4; // Default is 4 nodes per element. - numberoftetrahedronattributes = 0; - - trifacelist = (int *) NULL; - adjtetlist = (int *) NULL; - trifacemarkerlist = (int *) NULL; - numberoftrifaces = 0; - - facetlist = (facet *) NULL; - facetmarkerlist = (int *) NULL; - numberoffacets = 0; - - edgelist = (int *) NULL; - edgemarkerlist = (int *) NULL; - numberofedges = 0; - - holelist = (REAL *) NULL; - numberofholes = 0; - - regionlist = (REAL *) NULL; - numberofregions = 0; - - facetconstraintlist = (REAL *) NULL; - numberoffacetconstraints = 0; - segmentconstraintlist = (REAL *) NULL; - numberofsegmentconstraints = 0; - - pbcgrouplist = (pbcgroup *) NULL; - numberofpbcgroups = 0; - - vpointlist = (REAL *) NULL; - vedgelist = (voroedge *) NULL; - vfacetlist = (vorofacet *) NULL; - vcelllist = (int **) NULL; - numberofvpoints = 0; - numberofvedges = 0; - numberofvfacets = 0; - numberofvcells = 0; - - tetunsuitable = NULL; - } - - // Free the memory allocated in 'tetgenio'. - void deinitialize() - { - facet *f; - polygon *p; - pbcgroup *pg; - int i, j; - - // This routine assumes that the memory was allocated by - // C++ memory allocation operator 'new'. - - if (pointlist != (REAL *) NULL) { - delete [] pointlist; - } - if (pointattributelist != (REAL *) NULL) { - delete [] pointattributelist; - } - if (pointmtrlist != (REAL *) NULL) { - delete [] pointmtrlist; - } - if (pointmarkerlist != (int *) NULL) { - delete [] pointmarkerlist; + // Maximum number of characters in a file name (including the null). + enum {FILENAMESIZE = 1024}; + + // Maxi. numbers of chars in a line read from a file (incl. the null). + enum {INPUTLINESIZE = 1024}; + + // The polygon data structure. A "polygon" is a planar polygon. It can + // be arbitrary shaped (convex or non-convex) and bounded by non- + // crossing segments, i.e., the number of vertices it has indictes the + // same number of edges. + // 'vertexlist' is a list of vertex indices (integers), its length is + // indicated by 'numberofvertices'. The vertex indices are odered in + // either counterclockwise or clockwise way. + struct polygon : public boost::noncopyable { + int *vertexlist; + int numberofvertices; + + polygon(); + ~polygon(); + }; + + static void init(polygon* p) { + p->vertexlist = (int *) NULL; + p->numberofvertices = 0; } - if (tetrahedronlist != (int *) NULL) { - delete [] tetrahedronlist; - } - if (tetrahedronattributelist != (REAL *) NULL) { - delete [] tetrahedronattributelist; - } - if (tetrahedronvolumelist != (REAL *) NULL) { - delete [] tetrahedronvolumelist; - } - if (neighborlist != (int *) NULL) { - delete [] neighborlist; + // The facet data structure. A "facet" is a planar facet. It is used + // to represent a planar straight line graph (PSLG) in two dimension. + // A PSLG contains a list of polygons. It also may conatin holes in it, + // indicated by a list of hole points (their coordinates). + struct facet { + polygon *polygonlist; + int numberofpolygons; + REAL *holelist; + int numberofholes; + + facet(); + ~facet(); + }; + + static void init(facet* f) { + f->polygonlist = (polygon *) NULL; + f->numberofpolygons = 0; + f->holelist = (REAL *) NULL; + f->numberofholes = 0; } - if (trifacelist != (int *) NULL) { - delete [] trifacelist; - } - if (adjtetlist != (int *) NULL) { - delete [] adjtetlist; - } - if (trifacemarkerlist != (int *) NULL) { - delete [] trifacemarkerlist; - } + // A 'voroedge' is an edge of the Voronoi diagram. It corresponds to a + // Delaunay face. Each voroedge is either a line segment connecting + // two Voronoi vertices or a ray starting from a Voronoi vertex to an + // "infinite vertex". 'v1' and 'v2' are two indices pointing to the + // list of Voronoi vertices. 'v1' must be non-negative, while 'v2' may + // be -1 if it is a ray, in this case, the unit normal of this ray is + // given in 'vnormal'. + typedef struct { + int v1, v2; + REAL vnormal[3]; + } voroedge; + + // A 'vorofacet' is an facet of the Voronoi diagram. It corresponds to a + // Delaunay edge. Each Voronoi facet is a convex polygon formed by a + // list of Voronoi edges, it may not be closed. 'c1' and 'c2' are two + // indices pointing into the list of Voronoi cells, i.e., the two cells + // share this facet. 'elist' is an array of indices pointing into the + // list of Voronoi edges, 'elist[0]' saves the number of Voronoi edges + // (including rays) of this facet. + typedef struct { + int c1, c2; + 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. + struct pbcgroup { + int fmark1, fmark2; + REAL transmat[4][4]; + int numberofpointpairs; + int *pointpairlist; + + pbcgroup(); + ~pbcgroup(); + }; - if (edgelist != (int *) NULL) { - delete [] edgelist; - } - if (edgemarkerlist != (int *) NULL) { - delete [] edgemarkerlist; - } + public: - if (facetlist != (facet *) NULL) { - delete [] facetlist; - } - if (facetmarkerlist != (int *) NULL) { - delete [] facetmarkerlist; - } + // Items are numbered starting from 'firstnumber' (0 or 1), default is 0. + int firstnumber; + // Dimension of the mesh (2 or 3), default is 3. + int mesh_dim; + // Does the lines in .node file contain index or not, default is TRUE. + bool useindex; + + // 'pointlist': An array of point coordinates. The first point's x + // coordinate is at index [0] and its y coordinate at index [1], its + // z coordinate is at index [2], followed by the coordinates of the + // remaining points. Each point occupies three REALs. + // 'pointattributelist': An array of point attributes. Each point's + // 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 int per point. + REAL *pointlist; + REAL *pointattributelist; + REAL *pointmtrlist; + int *pointmarkerlist; + 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; + REAL *tetrahedronattributelist; + REAL *tetrahedronvolumelist; + 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. + 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. + REAL *holelist; + int numberofholes; - if (holelist != (REAL *) NULL) { - delete [] holelist; - } - if (regionlist != (REAL *) NULL) { - delete [] regionlist; - } - if (facetconstraintlist != (REAL *) NULL) { - delete [] facetconstraintlist; - } - if (segmentconstraintlist != (REAL *) NULL) { - delete [] segmentconstraintlist; - } - if (pbcgrouplist != (pbcgroup *) NULL) { - delete [] pbcgrouplist; - } - if (vpointlist != (REAL *) NULL) { - delete [] vpointlist; - } - if (vedgelist != (voroedge *) NULL) { - delete [] vedgelist; - } - if (vfacetlist != (vorofacet *) NULL) { - for (i = 0; i < numberofvfacets; i++) { - delete [] vfacetlist[i].elist; - } - delete [] vfacetlist; - } - if (vcelllist != (int **) NULL) { - for (i = 0; i < numberofvcells; i++) { - delete [] vcelllist[i]; - } - delete [] vcelllist; - } - } + // `regionlist': An array of regional attributes and volume constraints. + // The first constraint's x, y and z coordinates are at indices [0], + // [1] and [2], followed by the regional attribute at index [3], foll- + // owed by the maximum volume at index [4]. Five REALs per constraint. + // Note that each regional attribute is used only if you select the `A' + // switch, and each volume constraint is used only if you select the + // `a' switch (with no number following). + REAL *regionlist; + int numberofregions; + + // `facetconstraintlist': An array of facet maximal area constraints. + // Two REALs per constraint. The first one is the facet marker (cast + // it to int), the second is its maximum area bound. + // Note the 'facetconstraintlist' is used only for the 'q' switch. + REAL *facetconstraintlist; + int numberoffacetconstraints; + + // `segmentconstraintlist': An array of segment max. length constraints. + // Three REALs per constraint. The first two are the indices (pointing + // into 'pointlist') of the endpoints of the segment, the third is its + // maximum length bound. + // Note the 'segmentconstraintlist' is used only for the 'q' switch. + 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. + int *trifacelist; + int *adjtetlist; + int *trifacemarkerlist; + int numberoftrifaces; + + // `edgelist': An array of edge endpoints. The first edge's endpoints + // are at indices [0] and [1], followed by the remaining edges. Two + // ints per edge. + // `edgemarkerlist': An array of edge markers; one int per edge. + int *edgelist; + int *edgemarkerlist; + int numberofedges; + + // 'vpointlist': An array of Voronoi vertex coordinates (like pointlist). + // 'vedgelist': An array of Voronoi edges. Each entry is a 'voroedge'. + // 'vfacetlist': An array of Voronoi facets. Each entry is a 'vorofacet'. + // 'vcelllist': An array of Voronoi cells. Each entry is an array of + // indices pointing into 'vfacetlist'. The 0th entry is used to store + // the length of this array. + REAL *vpointlist; + voroedge *vedgelist; + vorofacet *vfacetlist; + int **vcelllist; + int numberofvpoints; + int numberofvedges; + int numberofvfacets; + int numberofvcells; + + public: - // Constructor & destructor. - tetgenio() {initialize();} - ~tetgenio() {deinitialize();} + // Initialize routine. + void initialize(); + void deinitialize(); + + // Input & output routines. + bool load_node_call(FILE* infile, int markers, char* nodefilename); + bool load_node(char* filename); + bool load_pbc(char* filename); + bool load_var(char* filename); + bool load_mtr(char* filename); + bool load_poly(char* filename); + bool load_off(char* filename); + bool load_ply(char* filename); + bool load_stl(char* filename); + bool load_medit(char* filename); + bool load_plc(char* filename, int object); + bool load_tetmesh(char* filename); + bool load_voronoi(char* filename); + void save_nodes(char* filename); + void save_elements(char* filename); + void save_faces(char* filename); + void save_edges(char* filename); + void save_neighbors(char* filename); + void save_poly(char* filename); + + // Read line and parse string functions. + char *readline(char* string, FILE* infile, int *linenumber); + char *findnextfield(char* string); + char *readnumberline(char* string, FILE* infile, char* infilename); + char *findnextnumber(char* string); + + // Constructor and destructor. + tetgenio() {initialize();} + ~tetgenio() {deinitialize();} }; /////////////////////////////////////////////////////////////////////////////// // // -// Class tetgenbehavior // +// tetgenbehavior Parsing command line switches and file names. // // // -// The object holding a collection of options controlling TetGen's behavior. // -// See "command line switches" in User's manual. // +// It includes a list of variables corresponding to the commandline switches // +// for control the behavior of TetGen. These varibales are all initialized // +// to their default values. // // // // 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. // // // +// You don't need to understand this data type. It can be implicitly called // +// by the global function "tetrahedralize()" defined below. The necessary // +// thing you need to know is the meaning of command line switches of TetGen. // +// They are described in the third section of the user's manual. // +// // /////////////////////////////////////////////////////////////////////////////// class tetgenbehavior { public: - // Labels define the objects which are acceptable by TetGen. They are - // recognized by the file extensions. - // - 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); - // - 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 - // (-p or -r) implies the object. - - enum objecttype {NONE, NODES, POLY, OFF, PLY, STL, MEDIT, VTK, MESH}; - - // Variables of command line switches. Each variable corresponds to a - // switch and will be initialized. - - int plc; // '-p' switch, 0. - int quality; // '-q' switch, 0. - int refine; // '-r' switch, 0. - int coarse; // '-R' switch, 0. - int metric; // '-m' switch, 0. - int varvolume; // '-a' switch without number, 0. - int fixedvolume; // '-a' switch with number, 0. - int insertaddpoints; // '-i' switch, 0. - int regionattrib; // '-A' switch, 0. - int conformdel; // '-D' switch, 0. - int diagnose; // '-d' switch, 0. - int zeroindex; // '-z' switch, 0. - int btree; // -u, 1. - int max_btreenode_size; // number after -u, 100. - int optlevel; // number specified after '-s' switch, 3. - int optpasses; // number specified after '-ss' switch, 3. - int order; // element order, specified after '-o' switch, 1. - 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 gidview; // '-G' switch, 0. - int geomview; // '-O' 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 nobisect; // count of how often '-Y' switch is selected, 0. - int noflip; // do not perform flips. '-X' switch. 0. - int nojettison; // do not jettison redundants nodes. '-J' switch. 0. - int steiner; // number after '-S' switch. 0. - int fliprepair; // '-X' switch, 1. - int offcenter; // '-R' switch, 0. - int docheck; // '-C' switch, 0. - int quiet; // '-Q' switch, 0. - int verbose; // count of how often '-V' switch is selected, 0. - int useshelles; // '-p', '-r', '-q', '-d', or '-R' switch, 0. - int maxflipedgelinksize; // The maximum flippable edge link size 10. - REAL minratio; // number after '-q' switch, 2.0. - REAL goodratio; // number calculated from 'minratio', 0.0. - REAL minangle; // minimum angle bound, 20.0. - REAL goodangle; // cosine squared of minangle, 0.0. - REAL maxvolume; // number after '-a' switch, -1.0. - REAL mindihedral; // number after '-qq' switch, 5.0. - REAL maxdihedral; // number after '-qqq' switch, 165.0. - REAL alpha1; // number after '-m' switch, sqrt(2). - REAL alpha2; // number after '-mm' switch, 1.0. - REAL alpha3; // number after '-mmm' switch, 0.6. - REAL epsilon; // number after '-T' switch, 1.0e-8. - REAL epsilon2; // number after '-TT' switch, 1.0e-5. - enum objecttype object; // determined by -p, or -r switch. NONE. - - // Variables used to save command line switches and in/out file names. - char commandline[1024]; - char infilename[1024]; - char outfilename[1024]; - char addinfilename[1024]; - char bgmeshfilename[1024]; - - void syntax(); - void usage(); - - // Command line parse routine. - bool parse_commandline(int argc, char **argv); - bool parse_commandline(char *switches) { - return parse_commandline(0, &switches); - } - - // Initialize all variables. - tetgenbehavior() - { - plc = 0; - quality = 0; - refine = 0; - coarse = 0; - metric = 0; - minratio = 2.0; - goodratio = 0.0; - minangle = 20.0; - goodangle = 0.0; - maxdihedral = 165.0; - mindihedral = 5.0; - varvolume = 0; - fixedvolume = 0; - maxvolume = -1.0; - regionattrib = 0; - insertaddpoints = 0; - diagnose = 0; - offcenter = 0; - conformdel = 0; - alpha1 = sqrt(2.0); - alpha2 = 1.0; - alpha3 = 0.6; - zeroindex = 0; - btree = 1; - max_btreenode_size = 100; - facesout = 0; - edgesout = 0; - neighout = 0; - voroout = 0; - meditview = 0; - gidview = 0; - geomview = 0; - vtkview = 0; - optlevel = 3; - optpasses = 3; - order = 1; - nojettison = 0; - nobound = 0; - nonodewritten = 0; - noelewritten = 0; - nofacewritten = 0; - noiterationnum = 0; - nobisect = 0; - noflip = 0; - steiner = -1; - fliprepair = 1; - nomerge = 0; - docheck = 0; - quiet = 0; - verbose = 0; - useshelles = 0; - maxflipedgelinksize = 10; - epsilon = 1.0e-8; - epsilon2 = 1.0e-5; - object = NONE; - - commandline[0] = '\0'; - infilename[0] = '\0'; - outfilename[0] = '\0'; - addinfilename[0] = '\0'; - bgmeshfilename[0] = '\0'; - } - - ~tetgenbehavior() - { - } + // Labels define the objects which are acceptable by TetGen. They are + // recognized by the file extensions. + // - 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); + // - 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 + // (-p or -r) implies the object. + + enum objecttype {NONE, NODES, POLY, OFF, PLY, STL, MEDIT, MESH}; + + // Variables of command line switches. Each variable corresponds to a + // switch and will be initialized. The meanings of these switches + // are explained in the user's manul. + + int plc; // '-p' switch, 0. + int quality; // '-q' switch, 0. + int refine; // '-r' switch, 0. + int coarse; // '-R' switch, 0. + int metric; // '-m' switch, 0. + int varvolume; // '-a' switch without number, 0. + int fixedvolume; // '-a' switch with number, 0. + int insertaddpoints; // '-i' switch, 0. + int regionattrib; // '-A' switch, 0. + int conformdel; // '-D' switch, 0. + int diagnose; // '-d' switch, 0. + int zeroindex; // '-z' switch, 0. + int optlevel; // number specified after '-s' switch, 3. + int optpasses; // number specified after '-ss' switch, 5. + int order; // element order, specified after '-o' switch, 1. + 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 gidview; // '-G' switch, 0. + int geomview; // '-O' 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 nobisect; // count of how often '-Y' switch is selected, 0. + int noflip; // do not perform flips. '-X' switch. 0. + int nojettison; // do not jettison redundants nodes. '-J' switch. 0. + int steiner; // number after '-S' switch. 0. + int fliprepair; // '-X' switch, 1. + int offcenter; // '-R' switch, 0. + int docheck; // '-C' switch, 0. + int quiet; // '-Q' switch, 0. + int verbose; // count of how often '-V' switch is selected, 0. + int useshelles; // '-p', '-r', '-q', '-d', or '-R' switch, 0. + REAL minratio; // number after '-q' switch, 2.0. + REAL goodratio; // number calculated from 'minratio', 0.0. + REAL minangle; // minimum angle bound, 20.0. + REAL goodangle; // cosine squared of minangle, 0.0. + REAL maxvolume; // number after '-a' switch, -1.0. + REAL mindihedral; // number after '-qq' switch, 5.0. + REAL maxdihedral; // number after '-qqq' switch, 165.0. + REAL alpha1; // number after '-m' switch, sqrt(2). + REAL alpha2; // number after '-mm' switch, 1.0. + REAL alpha3; // number after '-mmm' switch, 0.6. + REAL epsilon; // number after '-T' switch, 1.0e-8. + REAL epsilon2; // number after '-TT' switch, 1.0e-5. + enum objecttype object; // determined by -p, or -r switch. NONE. + + // Variables used to save command line switches and in/out file names. + char commandline[1024]; + char infilename[1024]; + char outfilename[1024]; + char addinfilename[1024]; + char bgmeshfilename[1024]; + + tetgenbehavior(); + ~tetgenbehavior() {} + + void versioninfo(); + void syntax(); + void usage(); + + // Command line parse routine. + bool parse_commandline(int argc, char **argv); + bool parse_commandline(char *switches) { + return parse_commandline(0, &switches); + } }; /////////////////////////////////////////////////////////////////////////////// // // -// Class tetgenmesh // +// 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 correctness of geometric predicates is crucial for the control flow // +// and hence for the correctness and robustness of an implementation of a // +// geometric algorithm. The following routines use arbitrary precision // +// floating-point arithmetic. They are fast and robust. It is provided by J. // +// Schewchuk in public domain (http://www.cs.cmu.edu/~quake/robust.html). // +// The source code are found in a separate file "predicates.cxx". // +// // +/////////////////////////////////////////////////////////////////////////////// + +REAL exactinit(); +void exactdeinit(); +REAL orient3d(REAL *pa, REAL *pb, REAL *pc, REAL *pd); +REAL insphere(REAL *pa, REAL *pb, REAL *pc, REAL *pd, REAL *pe); + +/////////////////////////////////////////////////////////////////////////////// +// // +// The tetgenmesh data type // +// // +// Includes data types and mesh routines for creating tetrahedral meshes and // +// Delaunay tetrahedralizations, mesh input & output, and so on. // // // -// The object to store, generate, and refine a tetrahedral mesh. // +// An object of tetgenmesh can be used to store a triangular or tetrahedral // +// mesh and its settings. TetGen's functions operates on one mesh each time. // +// This type allows reusing of the same function for different meshes. // // // -// It implements the mesh data structures and functions to create and update // -// a tetrahedral mesh according to the specified options. // +// The mesh data structure (tetrahedron-based and triangle-edge data struct- // +// ures) are declared. There are other accessary data type defined as well, // +// for efficient memory management and link list operations, etc. // +// // +// All algorithms TetGen used are implemented in this data type as member // +// functions. References of these algorithms can be found in user's manual. // +// // +// It's not necessary to understand this type. There is a global function // +// "tetrahedralize()" (defined at the end of this file) implicitly creates // +// the object and calls its member functions according to the command line // +// switches you specified. // // // /////////////////////////////////////////////////////////////////////////////// @@ -798,56 +611,72 @@ class tetgenmesh { public: - // Maximum number of characters in a file name (including the null). - enum {FILENAMESIZE = 1024}; - - // For efficiency, a variety of data structures are allocated in bulk. - // The following constants determine how many of each structure is - // allocated at once. - enum {VERPERBLOCK = 4092, SUBPERBLOCK = 4092, ELEPERBLOCK = 8188}; - - // Used for the point location scheme of Mucke, Saias, and Zhu, to - // decide how large a random sample of tetrahedra to inspect. - enum {SAMPLEFACTOR = 11}; - - // Labels that signify two edge rings of a triangle (see Muecke's thesis). - enum {CCW = 0, CW = 1}; - - // 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, NACUTEVERTEX, ACUTEVERTEX, - FREESEGVERTEX, FREESUBVERTEX, FREEVOLVERTEX, DEADVERTEX = -32768}; + // Maximum number of characters in a file name (including the null). + enum {FILENAMESIZE = 1024}; + + // For efficiency, a variety of data structures are allocated in bulk. + // The following constants determine how many of each structure is + // allocated at once. + enum {VERPERBLOCK = 4092, SUBPERBLOCK = 4092, ELEPERBLOCK = 8188}; + + // Used for the point location scheme of Mucke, Saias, and Zhu, to + // decide how large a random sample of tetrahedra to inspect. + enum {SAMPLEFACTOR = 11}; + + // Labels that signify two edge rings of a triangle defined in Muecke's + // triangle-edge data structure, one (CCW) traversing edges in count- + // erclockwise direction and one (CW) in clockwise direction. + enum {CCW = 0, CW = 1}; + + // Labels that signify whether a record consists primarily of pointers + // or of floating-point words. Used to make decisions about data + // alignment. + enum wordtype {POINTER, FLOATINGPOINT}; + + // Labels that signify the type of a vertex. An UNUSEDVERTEX is a vertex + // read from input (.node file or tetgenio structure) or an isolated + // vertex (outside the mesh). It is the default type for a newpoint. + enum verttype {UNUSEDVERTEX, DUPLICATEDVERTEX, NACUTEVERTEX, ACUTEVERTEX, + FREESEGVERTEX, FREESUBVERTEX, FREEVOLVERTEX, DEADVERTEX = -32768}; - // Labels that signify the type of a subface/subsegment. - enum shestype {NSHARP, SHARP}; + // Labels that signify the type of a subface/subsegment. + enum shestype {NSHARP, SHARP}; - // Labels that signify the type of flips can be applied on a face. - enum fliptype {T23, T32, T22, T44, N32, N40, FORBIDDENFACE, FORBIDDENEDGE}; + // Labels that signify the type of flips can be applied on a face. + // A flipable face has the one of the types T23, T32, T22, and T44. + // Types N32, N40 are unflipable. + enum fliptype {T23, T32, T22, T44, N32, N40, FORBIDDENFACE, FORBIDDENEDGE}; - // Labels that signify the result of triangle-triangle intersection test. - enum interresult {DISJOINT, INTERSECT, SHAREVERTEX, SHAREEDGE, SHAREFACE, - TOUCHEDGE, TOUCHFACE, INTERVERT, INTEREDGE, INTERFACE, INTERTET, - TRIEDGEINT, EDGETRIINT, COLLISIONFACE, INTERSUBSEG, INTERSUBFACE, - BELOWHULL2}; + // Labels that signify the result of triangle-triangle intersection test. + // Two triangles are DISJOINT, or adjoint at a vertex SHAREVERTEX, or + // adjoint at an edge SHAREEDGE, or coincident SHAREFACE or INTERSECT. + enum interresult {DISJOINT, SHAREVERTEX, SHAREEDGE, SHAREFACE, INTERSECT}; - // Labels that signify the result of point location. - enum locateresult {INTETRAHEDRON, ONFACE, ONEDGE, ONVERTEX, OUTSIDE, - ENCSEGMENT}; + // Labels that signify the result of point location. The result of a + // search indicates that the point falls inside a tetrahedron, inside + // a triangle, on an edge, on a vertex, or outside the mesh. + enum locateresult {INTETRAHEDRON, ONFACE, ONEDGE, ONVERTEX, OUTSIDE}; - // Labels that signify the result of vertex insertion. - enum insertsiteresult {SUCCESSINTET, SUCCESSONFACE, SUCCESSONEDGE, - DUPLICATEPOINT, OUTSIDEPOINT}; + // Labels that signify the result of vertex insertion. The result + // indicates that the vertex was inserted with complete success, was + // inserted but encroaches upon a subsegment, was not inserted because + // it lies on a segment, or was not inserted because another vertex + // occupies the same location. + enum insertsiteresult {SUCCESSINTET, SUCCESSONFACE, SUCCESSONEDGE, + DUPLICATEPOINT, OUTSIDEPOINT}; - // Labels that signify the result of direction finding. - enum finddirectionresult {ACROSSEDGE, ACROSSFACE, LEFTCOLLINEAR, - RIGHTCOLLINEAR, TOPCOLLINEAR, BELOWHULL}; + // Labels that signify the result of direction finding. The result + // indicates that a segment connecting the two query points accross + // an edge of the direction triangle/tetrahedron, across a face of + // the direction tetrahedron, along the left edge of the direction + // triangle/tetrahedron, along the right edge of the direction + // triangle/tetrahedron, or along the top edge of the tetrahedron. + enum finddirectionresult {ACROSSEDGE, ACROSSFACE, LEFTCOLLINEAR, + RIGHTCOLLINEAR, TOPCOLLINEAR, BELOWHULL}; /////////////////////////////////////////////////////////////////////////////// // // -// Mesh elements // +// The basic mesh element data structures // // // // There are four types of mesh elements: tetrahedra, subfaces, subsegments, // // and points, where subfaces and subsegments are triangles and edges which // @@ -865,7 +694,8 @@ class tetgenmesh { // on the selected switches), it may contain an arbitrary number of user- // // defined floating-point attributes, an optional maximum volume constraint // // (for -a switch), and a pointer to a list of high-order nodes (-o2 switch).// -// Since the size of a tetrahedron is not determined until running time. // +// Since the size of a tetrahedron is not determined until running time, it // +// is not simply declared as a structure. // // // // The data structure of tetrahedron also stores the geometrical information.// // Let t be a tetrahedron, v0, v1, v2, and v3 be the 4 nodes corresponding // @@ -915,7 +745,7 @@ class tetgenmesh { /////////////////////////////////////////////////////////////////////////////// // // -// Subface-subface and subface-subsegment connections // +// The subface-subface and subface-subsegment connections // // // // Adjoining subfaces sharing a common edge are connected in such a way that // // they form a face ring around the edge. It is indeed a single linked list // @@ -952,46 +782,46 @@ class tetgenmesh { // // /////////////////////////////////////////////////////////////////////////////// - // The tetrahedron data structure. Fields of a tetrahedron contains: - // - a list of four adjoining tetrahedra; - // - a list of four vertices; - // - a list of four subfaces (optional, used for -p switch); - // - a list of user-defined floating-point attributes (optional); - // - a volume constraint (optional, used for -a switch); - // - an integer of element marker (optional, used for -n switch); - // - a pointer to a list of high-ordered nodes (optional, -o2 switch); - - typedef REAL **tetrahedron; - - // The shellface data structure. Fields of a shellface contains: - // - a list of three adjoining subfaces; - // - a list of three vertices; - // - a list of two adjoining tetrahedra; - // - a list of three adjoining subsegments; - // - a pointer to a badface containing it (used for -q); - // - an area constraint (optional, used for -q); - // - an integer for boundary marker; - // - an integer for type: SHARPSEGMENT, NONSHARPSEGMENT, ...; - // - an integer for pbc group (optional, if in->pbcgrouplist exists); - - typedef REAL **shellface; - - // The point data structure. It is actually an array of REALs: - // - x, y and z coordinates; - // - a list of user-defined point attributes (optional); - // - a list of REALs of a user-defined metric tensor (optional); - // - a pointer to a simplex (tet, tri, edge, or vertex); - // - a pointer to a parent (or duplicate) point; - // - a pointer to a tet in background mesh (optional); - // - a pointer to another pbc point (optional); - // - an integer for boundary marker; - // - an integer for verttype: INPUTVERTEX, FREEVERTEX, ...; - - typedef REAL *point; + // The tetrahedron data structure. Fields of a tetrahedron contains: + // - a list of four adjoining tetrahedra; + // - a list of four vertices; + // - a list of four subfaces (optional, used for -p switch); + // - a list of user-defined floating-point attributes (optional); + // - a volume constraint (optional, used for -a switch); + // - an integer of element marker (optional, used for -n switch); + // - a pointer to a list of high-ordered nodes (optional, -o2 switch); + + typedef REAL **tetrahedron; + + // The shellface data structure. Fields of a shellface contains: + // - a list of three adjoining subfaces; + // - a list of three vertices; + // - a list of two adjoining tetrahedra; + // - a list of three adjoining subsegments; + // - a pointer to a badface containing it (used for -q); + // - an area constraint (optional, used for -q); + // - an integer for boundary marker; + // - an integer for type: SHARPSEGMENT, NONSHARPSEGMENT, ...; + // - an integer for pbc group (optional, if in->pbcgrouplist exists); + + typedef REAL **shellface; + + // The point data structure. It is actually an array of REALs: + // - x, y and z coordinates; + // - a list of user-defined point attributes (optional); + // - a list of REALs of a user-defined metric tensor (optional); + // - a pointer to a simplex (tet, tri, edge, or vertex); + // - a pointer to a parent (or duplicate) point; + // - a pointer to a tet in background mesh (optional); + // - a pointer to another pbc point (optional); + // - an integer for boundary marker; + // - an integer for verttype: INPUTVERTEX, FREEVERTEX, ...; + + typedef REAL *point; /////////////////////////////////////////////////////////////////////////////// // // -// Mesh handles // +// The mesh handle (triface, face) data types // // // // Two special data types, 'triface' and 'face' are defined for maintaining // // and updating meshes. They are like pointers (or handles), which allow you // @@ -1029,45 +859,45 @@ class tetgenmesh { // // /////////////////////////////////////////////////////////////////////////////// - class triface { + class triface { - public: + public: - tetrahedron* tet; - int loc, ver; + tetrahedron* tet; + int loc, ver; - // Constructors; - triface() : tet(0), loc(0), ver(0) {} - // Operators; - triface& operator=(const triface& t) { - tet = t.tet; loc = t.loc; ver = t.ver; - return *this; - } - bool operator==(triface& t) { - return tet == t.tet && loc == t.loc && ver == t.ver; - } - bool operator!=(triface& t) { - return tet != t.tet || loc != t.loc || ver != t.ver; - } - }; + // Constructors; + triface() : tet(0), loc(0), ver(0) {} + // Operators; + triface& operator=(const triface& t) { + tet = t.tet; loc = t.loc; ver = t.ver; + return *this; + } + bool operator==(triface& t) { + return tet == t.tet && loc == t.loc && ver == t.ver; + } + bool operator!=(triface& t) { + return tet != t.tet || loc != t.loc || ver != t.ver; + } + }; - class face { + class face { - public: + public: - shellface *sh; - int shver; + shellface *sh; + int shver; - // Constructors; - face() : sh(0), shver(0) {} - // Operators; - face& operator=(const face& s) { - sh = s.sh; shver = s.shver; - return *this; - } - bool operator==(face& s) {return (sh == s.sh) && (shver == s.shver);} - bool operator!=(face& s) {return (sh != s.sh) || (shver != s.shver);} - }; + // Constructors; + face() : sh(0), shver(0) {} + // Operators; + face& operator=(const face& s) { + sh = s.sh; shver = s.shver; + return *this; + } + bool operator==(face& s) {return (sh == s.sh) && (shver == s.shver);} + bool operator!=(face& s) {return (sh != s.sh) || (shver != s.shver);} + }; /////////////////////////////////////////////////////////////////////////////// // // @@ -1093,40 +923,15 @@ class tetgenmesh { // // /////////////////////////////////////////////////////////////////////////////// - struct badface { - triface tt; - face ss; - REAL key; - REAL cent[3]; - point forg, fdest, fapex, foppo; - point noppo; - struct badface *previtem, *nextitem; - }; - -/////////////////////////////////////////////////////////////////////////////// -// // -// Elementary flip data structure // -// // -// A data structure to record three types of elementary flips, which are // -// 2-to-3, 3-to-2, and 2-to-2 flips. // -// // -/////////////////////////////////////////////////////////////////////////////// - - class elemflip { - - public: - - enum fliptype ft; // ft \in {T23, T32, T22}. - point pset1[3]; - point pset2[3]; - - elemflip() { - ft = T23; // Default. - pset1[0] = pset1[1] = pset1[2] = (point) NULL; - pset2[0] = pset2[1] = pset2[2] = (point) NULL; - } - - }; + struct badface { + triface tt; + face ss; + REAL key; + REAL cent[3]; + point forg, fdest, fapex, foppo; + point noppo; + struct badface *previtem, *nextitem; + }; /////////////////////////////////////////////////////////////////////////////// // // @@ -1143,339 +948,115 @@ class tetgenmesh { // // /////////////////////////////////////////////////////////////////////////////// - struct pbcdata { - int fmark[2]; - int segid[2]; - face ss[2]; - REAL transmat[2][4][4]; - }; - -/////////////////////////////////////////////////////////////////////////////// -// // -// Fast lookup tables for mesh manipulation primitives. // -// // -// Mesh manipulation primitives (given below) are basic operations on mesh // -// data structures. They answer basic queries on mesh handles, such as "what // -// is the origin (or destination, or apex) of the face?", "what is the next // -// (or previous) edge in the edge ring?", and "what is the next face in the // -// face ring?", and so on. // -// // -// The implementation of teste basic queries can take advangtage of the fact // -// that the mesh data structures additionally store geometric informations. // -// For example, we have ordered the 4 vertices (from 0 to 3) and the 4 faces // -// (from 0 to 3) of a tetrahedron, and for each face of the tetrahedron, a // -// sequence of vertices has stipulated, therefore the origin of any face of // -// the tetrahedron can be quickly determined by a table 'locver2org', which // -// takes the index of the face and the edge version as inputs. A list of // -// fast lookup tables are defined below. They're just like global variables. // -// These tables are initialized at the runtime. // -// // -/////////////////////////////////////////////////////////////////////////////// - - // For enext() primitive, uses 'ver' as the index. - static int ve[6]; - - // For org(), dest() and apex() primitives, uses 'ver' as the index. - static int vo[6], vd[6], va[6]; - - // For org(), dest() and apex() primitives, uses 'loc' as the first - // index and 'ver' as the second index. - static int locver2org[4][6]; - static int locver2dest[4][6]; - static int locver2apex[4][6]; - - // For oppo() primitives, uses 'loc' as the index. - static int loc2oppo[4]; - - // For fnext() primitives, uses 'loc' as the first index and 'ver' as - // the second index, returns an array containing a new 'loc' and a - // new 'ver'. Note: Only valid for 'ver' equals one of {0, 2, 4}. - static int locver2nextf[4][6][2]; - - // The edge number (from 0 to 5) of a tet is defined as follows: - static int locver2edge[4][6]; - static int edge2locver[6][2]; - - // The map from a given face ('loc') to the other three faces in the tet. - // and the map from a given face's edge ('loc', 'ver') to other two - // faces in the tet opposite to this edge. (used in speeding the Bowyer- - // Watson cavity construction). - static int locpivot[4][3]; - static int locverpivot[4][6][2]; - - // For enumerating three edges of a triangle. - static int plus1mod3[3]; - static int minus1mod3[3]; - -/////////////////////////////////////////////////////////////////////////////// -// // -// 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. // -// // -/////////////////////////////////////////////////////////////////////////////// - - // Primitives for tetrahedra. - inline void decode(tetrahedron ptr, triface& t); - inline tetrahedron encode(triface& t); - inline void sym(triface& t1, triface& t2); - inline void symself(triface& t); - inline void bond(triface& t1, triface& t2); - inline void dissolve(triface& t); - inline point org(triface& t); - inline point dest(triface& t); - inline point apex(triface& t); - inline point oppo(triface& t); - inline void setorg(triface& t, point pointptr); - inline void setdest(triface& t, point pointptr); - inline void setapex(triface& t, point pointptr); - inline void setoppo(triface& t, point pointptr); - inline void esym(triface& t1, triface& t2); - inline void esymself(triface& t); - inline void enext(triface& t1, triface& t2); - inline void enextself(triface& t); - inline void enext2(triface& t1, triface& t2); - inline void enext2self(triface& t); - inline bool fnext(triface& t1, triface& t2); - inline bool fnextself(triface& t); - inline void symedge(triface& t1, triface& t2); - inline void symedgeself(triface& t); - inline void tfnext(triface& t1, triface& t2); - inline void tfnextself(triface& t); - inline void enextfnext(triface& t1, triface& t2); - inline void enextfnextself(triface& t); - inline void enext2fnext(triface& t1, triface& t2); - inline void enext2fnextself(triface& t); - inline REAL elemattribute(tetrahedron* ptr, int attnum); - inline void setelemattribute(tetrahedron* ptr, int attnum, REAL value); - inline REAL volumebound(tetrahedron* ptr); - inline void setvolumebound(tetrahedron* ptr, REAL value); - inline int getelemmarker(tetrahedron* ptr); - inline void setelemmarker(tetrahedron* ptr, int value); - inline void infect(triface& t); - inline void uninfect(triface& t); - inline bool infected(triface& t); - inline void marktest(triface& t); - inline void unmarktest(triface& t); - inline bool marktested(triface& t); - inline void markface(triface& t); - inline void unmarkface(triface& t); - inline bool facemarked(triface& t); - inline void markedge(triface& t); - inline void unmarkedge(triface& t); - inline bool edgemarked(triface& t); - - // Primitives for subfaces and subsegments. - inline void sdecode(shellface sptr, face& s); - inline shellface sencode(face& s); - inline void spivot(face& s1, face& s2); - inline void spivotself(face& s); - inline void sbond(face& s1, face& s2); - inline void sbond1(face& s1, face& s2); - inline void sdissolve(face& s); - inline point sorg(face& s); - inline point sdest(face& s); - inline point sapex(face& s); - inline void setsorg(face& s, point pointptr); - inline void setsdest(face& s, point pointptr); - inline void setsapex(face& s, point pointptr); - inline void sesym(face& s1, face& s2); - inline void sesymself(face& s); - inline void senext(face& s1, face& s2); - inline void senextself(face& s); - inline void senext2(face& s1, face& s2); - inline void senext2self(face& s); - inline void sfnext(face&, face&); - inline void sfnextself(face&); - inline badface* shell2badface(face& s); - inline void setshell2badface(face& s, badface* value); - inline REAL areabound(face& s); - inline void setareabound(face& s, REAL value); - inline int shellmark(face& s); - 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); - - // Primitives for interacting tetrahedra and subfaces. - inline void tspivot(triface& t, face& s); - inline void stpivot(face& s, triface& t); - inline void tsbond(triface& t, face& s); - inline void tsdissolve(triface& t); - inline void stdissolve(face& s); - - // Primitives for interacting subfaces and subsegs. - inline void sspivot(face& s, face& edge); - inline void ssbond(face& s, face& edge); - inline void ssdissolve(face& s); - - inline void tsspivot1(triface& t, face& seg); - inline void tssbond1(triface& t, face& seg); - inline void tssdissolve1(triface& t); - - // Primitives for points. - inline int pointmark(point pt); - inline void setpointmark(point pt, int value); - inline enum verttype pointtype(point pt); - inline void setpointtype(point pt, enum verttype value); - inline void pinfect(point pt); - inline void puninfect(point pt); - inline bool pinfected(point pt); - inline tetrahedron point2tet(point pt); - inline void setpoint2tet(point pt, tetrahedron value); - inline shellface point2sh(point pt); - inline void setpoint2sh(point pt, shellface value); - inline shellface point2seg(point pt); - inline void setpoint2seg(point pt, shellface value); - inline point point2ppt(point pt); - 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 adjustedgering(triface& t, int direction); - inline void adjustedgering(face& s, int direction); - inline bool isdead(triface* t); - inline bool isdead(face* s); - inline bool isfacehaspoint(triface* t, point testpoint); - inline bool isfacehaspoint(face* t, point testpoint); - inline bool isfacehasedge(face* s, point tend1, point tend2); - inline bool issymexist(triface* t); - void getnextsface(face*, face*); - void tsspivot(triface*, face*); - void sstpivot(face*, triface*); - void point2tetorg(point, triface&); - void point2shorg(point, face&); - void point2segorg(point, face&); - bool findorg(triface* t, point dorg); - bool findorg(face* s, point dorg); - void findedge(triface* t, point eorg, point edest); - void findedge(face* s, point eorg, point edest); - void getonextseg(face* s, face* lseg); - void getseghasorg(face* sseg, point dorg); - point getsubsegfarorg(face* sseg); - point getsubsegfardest(face* sseg); - void printtet(triface*); - void printsh(face*); + struct pbcdata { + int fmark[2]; + int segid[2]; + face ss[2]; + REAL transmat[2][4][4]; + }; /////////////////////////////////////////////////////////////////////////////// // // -// Arraypool // +// The list, link and queue data structures // // // -// 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. // +// These data types are used to manipulate a set of (same-typed) data items. // +// For a given set S = {a, b, c, ...}, a list stores the elements of S in a // +// piece of continuous memory. It allows quickly accessing each element of S,// +// thus is suitable for storing a fix-sized set. While a link stores its // +// elements incontinuously. It allows quickly inserting or deleting an item, // +// thus is suitable for storing a size-variable set. A queue is basically a // +// special case of a link where one data element joins the link at the end // +// and leaves in an ordered fashion at the other end. // // // /////////////////////////////////////////////////////////////////////////////// - class arraypool { - - public: + // The compfunc data type. "compfunc" is a pointer to a linear-order + // function, which takes two 'void*' arguments and returning an 'int'. + // + // A function: int cmp(const T &, const T &), is said to realize a + // linear order on the type T if there is a linear order <= on T such + // that for all x and y in T satisfy the following relation: + // -1 if x < y. + // comp(x, y) = 0 if x is equivalent to y. + // +1 if x > y. + typedef int (*compfunc) (const void *, const void *); - int objectbytes; - int objectsperblock; - int log2objectsperblock; - int toparraylen; - char **toparray; - long objects; - unsigned long totalmemory; + // The predefined compare functions for primitive data types. They + // take two pointers of the corresponding date type, perform the + // comparation, and return -1, 0 or 1 indicating the default linear + // order of them. + static int compare_2_ints(const void* x, const void* y); + static int compare_2_longs(const void* x, const void* y); + static int compare_2_unsignedlongs(const void* x, const void* y); - 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) - - -// A function: int cmp(const T &, const T &), is said to realize a -// linear order on the type T if there is a linear order <= on T such -// that for all x and y in T satisfy the following relation: -// -1 if x < y. -// comp(x, y) = 0 if x is equivalent to y. -// +1 if x > y. -// A 'compfunc' is a pointer to a linear-order function. - - typedef int (*compfunc) (const void *, const void *); + // The function used to determine the size of primitive data types and + // set the corresponding predefined linear order functions for them. + static void set_compfunc(char* str, int* itembytes, compfunc* pcomp); /////////////////////////////////////////////////////////////////////////////// // // -// List // +// List data structure. // // // -// An array of items with automatically reallocation of memory. // +// A 'list' is an array of items with automatically reallocation of memory. // +// It behaves like an array. // // // -// 'base' is the starting address of the array. 'itembytes' is the size of // -// each item in byte. // +// 'base' is the starting address of the array; The memory unit in list is // +// byte, i.e., sizeof(char). 'itembytes' is the size of each item in byte, // +// so that the next item in list will be found at the next 'itembytes' // +// counted from the current position. // // // // 'items' is the number of items stored in list. 'maxitems' indicates how // // many items can be stored in this list. 'expandsize' is the increasing // // size (items) when the list is full. // // // +// 'comp' is a pointer pointing to a linear order function for the list. // +// default it is set to 'NULL'. // +// // // The index of list always starts from zero, i.e., for a list L contains // // n elements, the first element is L[0], and the last element is L[n-1]. // +// This feature lets lists like C/C++ arrays. // // // /////////////////////////////////////////////////////////////////////////////// - class list { + class list { - public: + public: - char *base; - int itembytes; - int items, maxitems, expandsize; - compfunc comp; + char *base; + int itembytes; + int items, maxitems, expandsize; + compfunc comp; - list(int itbytes, compfunc pcomp, int mitems = 256, int exsize = 128) { - listinit(itbytes, pcomp, mitems, exsize); - } - ~list() { free(base); } + public: - void *operator[](int i) { return (void *) (base + i * itembytes); } + list(int itbytes, compfunc pcomp, int mitems = 256, int exsize = 128) { + listinit(itbytes, pcomp, mitems, exsize); + } + list(char* str, int mitems = 256, int exsize = 128) { + set_compfunc(str, &itembytes, &comp); + listinit(itembytes, comp, mitems, exsize); + } + ~list() { free(base); } - void listinit(int itbytes, compfunc pcomp, int mitems, int exsize); - void setcomp(compfunc compf) { comp = compf; } - void clear() { items = 0; } - int len() { return items; } - void *append(void* appitem); - void *insert(int pos, void* insitem); - void del(int pos, int order); - int hasitem(void* checkitem); - }; + void *operator[](int i) { return (void *) (base + i * itembytes); } + + void listinit(int itbytes, compfunc pcomp, int mitems, int exsize); + void setcomp(compfunc compf) { comp = compf; } + void clear() { items = 0; } + int len() { return items; } + void *append(void* appitem); + void *insert(int pos, void* insitem); + void del(int pos, int order); + int hasitem(void* checkitem); + void sort(); + }; /////////////////////////////////////////////////////////////////////////////// // // -// Memorypool // +// Memorypool data structure. // // // -// A type used to allocate memory. // +// A type used to allocate memory. (It is incorporated from Shewchuk's // +// Triangle program) // // // // firstblock is the first block of items. nowblock is the block from which // // items are currently being allocated. nextitem points to the next slab // @@ -1502,798 +1083,826 @@ class tetgenmesh { // // /////////////////////////////////////////////////////////////////////////////// - class memorypool { - - public: - - void **firstblock, **nowblock; - void *nextitem; - void *deaditemstack; - void **pathblock; - void *pathitem; - wordtype itemwordtype; - int alignbytes; - int itembytes, itemwords; - int itemsperblock; - long items, maxitems; - int unallocateditems; - int pathitemsleft; - - memorypool(); - memorypool(int, int, enum wordtype, int); - ~memorypool(); + class memorypool { + + public: + + 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; + + public: + + 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(); - }; + void poolinit(int, int, enum wordtype, int); + void restart(); + void *alloc(); + void dealloc(void*); + void traversalinit(); + void *traverse(); + }; /////////////////////////////////////////////////////////////////////////////// // // -// Queue // +// Link data structure. // +// // +// A 'link' is a double linked nodes. It uses the memorypool data structure // +// for memory management. Following is an image of a link. // +// // +// head-> ____0____ ____1____ ____2____ _________<-tail // +// |__next___|--> |__next___|--> |__next___|--> |__NULL___| // +// |__NULL___|<-- |__prev___|<-- |__prev___|<-- |__prev___| // +// | | |_ _| |_ _| | | // +// | | |_ Data1 _| |_ Data2 _| | | // +// |_________| |_________| |_________| |_________| // +// // +// The unit size for storage is size of pointer, which may be 4-byte (in 32- // +// bit machine) or 8-byte (in 64-bit machine). The real size of an item is // +// stored in 'linkitembytes'. // // // -// A 'queue' is a FIFO data structure. // +// 'head' and 'tail' are pointers pointing to the first and last nodes. They // +// do not conatin data (See above). // // // -/////////////////////////////////////////////////////////////////////////////// - - class queue : public memorypool { - - public: - - void **head, **tail; - int linkitembytes; - int linkitems; // Not count 'head' and 'tail'. - - queue(int bytecount, int itemcount = 256) { - linkitembytes = bytecount; - poolinit(bytecount + sizeof(void *), itemcount, POINTER, 0); - head = (void **) alloc(); - tail = (void **) alloc(); - *head = (void *) tail; - *tail = NULL; - linkitems = 0; - } - - void clear() { - // Reset the pool. - restart(); - // Initialize all variables. - head = (void **) alloc(); - tail = (void **) alloc(); - *head = (void *) tail; - *tail = NULL; - linkitems = 0; - } - - long len() { return linkitems; } - bool empty() { return linkitems == 0; } - - void *push(void* newitem) { - void **newnode = tail; - if (newitem != (void *) NULL) { - memcpy((void *)(newnode + 1), newitem, linkitembytes); - } - tail = (void **) alloc(); - *tail = NULL; - *newnode = (void *) tail; - linkitems++; - return (void *)(newnode + 1); - } - - void *pop() { - if (linkitems > 0) { - void **deadnode = (void **) *head; - *head = *deadnode; - dealloc((void *) deadnode); - linkitems--; - return (void *)(deadnode + 1); - } else { - return NULL; - } - } - }; - -/////////////////////////////////////////////////////////////////////////////// +// 'nextlinkitem' is a pointer pointing to a node which is the next one will // +// be traversed. 'curpos' remembers the position (1-based) of the current // +// traversing node. // // // -// Memory managment routines // +// 'linkitems' indicates how many items in link. Note it is different with // +// 'items' of memorypool. // // // -/////////////////////////////////////////////////////////////////////////////// - - void dummyinit(int, int); - void initializepools(); - void tetrahedrondealloc(tetrahedron*); - tetrahedron *tetrahedrontraverse(); - 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*); - - void makepoint2tetmap(); - void makepoint2segmap(); - void makeindex2pointmap(point*&); - void makesegmentmap(int*&, shellface**&); - void makesubfacemap(int*&, shellface**&); - void maketetrahedronmap(int*&, tetrahedron**&); +// The index of link starts from 1, i.e., for a link K contains n elements, // +// the first element of the link is K[1], and the last element is K[n]. // +// See the above figure. // +// // +/////////////////////////////////////////////////////////////////////////////// + + class link : public memorypool { + + public: + + void **head, **tail; + void *nextlinkitem; + int linkitembytes; + int linkitems; + int curpos; + compfunc comp; + + public: + + link(int _itembytes, compfunc _comp, int itemcount) { + linkinit(_itembytes, _comp, itemcount); + } + link(char* str, int itemcount) { + set_compfunc(str, &linkitembytes, &comp); + linkinit(linkitembytes, comp, itemcount); + } + + void linkinit(int _itembytes, compfunc _comp, int itemcount); + void setcomp(compfunc compf) { comp = compf; } + void rewind() { nextlinkitem = *head; curpos = 1; } + void goend() { nextlinkitem = *(tail + 1); curpos = linkitems; } + long len() { return linkitems; } + void clear(); + bool move(int numberofnodes); + bool locate(int pos); + void *add(void* newitem); + void *insert(int pos, void* insitem); + void *deletenode(void** delnode); + void *del(int pos); + void *getitem(); + void *getnitem(int pos); + int hasitem(void* checkitem); + }; + +/////////////////////////////////////////////////////////////////////////////// +// // +// Queue data structure. // +// // +// A 'queue' is basically a link. Following is an image of a queue. // +// ___________ ___________ ___________ // +// Pop() <-- |_ _|<--|_ _|<--|_ _| <-- Push() // +// |_ Data0 _| |_ Data1 _| |_ Data2 _| // +// |___________| |___________| |___________| // +// queue head queue tail // +// // +/////////////////////////////////////////////////////////////////////////////// + + class queue : public link { + + public: + + queue(int bytes, int count = 256) : link(bytes, NULL, count) {} + bool empty() { return linkitems == 0; } + void *push(void* newitem) {return link::add(newitem);} + void *pop() {return link::deletenode((void **) *head);} + // Stack is implemented as a single link list. + void *stackpush() { + void **newnode = (void **) alloc(); + // if (newitem != (void *) NULL) { + // memcpy((void *)(newnode + 2), newitem, linkitembytes); + // } + void **nextnode = (void **) *head; + *head = (void *) newnode; + *newnode = (void *) nextnode; + linkitems++; + return (void *)(newnode + 2); + } + void *stackpop() { + void **deadnode = (void **) *head; + *head = *deadnode; + linkitems--; + return (void *)(deadnode + 2); + } + }; + +/////////////////////////////////////////////////////////////////////////////// +// // +// Global variables used for miscellaneous purposes. // +// // +/////////////////////////////////////////////////////////////////////////////// + + // Pointer to the input data (a set of nodes, a PLC, or a mesh). + tetgenio *in; + // Pointer to the options (and filenames). + tetgenbehavior *b; + // Pointer to a background mesh (contains size specification map). + tetgenmesh *bgm; + + // Variables used to allocate and access memory for tetrahedra, subfaces + // subsegments, points, encroached subfaces, encroached subsegments, + // bad-quality tetrahedra, and so on. + memorypool *tetrahedrons; + memorypool *subfaces; + memorypool *subsegs; + memorypool *points; + memorypool *badsubsegs; + memorypool *badsubfaces; + memorypool *badtetrahedrons; + memorypool *flipstackers; + + // Pointer to the 'tetrahedron' that occupies all of "outer space". + tetrahedron *dummytet; + tetrahedron *dummytetbase; // Keep base address so we can free it later. + + // Pointer to the omnipresent subface. Referenced by any tetrahedron, + // or subface that isn't connected to a subface at that location. + shellface *dummysh; + shellface *dummyshbase; // Keep base address so we can free it later. + + // A point above the plane in which the facet currently being used lies. + // It is used as a reference point for orient3d(). + point *facetabovepointarray, abovepoint; + + // Array (size = numberoftetrahedra * 6) for storing high-order nodes of + // tetrahedra (only used when -o2 switch is selected). + point *highordertable; + + // Arrays for storing and searching pbc data. 'subpbcgrouptable', (size + // is numberofpbcgroups) for pbcgroup of subfaces. 'segpbcgrouptable', + // a list for pbcgroup of segments. Because a segment can have several + // pbcgroup incident on it, its size is unknown on input, it will be + // found in 'createsegpbcgrouptable()'. + pbcdata *subpbcgrouptable; + list *segpbcgrouptable; + // A map for searching the pbcgroups of a given segment. 'idx2segpglist' + // (size = number of input segments + 1), and 'segpglist'. + int *idx2segpglist, *segpglist; + + // Queues that maintain the bad (badly-shaped or too large) tetrahedra. + // The tails are pointers to the pointers that have to be filled in to + // enqueue an item. The queues are ordered from 63 (highest priority) + // to 0 (lowest priority). + badface *subquefront[3], **subquetail[3]; + badface *tetquefront[64], *tetquetail[64]; + int nextnonemptyq[64]; + int firstnonemptyq, recentq; + + // Pointer to a recently visited tetrahedron. Improves point location + // if proximate points are inserted sequentially. + triface recenttet; + + REAL xmax, xmin, ymax, ymin, zmax, zmin; // Bounding box of points. + REAL longest; // The longest possible edge length. + REAL lengthlimit; // The limiting length of a new edge. + long hullsize; // Number of faces of convex hull. + long insegments; // Number of input segments. + int steinerleft; // Number of Steiner points not yet used. + int sizeoftensor; // Number of REALs per metric tensor. + int pointmtrindex; // Index to find the metric tensor of a point. + int 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 highorderindex; // Index to find extra nodes for highorder elements. + 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. + int shmarkindex; // Index to find boundary marker of a subface. + int areaboundindex; // Index to find area bound of a subface. + int checksubfaces; // Are there subfaces in the mesh yet? + int checksubsegs; // Are there subsegs in the mesh yet? + int checkpbcs; // Are there periodic boundary conditions? + int varconstraint; // 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 relverts; // The number of relocated vertices. + int suprelverts; // The number of suppressed relocated vertices. + int collapverts; // The number of collapsed relocated vertices. + int unsupverts; // The number of unsuppressed vertices. + int smoothsegverts; // The number of smoothed vertices. + int smoothvolverts; // The number of smoothed vertices. + int jettisoninverts; // The number of jettisoned input vertices. + int symbolic; // Use symbolic insphere test. + long samples; // Number of random samples for point location. + unsigned long randomseed; // Current random number seed. + REAL macheps; // The machine epsilon. + REAL cosmaxdihed, cosmindihed; // The cosine values of max/min dihedral. + REAL minfaceang, minfacetdihed; // The minimum input (dihedral) angles. + int maxcavfaces, maxcavverts; // The size of the largest cavity. + int expcavcount; // The times of expanding cavitys. + long abovecount; // Number of abovepoints calculation. + long bowatvolcount, bowatsubcount, bowatsegcount; // Bowyer-Watsons. + long updvolcount, updsubcount, updsegcount; // Bow-Wat cavities updates. + long failvolcount, failsubcount, failsegcount; // Bow-Wat fails. + long repairflipcount; // Number of flips for repairing segments. + long outbowatcircumcount; // Number of circumcenters outside Bowat-cav. + long r1count, r2count, r3count; // Numbers of edge splitting rules. + long cdtenforcesegpts; // Number of CDT enforcement points. + long rejsegpts, rejsubpts, rejtetpts; // Number of rejected points. + long optcount[10]; // Numbers of various optimizing operations. + long flip23s, flip32s, flip22s, flip44s; // Number of flips performed. + REAL tloctime, tfliptime; // Time (microseconds) of point location. /////////////////////////////////////////////////////////////////////////////// // // -// Geometric functions // +// Fast lookup tables for mesh manipulation primitives. // +// // +// Mesh manipulation primitives (given below) are basic operations on mesh // +// data structures. They answer basic queries on mesh handles, such as "what // +// is the origin (or destination, or apex) of the face?", "what is the next // +// (or previous) edge in the edge ring?", and "what is the next face in the // +// face ring?", and so on. // +// // +// The implementation of teste basic queries can take advangtage of the fact // +// that the mesh data structures additionally store geometric informations. // +// For example, we have ordered the 4 vertices (from 0 to 3) and the 4 faces // +// (from 0 to 3) of a tetrahedron, and for each face of the tetrahedron, a // +// sequence of vertices has stipulated, therefore the origin of any face of // +// the tetrahedron can be quickly determined by a table 'locver2org', which // +// takes the index of the face and the edge version as inputs. A list of // +// fast lookup tables are defined below. They're just like global variables. // +// These tables are initialized at the runtime. // // // /////////////////////////////////////////////////////////////////////////////// - // PI is the ratio of a circle's circumference to its diameter. - static REAL PI; + // For enext() primitive, uses 'ver' as the index. + static int ve[6]; - // Triangle-triangle intersection test - enum interresult edge_vert_col_inter(REAL*, REAL*, REAL*); - enum interresult edge_edge_cop_inter(REAL*, REAL*, REAL*, REAL*, REAL*); - enum interresult tri_vert_cop_inter(REAL*, REAL*, REAL*, REAL*, REAL*); - enum interresult tri_edge_cop_inter(REAL*, REAL*, REAL*,REAL*,REAL*,REAL*); - enum interresult tri_edge_inter_tail(REAL*, REAL*, REAL*, REAL*, REAL*, - REAL, REAL); - enum interresult tri_edge_inter(REAL*, REAL*, REAL*, REAL*, REAL*); - enum interresult tri_tri_inter(REAL*, REAL*, REAL*, REAL*, REAL*, REAL*); - int tri_edge_2d(point, point, point, point, point, point, int, int*, int*); - int tri_edge_test(point, point, point, point, point, point, int, int*, int*); - - // Geometric tests - REAL incircle3d(point pa, point pb, point pc, point pd); - REAL insphere_s(REAL*, REAL*, REAL*, REAL*, REAL*); - bool iscollinear(REAL*, REAL*, REAL*, REAL eps); - bool iscoplanar(REAL*, REAL*, REAL*, REAL*, REAL vol6, REAL eps); - bool iscospheric(REAL*, REAL*, REAL*, REAL*, REAL*, REAL vol24, REAL eps); - - // Linear algebra functions - inline REAL dot(REAL* v1, REAL* v2); - inline void cross(REAL* v1, REAL* v2, REAL* n); - 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 calculations - inline REAL distance(REAL* p1, REAL* p2); - REAL shortdistance(REAL* p, REAL* e1, REAL* e2); - REAL shortdistance(REAL* p, REAL* e1, REAL* e2, REAL* e3); - REAL interiorangle(REAL* o, REAL* p1, REAL* p2, REAL* n); - void projpt2edge(REAL* p, REAL* e1, REAL* e2, REAL* prj); - void projpt2face(REAL* p, REAL* f1, REAL* f2, REAL* f3, REAL* prj); - void facenormal(REAL* pa, REAL* pb, REAL* pc, REAL* n, REAL* nlen); - void facenormal2(point pa, point pb, point pc, REAL *n, int pivot); - void edgeorthonormal(REAL* e1, REAL* e2, REAL* op, REAL* n); - REAL facedihedral(REAL* pa, REAL* pb, REAL* pc1, REAL* pc2); - void tetalldihedral(point, point, point, point, REAL*, REAL*, REAL*); - 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); - void inscribedsphere(REAL*, REAL*, REAL*, REAL*, REAL* cent, REAL* radius); - void rotatepoint(REAL* p, REAL rotangle, REAL* p1, REAL* p2); - void planelineint(REAL*, REAL*, REAL*, REAL*, REAL*, REAL*, REAL*); - - // Point location routines. - unsigned long randomnation(unsigned int choices); - REAL distance2(tetrahedron* tetptr, point p); - void randomsample(point searchpt, triface *searchtet); - enum locateresult locate(point searchpt, triface* searchtet); - enum locateresult locate2(point searchpt, triface* searchtet, arraypool*); - enum locateresult preciselocate(point searchpt, triface* searchtet, long); - enum locateresult adjustlocate(point, triface*, enum locateresult, REAL); - enum locateresult hullwalk(point searchpt, triface* hulltet); - enum locateresult locatesub(point searchpt, face* searchsh, int, REAL); - enum locateresult adjustlocatesub(point, face*, enum locateresult, REAL); - enum locateresult locateseg(point searchpt, face* searchseg); - enum locateresult adjustlocateseg(point, face*, enum locateresult, REAL); + // For org(), dest() and apex() primitives, uses 'ver' as the index. + static int vo[6], vd[6], va[6]; -/////////////////////////////////////////////////////////////////////////////// -// // -// Mesh update functions // -// // -/////////////////////////////////////////////////////////////////////////////// + // For org(), dest() and apex() primitives, uses 'loc' as the first + // index and 'ver' as the second index. + static int locver2org[4][6]; + static int locver2dest[4][6]; + static int locver2apex[4][6]; - void enqueueflipface(triface&, queue*); - void enqueueflipedge(face&, queue*); - void flip23(triface*, queue*); - void flip32(triface*, queue*); - void flip22(triface*, queue*); - void flip22sub(face*, queue*); - long lawson3d(queue* flipqueue); - long lawson(queue* flipqueue); - - bool removetetbypeeloff(triface *striptet, triface*); - bool removefacebyflip23(REAL *key, triface*, triface*, queue*); - bool removeedgebyflip22(REAL *key, int, triface*, queue*); - bool removeedgebyflip32(REAL *key, triface*, triface*, queue*); - bool removeedgebytranNM(REAL*,int,triface*,triface*,point,point,queue*); - bool removeedgebycombNM(REAL*,int,triface*,int*,triface*,triface*,queue*); - - void splittetrahedron(point, triface*, queue*); - void splittetface(point, triface*, queue*); - void splitsubface(point, face*, queue*); - bool splittetedge(point, triface*, queue*); - void splitsubedge(point, face*, queue*); - - void formstarpolyhedron(point pt, list* tetlist, list* verlist, bool); - void formbowatcavitysub(point, face*, list*, list*); - void formbowatcavityquad(point, list*, list*); - void formbowatcavitysegquad(point, list*, list*); - void formbowatcavity(point bp, face* bpseg, face* bpsh, int* n, int* nmax, - list** sublists, list** subceillists, list** tetlists, - list** ceillists); - void releasebowatcavity(face*, int, list**, list**, list**, list**); - bool validatebowatcavityquad(point bp, list* ceillist, REAL maxcosd); - void updatebowatcavityquad(list* tetlist, list* ceillist); - void updatebowatcavitysub(list* sublist, list* subceillist, int* cutcount); - bool trimbowatcavity(point bp, face* bpseg, int n, list** sublists, - list** subceillists, list** tetlists,list** ceillists, - REAL maxcosd); - void bowatinsertsite(point bp, face* splitseg, int n, list** sublists, - list** subceillists, list** tetlists, list** ceillists, - list* verlist, queue* flipque, bool chkencseg, - bool chkencsub, bool chkbadtet); + // For oppo() primitives, uses 'loc' as the index. + static int loc2oppo[4]; -/////////////////////////////////////////////////////////////////////////////// -// // -// Delaunay tetrahedralization functions // -// // -/////////////////////////////////////////////////////////////////////////////// + // For fnext() primitives, uses 'loc' as the first index and 'ver' as + // the second index, returns an array containing a new 'loc' and a + // new 'ver'. Note: Only valid for 'ver' equals one of {0, 2, 4}. + static int locver2nextf[4][6][2]; - // Point sorting routines. - 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); + // The edge number (from 0 to 5) of a tet is defined as follows: + static int locver2edge[4][6]; + static int edge2locver[6][2]; - enum locateresult insertvertexbw(point insertpt, triface *searchtet, - bool bwflag, bool visflag, - bool noencsegflag, bool noencsubflag); - bool unifypoint(point testpt, triface*, enum locateresult, REAL); - bool incrflipdelaunay(triface*, point*, long, bool, bool, REAL, queue*); - long delaunizevertices(); + // For enumerating three edges of a triangle. + static int plus1mod3[3]; + static int minus1mod3[3]; /////////////////////////////////////////////////////////////////////////////// // // -// Surface triangulation functions // +// 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. // // // /////////////////////////////////////////////////////////////////////////////// - enum locateresult sinsertvertex(point insertpt, face *splitsh,face *splitseg, - bool bwflag, bool cflag); - void formstarpolygon(point pt, list* trilist, list* verlist); - void getfacetabovepoint(face* facetsh); - bool incrflipdelaunaysub(int shmark, REAL eps, list*, int, REAL*, queue*); - enum finddirectionresult finddirectionsub(face* searchsh, point tend); - void insertsubseg(face* tri); - bool scoutsegmentsub(face* searchsh, point tend); - void flipedgerecursive(face* flipedge, queue* flipqueue); - void constrainededge(face* startsh, point tend, queue* flipqueue); - void recoversegment(point tstart, point tend, queue* flipqueue); - void infecthullsub(memorypool* viri); - void plaguesub(memorypool* viri); - void carveholessub(int holes, REAL* holelist, memorypool* viri); - void triangulate(int shmark, REAL eps, list* ptlist, list* conlist,int holes, - REAL* holelist, memorypool* viri, queue*); - void retrievenewsubs(list* newshlist, bool removeseg); - void unifysegments(); - void assignsegmentmarkers(); - void mergefacets(queue* flipqueue); - long meshsurface(); - - // Detect intersecting facets of PLC. - void interecursive(shellface** subfacearray, int arraysize, int axis, - REAL bxmin, REAL bxmax, REAL bymin, REAL bymax, - REAL bzmin, REAL bzmax, int* internum); - void detectinterfaces(); + // Primitives for tetrahedra. + inline void decode(tetrahedron ptr, triface& t); + inline tetrahedron encode(triface& t); + inline void sym(triface& t1, triface& t2); + inline void symself(triface& t); + inline void bond(triface& t1, triface& t2); + inline void dissolve(triface& t); + inline point org(triface& t); + inline point dest(triface& t); + inline point apex(triface& t); + inline point oppo(triface& t); + inline void setorg(triface& t, point pointptr); + inline void setdest(triface& t, point pointptr); + inline void setapex(triface& t, point pointptr); + inline void setoppo(triface& t, point pointptr); + inline void esym(triface& t1, triface& t2); + inline void esymself(triface& t); + inline void enext(triface& t1, triface& t2); + inline void enextself(triface& t); + inline void enext2(triface& t1, triface& t2); + inline void enext2self(triface& t); + inline bool fnext(triface& t1, triface& t2); + inline bool fnextself(triface& t); + inline void enextfnext(triface& t1, triface& t2); + inline void enextfnextself(triface& t); + inline void enext2fnext(triface& t1, triface& t2); + inline void enext2fnextself(triface& t); + inline void infect(triface& t); + inline void uninfect(triface& t); + inline bool infected(triface& t); + inline REAL elemattribute(tetrahedron* ptr, int attnum); + inline void setelemattribute(tetrahedron* ptr, int attnum, REAL value); + inline REAL volumebound(tetrahedron* ptr); + inline void setvolumebound(tetrahedron* ptr, REAL value); + + // Primitives for subfaces and subsegments. + inline void sdecode(shellface sptr, face& s); + inline shellface sencode(face& s); + inline void spivot(face& s1, face& s2); + inline void spivotself(face& s); + inline void sbond(face& s1, face& s2); + inline void sbond1(face& s1, face& s2); + inline void sdissolve(face& s); + inline point sorg(face& s); + inline point sdest(face& s); + inline point sapex(face& s); + inline void setsorg(face& s, point pointptr); + inline void setsdest(face& s, point pointptr); + inline void setsapex(face& s, point pointptr); + inline void sesym(face& s1, face& s2); + inline void sesymself(face& s); + inline void senext(face& s1, face& s2); + inline void senextself(face& s); + inline void senext2(face& s1, face& s2); + inline void senext2self(face& s); + inline void sfnext(face&, face&); + inline void sfnextself(face&); + inline badface* shell2badface(face& s); + inline void setshell2badface(face& s, badface* value); + inline REAL areabound(face& s); + inline void setareabound(face& s, REAL value); + inline int shellmark(face& s); + 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); + + // Primitives for interacting tetrahedra and subfaces. + inline void tspivot(triface& t, face& s); + inline void stpivot(face& s, triface& t); + inline void tsbond(triface& t, face& s); + inline void tsdissolve(triface& t); + inline void stdissolve(face& s); + + // Primitives for interacting subfaces and subsegs. + inline void sspivot(face& s, face& edge); + inline void ssbond(face& s, face& edge); + inline void ssdissolve(face& s); + + inline void tsspivot1(triface& t, face& seg); + inline void tssbond1(triface& t, face& seg); + inline void tssdissolve1(triface& t); + + // Primitives for points. + inline int pointmark(point pt); + inline void setpointmark(point pt, int value); + inline enum verttype pointtype(point pt); + inline void setpointtype(point pt, enum verttype value); + inline tetrahedron point2tet(point pt); + inline void setpoint2tet(point pt, tetrahedron value); + inline shellface point2sh(point pt); + inline void setpoint2sh(point pt, shellface value); + inline point point2ppt(point pt); + 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 adjustedgering(triface& t, int direction); + inline void adjustedgering(face& s, int direction); + inline bool isdead(triface* t); + inline bool isdead(face* s); + inline bool isfacehaspoint(triface* t, point testpoint); + inline bool isfacehaspoint(face* t, point testpoint); + inline bool isfacehasedge(face* s, point tend1, point tend2); + inline bool issymexist(triface* t); + void getnextsface(face*, face*); + void tsspivot(triface*, face*); + void sstpivot(face*, triface*); + bool findorg(triface* t, point dorg); + bool findorg(face* s, point dorg); + void findedge(triface* t, point eorg, point edest); + void findedge(face* s, point eorg, point edest); + void findface(triface *fface, point forg, point fdest, point fapex); + void getonextseg(face* s, face* lseg); + void getseghasorg(face* sseg, point dorg); + point getsubsegfarorg(face* sseg); + point getsubsegfardest(face* sseg); + void printtet(triface*); + void printsh(face*); + +/////////////////////////////////////////////////////////////////////////////// +// // +// Triangle-triangle intersection test // +// // +// The triangle-triangle intersection test is implemented with exact arithm- // +// etic. It exactly tells whether or not two triangles in three dimensions // +// intersect. Before implementing this test myself, I tried two C codes // +// (implemented by Thomas Moeller and Philippe Guigue, respectively), which // +// are all public available. However both of them failed frequently. Another // +// unconvenience is both codes only tell whether or not the two triangles // +// intersect without distinguishing the cases whether they exactly intersect // +// in interior or they just share a vertex or share an edge. The two latter // +// cases are acceptable and should return not intersection in TetGen. // +// // +/////////////////////////////////////////////////////////////////////////////// + + enum interresult edge_vert_col_inter(REAL*, REAL*, REAL*); + enum interresult edge_edge_cop_inter(REAL*, REAL*, REAL*, REAL*, REAL*); + enum interresult tri_vert_cop_inter(REAL*, REAL*, REAL*, REAL*, REAL*); + enum interresult tri_edge_cop_inter(REAL*, REAL*, REAL*,REAL*,REAL*,REAL*); + enum interresult tri_edge_inter_tail(REAL*, REAL*, REAL*, REAL*, REAL*, + REAL, REAL); + enum interresult tri_edge_inter(REAL*, REAL*, REAL*, REAL*, REAL*); + enum interresult tri_tri_inter(REAL*, REAL*, REAL*, REAL*, REAL*, REAL*); + + // Geometric predicates + REAL insphere_sos(REAL*, REAL*, REAL*, REAL*, REAL*, int, int,int,int,int); + bool iscollinear(REAL*, REAL*, REAL*, REAL eps); + bool iscoplanar(REAL*, REAL*, REAL*, REAL*, REAL vol6, REAL eps); + bool iscospheric(REAL*, REAL*, REAL*, REAL*, REAL*, REAL vol24, REAL eps); + + // Linear algebra functions + inline REAL dot(REAL* v1, REAL* v2); + inline void cross(REAL* v1, REAL* v2, REAL* n); + 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 quantities calculators. + inline REAL distance(REAL* p1, REAL* p2); + REAL shortdistance(REAL* p, REAL* e1, REAL* e2); + REAL shortdistance(REAL* p, REAL* e1, REAL* e2, REAL* e3); + REAL interiorangle(REAL* o, REAL* p1, REAL* p2, REAL* n); + void projpt2edge(REAL* p, REAL* e1, REAL* e2, REAL* prj); + void projpt2face(REAL* p, REAL* f1, REAL* f2, REAL* f3, REAL* prj); + void facenormal(REAL* pa, REAL* pb, REAL* pc, REAL* n, REAL* nlen); + void edgeorthonormal(REAL* e1, REAL* e2, REAL* op, REAL* n); + REAL facedihedral(REAL* pa, REAL* pb, REAL* pc1, REAL* pc2); + void tetalldihedral(point, point, point, point, REAL*, REAL*, REAL*); + 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); + void inscribedsphere(REAL*, REAL*, REAL*, REAL*, REAL* cent, REAL* radius); + void rotatepoint(REAL* p, REAL rotangle, REAL* p1, REAL* p2); + void spherelineint(REAL* p1, REAL* p2, REAL* C, REAL R, REAL p[7]); + void linelineint(REAL *p1,REAL *p2, REAL *p3, REAL *p4, REAL p[7]); + void planelineint(REAL*, REAL*, REAL*, REAL*, REAL*, REAL*, REAL*); + + // Memory managment routines. + void dummyinit(int, int); + void initializepools(); + void tetrahedrondealloc(tetrahedron*); + tetrahedron *tetrahedrontraverse(); + 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*); + + // Mesh items searching routines. + void makepoint2tetmap(); + void makeindex2pointmap(point*& idx2verlist); + void makesegmentmap(int*& idx2seglist, shellface**& segsperverlist); + void makesubfacemap(int*& idx2facelist, shellface**& facesperverlist); + void maketetrahedronmap(int*& idx2tetlist, tetrahedron**& tetsperverlist); + + // Point location routines. + unsigned long randomnation(unsigned int choices); + REAL distance2(tetrahedron* tetptr, point p); + enum locateresult preciselocate(point searchpt, triface* searchtet, long); + enum locateresult locate(point searchpt, triface* searchtet); + enum locateresult adjustlocate(point, triface*, enum locateresult, REAL); + enum locateresult hullwalk(point searchpt, triface* hulltet); + enum locateresult locatesub(point searchpt, face* searchsh, int, REAL); + enum locateresult adjustlocatesub(point, face*, enum locateresult, REAL); + enum locateresult locateseg(point searchpt, face* searchseg); + enum locateresult adjustlocateseg(point, face*, enum locateresult, REAL); + +/////////////////////////////////////////////////////////////////////////////// +// // +// Mesh Local Transformation Operators // +// // +// These operators (including flips, insert & remove vertices and so on) are // +// used to transform (or replace) a set of mesh elements into another set of // +// mesh elements. // +// // +/////////////////////////////////////////////////////////////////////////////// + + // Mesh transformation routines. + enum fliptype categorizeface(triface& horiz); + void enqueueflipface(triface& checkface, queue* flipqueue); + void enqueueflipedge(face& checkedge, queue* flipqueue); + void flip23(triface* flipface, queue* flipqueue); + void flip32(triface* flipface, queue* flipqueue); + void flip22(triface* flipface, queue* flipqueue); + void flip22sub(face* flipedge, queue* flipqueue); + long flip(queue* flipqueue, badface **plastflip); + long lawson(list *misseglist, queue* flipqueue); + void undoflip(badface *lastflip); + long flipsub(queue* flipqueue); + bool removetetbypeeloff(triface *striptet); + bool removefacebyflip23(REAL *key, triface*, triface*, queue*); + bool removeedgebyflip22(REAL *key, int, triface*, queue*); + bool removeedgebyflip32(REAL *key, triface*, triface*, queue*); + bool removeedgebytranNM(REAL*,int,triface*,triface*,point,point,queue*); + bool removeedgebycombNM(REAL*,int,triface*,int*,triface*,triface*,queue*); + + void splittetrahedron(point newpoint, triface* splittet, queue* flipqueue); + void unsplittetrahedron(triface* splittet); + void splittetface(point newpoint, triface* splittet, queue* flipqueue); + void unsplittetface(triface* splittet); + void splitsubface(point newpoint, face* splitface, queue* flipqueue); + void unsplitsubface(face* splitsh); + void splittetedge(point newpoint, triface* splittet, queue* flipqueue); + void unsplittetedge(triface* splittet); + void splitsubedge(point newpoint, face* splitsh, queue* flipqueue); + void unsplitsubedge(face* splitsh); + enum insertsiteresult insertsite(point newpoint, triface* searchtet, + bool approx, queue* flipqueue); + void undosite(enum insertsiteresult insresult, triface* splittet, + point torg, point tdest, point tapex, point toppo); + void closeopenface(triface* openface, queue* flipque); + void inserthullsite(point inspoint, triface* horiz, queue* flipque); + + void formbowatcavitysub(point, face*, list*, list*); + void formbowatcavityquad(point, list*, list*); + void formbowatcavitysegquad(point, list*, list*); + void formbowatcavity(point bp, face* bpseg, face* bpsh, int* n, int* nmax, + list** sublists, list** subceillists, list** tetlists, + list** ceillists); + void releasebowatcavity(face*, int, list**, list**, list**, list**); + bool validatebowatcavityquad(point bp, list* ceillist, REAL maxcosd); + void updatebowatcavityquad(list* tetlist, list* ceillist); + void updatebowatcavitysub(list* sublist, list* subceillist, int* cutcount); + bool trimbowatcavity(point bp, face* bpseg, int n, list** sublists, + list** subceillists, list** tetlists,list** ceillists, + REAL maxcosd); + void bowatinsertsite(point bp, face* splitseg, int n, list** sublists, + list** subceillists, list** tetlists, + list** ceillists, list* verlist, queue* flipque, + bool chkencseg, bool chkencsub, bool chkbadtet); + + // Delaunay tetrahedralization routines. + void formstarpolyhedron(point pt, list* tetlist, list* verlist, bool); + bool unifypoint(point testpt, triface*, enum locateresult, REAL); + void incrflipdelaunay(triface*, point*, long, bool, bool, REAL, queue*); + long delaunizevertices(); + + // Surface triangulation routines. + void formstarpolygon(point pt, list* trilist, list* verlist); + void getfacetabovepoint(face* facetsh); + void collectcavsubs(point newpoint, list* cavsublist); + void collectvisiblesubs(int shmark, point inspoint, face* horiz, queue*); + void incrflipdelaunaysub(int shmark, REAL eps, list*, int, REAL*, queue*); + enum finddirectionresult finddirectionsub(face* searchsh, point tend); + void insertsubseg(face* tri); + bool scoutsegmentsub(face* searchsh, point tend); + void flipedgerecursive(face* flipedge, queue* flipqueue); + void constrainededge(face* startsh, point tend, queue* flipqueue); + void recoversegment(point tstart, point tend, queue* flipqueue); + void infecthullsub(memorypool* viri); + void plaguesub(memorypool* viri); + void carveholessub(int holes, REAL* holelist, memorypool* viri); + void triangulate(int shmark, REAL eps, list* ptlist, list* conlist, + int holes, REAL* holelist, memorypool* viri, queue*); + void retrievenewsubs(list* newshlist, bool removeseg); + void unifysegments(); + void mergefacets(queue* flipqueue); + long meshsurface(); + + // Detect intersecting facets of PLC. + void interecursive(shellface** subfacearray, int arraysize, int axis, + REAL bxmin, REAL bxmax, REAL bymin, REAL bymax, + REAL bzmin, REAL bzmax, int* internum); + void detectinterfaces(); + + // Periodic boundary condition supporting routines. + void createsubpbcgrouptable(); + void getsubpbcgroup(face* pbcsub, pbcdata** pd, int *f1, int *f2); + enum locateresult getsubpbcsympoint(point, face*, point, face*); + void createsegpbcgrouptable(); + enum locateresult getsegpbcsympoint(point, face*, point, face*, int); + + // Vertex perturbation routines. + REAL randgenerator(REAL range); + bool checksub4cocir(face* testsub, REAL eps, bool once, bool enqflag); + void tallcocirsubs(REAL eps, bool enqflag); + bool tallencsegsfsubs(point testpt, list* cavsublist); + void collectflipedges(point inspoint, face* splitseg, queue* flipqueue); + void perturbrepairencsegs(queue* flipqueue); + void perturbrepairencsubs(list* cavsublist, queue* flipqueue); + void incrperturbvertices(REAL eps); + + // Segment recovery routines. + void markacutevertices(REAL acuteangle); + enum finddirectionresult finddirection(triface* searchtet, point, long); + void getsearchtet(point p1, point p2, triface* searchtet, point* tend); + bool isedgeencroached(point p1, point p2, point testpt, bool degflag); + point scoutrefpoint(triface* searchtet, point tend); + point getsegmentorigin(face* splitseg); + point getsplitpoint(face* splitseg, point refpoint); + bool insertsegment(face *insseg, list *misseglist); + void tallmissegs(list *misseglist); + void delaunizesegments(); + + // Facets recovery routines. + bool insertsubface(face* insertsh, triface* searchtet); + bool tritritest(triface* checktet, point p1, point p2, point p3); + void initializecavity(list* floorlist, list* ceillist, list* frontlist); + void delaunizecavvertices(triface*, list*, list*, list*, queue*); + void retrievenewtets(list* newtetlist); + void insertauxsubface(triface* front, triface* idfront); + bool scoutfront(triface* front, triface* idfront, list* newtetlist); + void gluefronts(triface* front, triface* front1); + bool identifyfronts(list* frontlist, list* misfrontlist, list* newtetlist); + void detachauxsubfaces(list* newtetlist); + void expandcavity(list* frontlist, list* misfrontlist, list* newtetlist, + list* crosstetlist, queue* missingshqueue, queue*); + void carvecavity(list* newtetlist, list* outtetlist, queue* flipque); + void delaunizecavity(list* floorlist, list* ceillist, list* ceilptlist, + list* floorptlist, list* frontlist,list* misfrontlist, + list* newtetlist, list* crosstetlist, queue*, queue*); + void formmissingregion(face* missingsh, list* missingshlist, + list* equatptlist, int* worklist); + void formcavity(list* missingshlist, list* crossedgelist, + list* equatptlist, list* crossshlist, list* crosstetlist, + list* belowfacelist, list* abovefacelist, + list* horizptlist, list* belowptlist, list* aboveptlist, + queue* missingshqueue, int* worklist); + bool scoutcrossingedge(list* missingshlist, list* boundedgelist, + list* crossedgelist, int* worklist); + void rearrangesubfaces(list* missingshlist, list* boundedgelist, + list* equatptlist, int* worklist); + void insertallsubfaces(queue* missingshqueue); + void constrainedfacets(); + + // Carving out holes and concavities routines. + void infecthull(memorypool *viri); + void plague(memorypool *viri); + void regionplague(memorypool *viri, REAL attribute, REAL volume); + void removeholetets(memorypool *viri); + void assignregionattribs(); + void carveholes(); + + // Steiner points removing routines. + void replacepolygonsubs(list* oldshlist, list* newshlist); + void orientnewsubs(list* newshlist, face* orientsh, REAL* norm); + bool constrainedflip(triface* flipface, triface* front, queue* flipque); + bool recoverfront(triface* front, list* newtetlist, queue* flipque); + void repairflips(queue* flipque); + bool constrainedcavity(triface* oldtet, list* floorlist, list* ceillist, + list* ptlist, list* frontlist, list* misfrontlist, + list* newtetlist, queue* flipque); + void expandsteinercavity(point steinpt, REAL eps, list* frontlist, list*); + bool findrelocatepoint(point sp, point np, REAL* n, list*, list*); + void relocatepoint(point steinpt, triface* oldtet, list*, list*, queue*); + bool findcollapseedge(point suppt, point* conpt, list* oldtetlist, list*); + void collapseedge(point suppt, point conpt, list* oldtetlist, list*); + void deallocfaketets(list* frontlist); + void restorepolyhedron(list* oldtetlist); + bool suppressfacetpoint(face* supsh, list* frontlist, list* misfrontlist, + list* ptlist, list* conlist, memorypool* viri, + queue* flipque, bool noreloc, bool optflag); + bool suppresssegpoint(face* supseg, list* spinshlist, list* newsegshlist, + list* frontlist, list* misfrontlist, list* ptlist, + list* conlist, memorypool* viri, queue* flipque, + bool noreloc, bool optflag); + bool suppressvolpoint(triface* suptet, list* frontlist, list* misfrontlist, + list* ptlist, queue* flipque, bool optflag); + bool smoothpoint(point smthpt, point, point, list *starlist, bool, REAL*); + void removesteiners(bool coarseflag); + + // Mesh reconstruction routines. + long reconstructmesh(); + // Constrained points insertion routines. + void insertconstrainedpoints(tetgenio *addio); + // Background mesh operations. + bool p1interpolatebgm(point pt, triface* bgmtet, long *scount); + void interpolatesizemap(); + void duplicatebgmesh(); + + // Delaunay refinement routines. + void marksharpsegments(REAL sharpangle); + void decidefeaturepointsizes(); + void enqueueencsub(face* ss, point encpt, int quenumber, REAL* cent); + badface* dequeueencsub(int* quenumber); + void enqueuebadtet(triface* tt, REAL key, REAL* cent); + badface* topbadtetra(); + void dequeuebadtet(); + bool checkseg4encroach(face* testseg, point testpt, point*, bool enqflag); + bool checksub4encroach(face* testsub, point testpt, bool enqflag); + bool checktet4badqual(triface* testtet, bool enqflag); + bool acceptsegpt(point segpt, point refpt, face* splitseg); + bool acceptfacpt(point facpt, list* subceillist, list* verlist); + bool acceptvolpt(point volpt, list* ceillist, list* verlist); + void getsplitpoint(point e1, point e2, point refpt, point newpt); + void shepardinterpolate(point newpt, list* verlist); + void setnewpointsize(point newpt, point e1, point e2); + void splitencseg(point, face*, list*, list*, list*,queue*,bool,bool,bool); + bool tallencsegs(point testpt, int n, list** ceillists); + bool tallencsubs(point testpt, int n, list** ceillists); + void tallbadtetrahedrons(); + void repairencsegs(bool chkencsub, bool chkbadtet); + void repairencsubs(bool chkbadtet); + void repairbadtets(); + void enforcequality(); + + // Mesh optimization routines. + void dumpbadtets(); + bool checktet4ill(triface* testtet, bool enqflag); + bool checktet4opt(triface* testtet, bool enqflag); + bool removeedge(badface* remedge, bool optflag); + bool smoothsliver(badface* remedge, list *starlist); + bool splitsliver(badface* remedge, list *tetlist, list *ceillist); + void tallslivers(bool optflag); + void optimizemesh(bool optflag); + + // I/O routines + void transfernodes(); + void jettisonnodes(); + void highorder(); + void outnodes(tetgenio* out); + void outmetrics(tetgenio* out); + void outelements(tetgenio* out); + void outfaces(tetgenio* out); + void outhullfaces(tetgenio* out); + void outsubfaces(tetgenio* out); + void outedges(tetgenio* out); + void outsubsegments(tetgenio* out); + void outneighbors(tetgenio* out); + void outvoronoi(tetgenio* out); + void outpbcnodes(tetgenio* out); + void outsmesh(char* smfilename); + void outmesh2medit(char* mfilename); + void outmesh2gid(char* gfilename); + void outmesh2off(char* ofilename); + + // User interaction routines. + void internalerror(); + void checkmesh(); + void checkshells(); + void checkdelaunay(REAL eps, queue* flipqueue); + void checkconforming(); + void algorithmicstatistics(); + void qualitystatistics(); + void statistics(); + + public: -/////////////////////////////////////////////////////////////////////////////// -// // -// Constrained Delaunay tetrahedralization functions // -// // -/////////////////////////////////////////////////////////////////////////////// + // Constructor and destructor. + tetgenmesh(); + ~tetgenmesh(); - // Segment recovery routines. - void markacutevertices(REAL acuteangle); - enum finddirectionresult finddirection(triface* searchtet, point, long); - enum interresult finddirection2(triface* searchtet, point); - enum interresult finddirection3(triface* searchtet, point); - enum interresult scoutsegment2(face*, triface*, point*); - void getsegmentsplitpoint2(face* sseg, point refpt, REAL* vt); - void delaunizesegments2(); - - // Facets recovery routines. - enum interresult scoutsubface(face* ssub, triface* searchtet, int); - enum interresult scoutcrosstet(face* ssub, triface* searchtet, arraypool*); - void recoversubfacebyflips(face* pssub, triface* crossface, arraypool*); - void formcavity(face*, arraypool*, arraypool*, arraypool*, arraypool*, - arraypool*, arraypool*, arraypool*); - bool delaunizecavity(arraypool*, arraypool*, arraypool*, arraypool*, - arraypool*, arraypool*); - bool fillcavity(arraypool*, arraypool*, arraypool*, arraypool*); - void carvecavity(arraypool*, arraypool*, arraypool*); - void restorecavity(arraypool*, arraypool*, arraypool*); - void splitsubedge(point, face*, arraypool*, arraypool*); - void constrainedfacets2(); - - void formskeleton(clock_t&); - - // Carving out holes and concavities routines. - void infecthull(memorypool *viri); - void plague(memorypool *viri); - void regionplague(memorypool *viri, REAL attribute, REAL volume); - void removeholetets(memorypool *viri); - void assignregionattribs(); - void carveholes(); - -/////////////////////////////////////////////////////////////////////////////// -// // -// Steiner points removal functions // -// // -/////////////////////////////////////////////////////////////////////////////// - - void initializecavity(list* floorlist, list* ceillist, list* frontlist, - list* ptlist, list* gluelist); - bool delaunizecavvertices(triface*, list*, list*, list*, queue*); - void retrievenewtets(list* newtetlist); - void insertauxsubface(triface* front, triface* idfront); - bool scoutfront(triface* front, triface* idfront); - void gluefronts(triface* front, triface* front1, list* gluetetlist, - list* glueshlist); - bool identifyfronts(list* frontlist,list* misfrontlist,list* gluetetlist, - list* glueshlist); - void detachauxsubfaces(list* newtetlist); - bool carvecavity(list* newtetlist, list* outtetlist, list* gluetetlist, - queue* flipque); - - void replacepolygonsubs(list* oldshlist, list* newshlist); - void orientnewsubs(list* newshlist, face* orientsh, REAL* norm); - bool registerelemflip(enum fliptype ft, point pa1, point pb1, point pc1, - point pa2, point pb2, point pc2); - bool check4fixededge(point pa, point pb); - bool removeedgebyflips(triface* remedge, int*); - bool removefacebyflips(triface* remface, int*); - bool recoveredgebyflips(triface* searchtet, point pb, int*); - bool recoverfacebyflips(triface* front, int*); - bool constrainedcavity(triface* oldtet, list* floorlist, list* ceillist, - list* ptlist, list* frontlist, list* misfrontlist, - list* newtetlist, list* gluetetlist, list* glueshlist, - queue* flipque); - bool findrelocatepoint2(point sp, point np, REAL* n, list*, list*); - bool relocatepoint(point steinpt, triface* oldtet, list*, list*, queue*); - bool findcollapseedge(point suppt, point* conpt, list* oldtetlist, list*); - void collapseedge(point suppt, point conpt, list* oldtetlist, list*); - void deallocfaketets(list* frontlist); - void restorepolyhedron(list* oldtetlist); - bool suppressfacetpoint(face* supsh, list* frontlist, list* misfrontlist, - list* ptlist, list* conlist, memorypool* viri, - queue* flipque, bool noreloc, bool optflag); - bool suppresssegpoint(face* supseg, list* spinshlist, list* newsegshlist, - list* frontlist, list* misfrontlist, list* ptlist, - list* conlist, memorypool* viri, queue* flipque, - bool noreloc, bool optflag); - bool suppressvolpoint(triface* suptet, list* frontlist, list* misfrontlist, - list* ptlist, queue* flipque, bool optflag); - void removesteiners2(); - -/////////////////////////////////////////////////////////////////////////////// -// // -// Mesh rebuild functions // -// // -/////////////////////////////////////////////////////////////////////////////// - - void transfernodes(); - long reconstructmesh(); - void insertconstrainedpoints(tetgenio *addio); - bool p1interpolatebgm(point pt, triface* bgmtet, long *scount); - void interpolatesizemap(); - void duplicatebgmesh(); - -/////////////////////////////////////////////////////////////////////////////// -// // -// Mesh refinement functions // -// // -/////////////////////////////////////////////////////////////////////////////// - - void marksharpsegments(REAL sharpangle); - void decidefeaturepointsizes(); - void enqueueencsub(face* ss, point encpt, int quenumber, REAL* cent); - badface* dequeueencsub(int* quenumber); - void enqueuebadtet(triface* tt, REAL key, REAL* cent); - badface* topbadtetra(); - void dequeuebadtet(); - bool checkseg4encroach(face* testseg, point testpt, point*, bool enqflag); - bool checksub4encroach(face* testsub, point testpt, bool enqflag); - bool checktet4badqual(triface* testtet, bool enqflag); - bool acceptsegpt(point segpt, point refpt, face* splitseg); - bool acceptfacpt(point facpt, list* subceillist, list* verlist); - bool acceptvolpt(point volpt, list* ceillist, list* verlist); - void getsplitpoint(point e1, point e2, point refpt, point newpt); - void setnewpointsize(point newpt, point e1, point e2); - bool splitencseg(point, face*, list*, list*, list*,queue*,bool,bool,bool); - bool tallencsegs(point testpt, int n, list** ceillists); - bool tallencsubs(point testpt, int n, list** ceillists); - void tallbadtetrahedrons(); - void repairencsegs(bool chkencsub, bool chkbadtet); - void repairencsubs(bool chkbadtet); - void repairbadtets(); - void enforcequality(); - -/////////////////////////////////////////////////////////////////////////////// -// // -// Mesh optimization routines // -// // -/////////////////////////////////////////////////////////////////////////////// - - bool checktet4ill(triface* testtet, bool enqflag); - bool checktet4opt(triface* testtet, bool enqflag); - bool removeedge(badface* remedge, bool optflag); - bool smoothpoint(point smthpt, point, point, list*, bool, REAL*); - bool smoothsliver(badface* remedge, list *starlist); - bool splitsliver(badface* remedge, list *tetlist, list *ceillist); - void tallslivers(bool optflag); - void optimizemesh2(bool optflag); - -/////////////////////////////////////////////////////////////////////////////// -// // -// Mesh output functions // -// // -/////////////////////////////////////////////////////////////////////////////// - - void jettisonnodes(); - void highorder(); - void numberedges(); - void outnodes(tetgenio*); - void outmetrics(tetgenio*); - void outelements(tetgenio*); - void outfaces(tetgenio*); - void outhullfaces(tetgenio*); - void outsubfaces(tetgenio*); - void outedges(tetgenio*); - void outsubsegments(tetgenio*); - void outneighbors(tetgenio*); - void outvoronoi(tetgenio*); - void outsmesh(char*); - void outmesh2medit(char*); - void outmesh2gid(char*); - void outmesh2off(char*); - void outmesh2vtk(char*); - -/////////////////////////////////////////////////////////////////////////////// -// // -// Mesh check functions // -// // -/////////////////////////////////////////////////////////////////////////////// - - int checkmesh(); - int checkshells(); - int checksegments(); - int checkdelaunay(REAL, queue*); - void checkconforming(); - void algorithmicstatistics(); - void qualitystatistics(); - void statistics(); - -/////////////////////////////////////////////////////////////////////////////// -// // -// Class variables // -// // -/////////////////////////////////////////////////////////////////////////////// - - // Pointer to the input data (a set of nodes, a PLC, or a mesh). - tetgenio *in; - - // Pointer to the options (and filenames). - tetgenbehavior *b; - - // Pointer to a background mesh (contains size specification map). - tetgenmesh *bgm; - - // Variables used to allocate and access memory for tetrahedra, subfaces - // subsegments, points, encroached subfaces, encroached subsegments, - // bad-quality tetrahedra, and so on. - memorypool *tetrahedrons; - memorypool *subfaces; - memorypool *subsegs; - memorypool *points; - memorypool *badsubsegs; - memorypool *badsubfaces; - memorypool *badtetrahedrons; - memorypool *tet2segpool, *tet2subpool; - - // Pointer to the 'tetrahedron' that occupies all of "outer space". - tetrahedron *dummytet; - tetrahedron *dummytetbase; // Keep base address so we can free it later. - - // Pointer to the omnipresent subface. Referenced by any tetrahedron, - // or subface that isn't connected to a subface at that location. - shellface *dummysh; - shellface *dummyshbase; // Keep base address so we can free it later. - - // Entry to find the binary tree nodes (-u option). - arraypool *btreenode_list; - // The maximum size of a btree node (number after -u option) is - int max_btreenode_size; // <= b->max_btreenode_size. - // The maximum btree depth (for bookkeeping). - int max_btree_depth; - - // Arrays used by Bowyer-Watson algorithm. - arraypool *cavetetlist, *cavebdrylist, *caveoldtetlist; - arraypool *caveshlist, *caveshbdlist; - // Stacks used by the boundary recovery algorithm. - arraypool *subsegstack, *subfacstack; - - // Two handles used in constrained facet recovery. - triface firsttopface, firstbotface; - - // An array for registering elementary flips. - arraypool *elemfliplist; - - // An array of fixed edges for facet recovering by flips. - arraypool *fixededgelist; - - // A point above the plane in which the facet currently being used lies. - // It is used as a reference point for orient3d(). - point *facetabovepointarray, abovepoint, dummypoint; - - // Array (size = numberoftetrahedra * 6) for storing high-order nodes of - // tetrahedra (only used when -o2 switch is selected). - point *highordertable; - - // Arrays for storing and searching pbc data. 'subpbcgrouptable', (size - // is numberofpbcgroups) for pbcgroup of subfaces. 'segpbcgrouptable', - // a list for pbcgroup of segments. Because a segment can have several - // pbcgroup incident on it, its size is unknown on input, it will be - // found in 'createsegpbcgrouptable()'. - pbcdata *subpbcgrouptable; - list *segpbcgrouptable; - // A map for searching the pbcgroups of a given segment. 'idx2segpglist' - // (size = number of input segments + 1), and 'segpglist'. - int *idx2segpglist, *segpglist; - - // Queues that maintain the bad (badly-shaped or too large) tetrahedra. - // The tails are pointers to the pointers that have to be filled in to - // enqueue an item. The queues are ordered from 63 (highest priority) - // to 0 (lowest priority). - badface *subquefront[3], **subquetail[3]; - badface *tetquefront[64], *tetquetail[64]; - int nextnonemptyq[64]; - int firstnonemptyq, recentq; - - // Pointer to a recently visited tetrahedron. Improves point location - // if proximate points are inserted sequentially. - triface recenttet; - - REAL xmax, xmin, ymax, ymin, zmax, zmin; // Bounding box of points. - REAL longest; // The longest possible edge length. - REAL lengthlimit; // The limiting length of a new edge. - long hullsize; // Number of faces of convex hull. - long insegments; // Number of input segments. - long meshedges; // Number of output mesh edges. - int steinerleft; // Number of Steiner points not yet used. - int sizeoftensor; // Number of REALs per metric tensor. - int pointmtrindex; // Index to find the metric tensor of a point. - int 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 highorderindex; // Index to find extra nodes for highorder elements. - 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. - int shmarkindex; // Index to find boundary marker of a subface. - int areaboundindex; // Index to find area bound of a subface. - int checksubfaces; // Are there subfaces in the mesh yet? - int checksubsegs; // Are there subsegs in the mesh yet? - int checkpbcs; // Are there periodic boundary conditions? - int varconstraint; // 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 relverts; // The number of relocated vertices. - int suprelverts; // The number of suppressed relocated vertices. - int collapverts; // The number of collapsed relocated vertices. - int unsupverts; // The number of unsuppressed vertices. - int smoothsegverts; // The number of smoothed vertices. - int jettisoninverts; // The number of jettisoned input vertices. - long samples; // Number of random samples for point location. - unsigned long randomseed; // Current random number seed. - REAL macheps; // The machine epsilon. - REAL cosmaxdihed, cosmindihed; // The cosine values of max/min dihedral. - REAL minfaceang, minfacetdihed; // The minimum input (dihedral) angles. - int maxcavfaces, maxcavverts; // The size of the largest cavity. - bool b_steinerflag; - - // Algorithm statistical counters. - long ptloc_count, ptloc_max_count; - long orient3dcount; - long inspherecount, insphere_sos_count; - long flip14count, flip26count, flipn2ncount; - long flip22count; - long inserthullcount; - long maxbowatcavsize, totalbowatcavsize, totaldeadtets; - long across_face_count, across_edge_count, across_max_count; - long maxcavsize, maxregionsize; - long ndelaunayedgecount, cavityexpcount; - long opt_tet_peels, opt_face_flips, opt_edge_flips; - - long abovecount; // Number of abovepoints calculation. - long bowatvolcount, bowatsubcount, bowatsegcount; // Bowyer-Watsons. - long updvolcount, updsubcount, updsegcount; // Bow-Wat cavities updates. - long failvolcount, failsubcount, failsegcount; // Bow-Wat fails. - long outbowatcircumcount; // Number of circumcenters outside Bowat-cav. - long r1count, r2count, r3count; // Numbers of edge splitting rules. - long cdtenforcesegpts; // Number of CDT enforcement points. - long rejsegpts, rejsubpts, rejtetpts; // Number of rejected points. - long optcount[10]; // Numbers of various optimizing operations. - long flip23s, flip32s, flip22s, flip44s; // Number of flips performed. - -/////////////////////////////////////////////////////////////////////////////// -// // -// Class constructor & destructor // -// // -/////////////////////////////////////////////////////////////////////////////// - - tetgenmesh() - { - bgm = (tetgenmesh *) NULL; - in = (tetgenio *) NULL; - b = (tetgenbehavior *) NULL; - - tetrahedrons = (memorypool *) NULL; - subfaces = (memorypool *) NULL; - subsegs = (memorypool *) NULL; - points = (memorypool *) NULL; - badsubsegs = (memorypool *) NULL; - badsubfaces = (memorypool *) NULL; - badtetrahedrons = (memorypool *) NULL; - tet2segpool = NULL; - tet2subpool = NULL; - - dummytet = (tetrahedron *) NULL; - dummytetbase = (tetrahedron *) NULL; - dummysh = (shellface *) NULL; - dummyshbase = (shellface *) NULL; - - facetabovepointarray = (point *) NULL; - abovepoint = (point) NULL; - dummypoint = NULL; - btreenode_list = (arraypool *) NULL; - highordertable = (point *) NULL; - subpbcgrouptable = (pbcdata *) NULL; - segpbcgrouptable = (list *) NULL; - idx2segpglist = (int *) NULL; - segpglist = (int *) NULL; - - cavetetlist = NULL; - cavebdrylist = NULL; - caveoldtetlist = NULL; - caveshlist = caveshbdlist = NULL; - subsegstack = subfacstack = NULL; - - elemfliplist = (arraypool *) NULL; - fixededgelist = (arraypool *) NULL; - - xmax = xmin = ymax = ymin = zmax = zmin = 0.0; - longest = 0.0; - hullsize = 0l; - insegments = 0l; - meshedges = 0l; - pointmtrindex = 0; - pointmarkindex = 0; - point2simindex = 0; - point2pbcptindex = 0; - highorderindex = 0; - elemattribindex = 0; - volumeboundindex = 0; - shmarkindex = 0; - areaboundindex = 0; - checksubfaces = 0; - checksubsegs = 0; - checkpbcs = 0; - varconstraint = 0; - nonconvex = 0; - dupverts = 0; - unuverts = 0; - relverts = 0; - suprelverts = 0; - collapverts = 0; - unsupverts = 0; - jettisoninverts = 0; - samples = 0l; - randomseed = 1l; - macheps = 0.0; - minfaceang = minfacetdihed = PI; - b_steinerflag = false; - - ptloc_count = ptloc_max_count = 0l; - orient3dcount = 0l; - inspherecount = insphere_sos_count = 0l; - flip14count = flip26count = flipn2ncount = 0l; - flip22count = 0l; - inserthullcount = 0l; - maxbowatcavsize = totalbowatcavsize = totaldeadtets = 0l; - across_face_count = across_edge_count = across_max_count = 0l; - maxcavsize = maxregionsize = 0l; - ndelaunayedgecount = cavityexpcount = 0l; - opt_tet_peels = opt_face_flips = opt_edge_flips = 0l; - - maxcavfaces = maxcavverts = 0; - abovecount = 0l; - bowatvolcount = bowatsubcount = bowatsegcount = 0l; - updvolcount = updsubcount = updsegcount = 0l; - outbowatcircumcount = 0l; - failvolcount = failsubcount = failsegcount = 0l; - r1count = r2count = r3count = 0l; - cdtenforcesegpts = 0l; - rejsegpts = rejsubpts = rejtetpts = 0l; - flip23s = flip32s = flip22s = flip44s = 0l; - } // tetgenmesh() - - ~tetgenmesh() - { - bgm = (tetgenmesh *) NULL; - in = (tetgenio *) NULL; - b = (tetgenbehavior *) NULL; - - 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 (dummytetbase != (tetrahedron *) NULL) { - delete [] dummytetbase; - } - if (dummyshbase != (shellface *) NULL) { - delete [] dummyshbase; - } - if (facetabovepointarray != (point *) NULL) { - delete [] facetabovepointarray; - } - if (dummypoint != NULL) { - delete [] dummypoint; - } - if (highordertable != (point *) NULL) { - delete [] highordertable; - } - if (subpbcgrouptable != (pbcdata *) NULL) { - delete [] subpbcgrouptable; - } - if (segpbcgrouptable != (list *) NULL) { - delete segpbcgrouptable; - delete [] idx2segpglist; - delete [] segpglist; - } - - if (cavetetlist != NULL) { - delete cavetetlist; - delete cavebdrylist; - delete caveoldtetlist; - } - if (subsegstack != NULL) { - delete subsegstack; - } - if (subfacstack != NULL) { - delete subfacstack; - } - } // ~tetgenmesh() - -}; // End of class tetgenmesh. +}; // End of class tetgenmesh. /////////////////////////////////////////////////////////////////////////////// // // @@ -2312,1078 +1921,7 @@ class tetgenmesh { void tetrahedralize(tetgenbehavior *b, tetgenio *in, tetgenio *out, tetgenio *addin = NULL, tetgenio *bgmin = NULL); - -#ifdef TETLIBRARY void tetrahedralize(char *switches, tetgenio *in, tetgenio *out, tetgenio *addin = NULL, tetgenio *bgmin = NULL); -#endif // #ifdef TETLIBRARY - -/////////////////////////////////////////////////////////////////////////////// -// // -// terminatetetgen() Terminate TetGen with a given exit code. // -// // -/////////////////////////////////////////////////////////////////////////////// - -inline void terminatetetgen(int x) -{ -#ifdef TETLIBRARY - throw x; -#else - switch (x) { - case 1: // Out of memory. - printf("Error: Out of memory.\n"); - break; - case 2: // Encounter an internal error. - printf(" Please report this bug to sihang@mail.berlios.de. Include\n"); - printf(" the message above, your input data set, and the exact\n"); - printf(" command line you used to run this program, thank you.\n"); - break; - default: - printf("Program stopped.\n"); - } // switch (x) - exit(x); -#endif // #ifdef TETLIBRARY -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// 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". // -// // -/////////////////////////////////////////////////////////////////////////////// - -REAL exactinit(); -void exactdeinit(); -REAL orient3d(REAL *pa, REAL *pb, REAL *pc, REAL *pd); -REAL insphere(REAL *pa, REAL *pb, REAL *pc, REAL *pd, REAL *pe); - -/////////////////////////////////////////////////////////////////////////////// -// // -// Inline functions of mesh data structures // -// // -/////////////////////////////////////////////////////////////////////////////// - -// Some macros for convenience - -#define Div2 >> 1 -#define Mod2 & 01 - -// NOTE: These bit operators should only be used in macros below. - -// Get orient(Range from 0 to 2) from face version(Range from 0 to 5). - -#define Orient(V) ((V) Div2) - -// Determine edge ring(0 or 1) from face version(Range from 0 to 5). - -#define EdgeRing(V) ((V) Mod2) - -// -// Begin of primitives for tetrahedra -// - -// Each tetrahedron contains four pointers to its neighboring tetrahedra, -// with face indices. To save memory, both information are kept in a -// single pointer. To make this possible, all tetrahedra are aligned to -// eight-byte boundaries, so that the last three bits of each pointer are -// zeros. A face index (in the range 0 to 3) is compressed into the last -// two bits of each pointer by the function 'encode()'. The function -// 'decode()' decodes a pointer, extracting a face index and a pointer to -// the beginning of a tetrahedron. - -inline void tetgenmesh::decode(tetrahedron ptr, triface& t) { - t.loc = (int) ((uintptr_t) (ptr) & (uintptr_t) 3); - t.tet = (tetrahedron *) ((uintptr_t) (ptr) & ~(uintptr_t) 7); -} - -inline tetgenmesh::tetrahedron tetgenmesh::encode(triface& t) { - return (tetrahedron) ((uintptr_t) t.tet | (uintptr_t) t.loc); -} - -// sym() finds the abutting tetrahedron on the same face. - -inline void tetgenmesh::sym(triface& t1, triface& t2) { - tetrahedron ptr = t1.tet[t1.loc]; - decode(ptr, t2); -} - -inline void tetgenmesh::symself(triface& t) { - tetrahedron ptr = t.tet[t.loc]; - decode(ptr, t); -} - -// Bond two tetrahedra together at their faces. - -inline void tetgenmesh::bond(triface& t1, triface& t2) { - t1.tet[t1.loc] = encode(t2); - t2.tet[t2.loc] = encode(t1); -} - -// Dissolve a bond (from one side). Note that the other tetrahedron will -// still think it is connected to this tetrahedron. Usually, however, -// the other tetrahedron is being deleted entirely, or bonded to another -// tetrahedron, so it doesn't matter. - -inline void tetgenmesh::dissolve(triface& t) { - t.tet[t.loc] = (tetrahedron) dummytet; -} - -// These primitives determine or set the origin, destination, apex or -// opposition of a tetrahedron with respect to 'loc' and 'ver'. - -inline tetgenmesh::point tetgenmesh::org(triface& t) { - return (point) t.tet[locver2org[t.loc][t.ver] + 4]; -} - -inline tetgenmesh::point tetgenmesh::dest(triface& t) { - return (point) t.tet[locver2dest[t.loc][t.ver] + 4]; -} - -inline tetgenmesh::point tetgenmesh::apex(triface& t) { - return (point) t.tet[locver2apex[t.loc][t.ver] + 4]; -} - -inline tetgenmesh::point tetgenmesh::oppo(triface& t) { - return (point) t.tet[loc2oppo[t.loc] + 4]; -} - -inline void tetgenmesh::setorg(triface& t, point pointptr) { - t.tet[locver2org[t.loc][t.ver] + 4] = (tetrahedron) pointptr; -} - -inline void tetgenmesh::setdest(triface& t, point pointptr) { - t.tet[locver2dest[t.loc][t.ver] + 4] = (tetrahedron) pointptr; -} - -inline void tetgenmesh::setapex(triface& t, point pointptr) { - t.tet[locver2apex[t.loc][t.ver] + 4] = (tetrahedron) pointptr; -} - -inline void tetgenmesh::setoppo(triface& t, point pointptr) { - t.tet[loc2oppo[t.loc] + 4] = (tetrahedron) pointptr; -} - -// These primitives were drived from Mucke's triangle-edge data structure -// to change face-edge relation in a tetrahedron (esym, enext and enext2) -// or between two tetrahedra (fnext). - -// If e0 = e(i, j), e1 = e(j, i), that is e0 and e1 are the two directions -// of the same undirected edge of a face. e0.sym() = e1 and vice versa. - -inline void tetgenmesh::esym(triface& t1, triface& t2) { - t2.tet = t1.tet; - t2.loc = t1.loc; - t2.ver = t1.ver + (EdgeRing(t1.ver) ? -1 : 1); -} - -inline void tetgenmesh::esymself(triface& t) { - t.ver += (EdgeRing(t.ver) ? -1 : 1); -} - -// If e0 and e1 are both in the same edge ring of a face, e1 = e0.enext(). - -inline void tetgenmesh::enext(triface& t1, triface& t2) { - t2.tet = t1.tet; - t2.loc = t1.loc; - t2.ver = ve[t1.ver]; -} - -inline void tetgenmesh::enextself(triface& t) { - t.ver = ve[t.ver]; -} - -// enext2() is equal to e2 = e0.enext().enext() - -inline void tetgenmesh::enext2(triface& t1, triface& t2) { - t2.tet = t1.tet; - t2.loc = t1.loc; - t2.ver = ve[ve[t1.ver]]; -} - -inline void tetgenmesh::enext2self(triface& t) { - t.ver = ve[ve[t.ver]]; -} - -// If f0 and f1 are both in the same face ring of a face, f1 = f0.fnext(). -// If f1 exists, return true. Otherwise, return false, i.e., f0 is a -// boundary or hull face. - -inline bool tetgenmesh::fnext(triface& t1, triface& t2) -{ - // Get the next face. - t2.loc = locver2nextf[t1.loc][t1.ver][0]; - // Is the next face in the same tet? - if (t2.loc != -1) { - // It's in the same tet. Get the edge version. - t2.ver = locver2nextf[t1.loc][t1.ver][1]; - t2.tet = t1.tet; - } else { - // The next face is in the neigbhour of 't1'. - sym(t1, t2); - if (t2.tet != dummytet) { - // Find the corresponding edge in t2. - point torg; - int tloc, tver, i; - t2.ver = 0; - torg = org(t1); - for (i = 0; (i < 3) && (org(t2) != torg); i++) { - enextself(t2); - } - // Go to the next face in t2. - tloc = t2.loc; - tver = t2.ver; - t2.loc = locver2nextf[tloc][tver][0]; - t2.ver = locver2nextf[tloc][tver][1]; - } - } - return t2.tet != dummytet; -} - -inline bool tetgenmesh::fnextself(triface& t1) -{ - triface t2; - - // Get the next face. - t2.loc = locver2nextf[t1.loc][t1.ver][0]; - // Is the next face in the same tet? - if (t2.loc != -1) { - // It's in the same tet. Get the edge version. - t2.ver = locver2nextf[t1.loc][t1.ver][1]; - t1.loc = t2.loc; - t1.ver = t2.ver; - } else { - // The next face is in the neigbhour of 't1'. - sym(t1, t2); - if (t2.tet != dummytet) { - // Find the corresponding edge in t2. - point torg; - int i; - t2.ver = 0; - torg = org(t1); - for (i = 0; (i < 3) && (org(t2) != torg); i++) { - enextself(t2); - } - t1.loc = locver2nextf[t2.loc][t2.ver][0]; - t1.ver = locver2nextf[t2.loc][t2.ver][1]; - t1.tet = t2.tet; - } - } - return t2.tet != dummytet; -} - -// Given a face t1, find the face f2 in the adjacent tet. If t2 is not -// a dummytet, then t1 and t2 refer to the same edge. Moreover, t2's -// edge must be in 0th edge ring, e.g., t2.ver is one of {0, 2, 4}. -// No matter what edge version t1 is. - -inline void tetgenmesh::symedge(triface& t1, triface& t2) -{ - decode(t1.tet[t1.loc], t2); - if (t2.tet != dummytet) { - // Search the edge of t1 in t2. - point tapex = apex(t1); - if ((point) (t2.tet[locver2apex[t2.loc][0] + 4]) == tapex) { - t2.ver = 0; - } else if ((point) (t2.tet[locver2apex[t2.loc][2] + 4]) == tapex) { - t2.ver = 2; - } else { - assert((point) (t2.tet[locver2apex[t2.loc][4] + 4]) == tapex); - t2.ver = 4; - } - } -} - -inline void tetgenmesh::symedgeself(triface& t) -{ - tetrahedron ptr; - point tapex; - - ptr = t.tet[t.loc]; - tapex = apex(t); - - decode(ptr, t); - if (t.tet != dummytet) { - // Search the edge of t1 in t2. - if ((point) (t.tet[locver2apex[t.loc][0] + 4]) == tapex) { - t.ver = 0; - } else if ((point) (t.tet[locver2apex[t.loc][2] + 4]) == tapex) { - t.ver = 2; - } else { - assert((point) (t.tet[locver2apex[t.loc][4] + 4]) == tapex); - t.ver = 4; - } - } -} - -// Given a face t1, find the next face t2 in the face ring, t1 and t2 -// are in two different tetrahedra. If the next face is a hull face, -// t2 is dummytet. - -inline void tetgenmesh::tfnext(triface& t1, triface& t2) -{ - int *iptr; - - if ((t1.ver & 1) == 0) { - t2.tet = t1.tet; - iptr = locver2nextf[t1.loc][t1.ver]; - t2.loc = iptr[0]; - t2.ver = iptr[1]; - symedgeself(t2); // t2.tet may be dummytet. - } else { - symedge(t1, t2); - if (t2.tet != dummytet) { - iptr = locver2nextf[t2.loc][t2.ver]; - t2.loc = iptr[0]; - t2.ver = iptr[1]; - } - } -} - -inline void tetgenmesh::tfnextself(triface& t) -{ - int *iptr; - - if ((t.ver & 1) == 0) { - iptr = locver2nextf[t.loc][t.ver]; - t.loc = iptr[0]; - t.ver = iptr[1]; - symedgeself(t); // t.tet may be dummytet. - } else { - symedgeself(t); - if (t.tet != dummytet) { - iptr = locver2nextf[t.loc][t.ver]; - t.loc = iptr[0]; - t.ver = iptr[1]; - } - } -} - -// enextfnext() and enext2fnext() are combination primitives of enext(), -// enext2() and fnext(). - -inline void tetgenmesh::enextfnext(triface& t1, triface& t2) { - enext(t1, t2); - fnextself(t2); -} - -inline void tetgenmesh::enextfnextself(triface& t) { - enextself(t); - fnextself(t); -} - -inline void tetgenmesh::enext2fnext(triface& t1, triface& t2) { - enext2(t1, t2); - fnextself(t2); -} - -inline void tetgenmesh::enext2fnextself(triface& t) { - enext2self(t); - fnextself(t); -} - -// Check or set a tetrahedron's attributes. - -inline REAL tetgenmesh::elemattribute(tetrahedron* ptr, int attnum) { - return ((REAL *) (ptr))[elemattribindex + attnum]; -} - -inline void tetgenmesh:: -setelemattribute(tetrahedron* ptr, int attnum, REAL value){ - ((REAL *) (ptr))[elemattribindex + attnum] = value; -} - -// Check or set a tetrahedron's maximum volume bound. - -inline REAL tetgenmesh::volumebound(tetrahedron* ptr) { - return ((REAL *) (ptr))[volumeboundindex]; -} - -inline void tetgenmesh::setvolumebound(tetrahedron* ptr, REAL value) { - ((REAL *) (ptr))[volumeboundindex] = value; -} - -// Check or set a tetrahedron's marker. - -inline int tetgenmesh::getelemmarker(tetrahedron* ptr) { - return ((int *) (ptr))[elemmarkerindex]; -} - -inline void tetgenmesh::setelemmarker(tetrahedron* ptr, int value) { - ((int *) (ptr))[elemmarkerindex] = value; -} - -// infect(), infected(), uninfect() -- primitives to flag or unflag a -// tetrahedron. The last bit of the element marker is flagged (1) -// or unflagged (0). - -inline void tetgenmesh::infect(triface& t) { - ((int *) (t.tet))[elemmarkerindex] |= (int) 1; -} - -inline void tetgenmesh::uninfect(triface& t) { - ((int *) (t.tet))[elemmarkerindex] &= ~(int) 1; -} - -// Test a tetrahedron for viral infection. - -inline bool tetgenmesh::infected(triface& t) { - return (((int *) (t.tet))[elemmarkerindex] & (int) 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. - -inline void tetgenmesh::marktest(triface& t) { - ((int *) (t.tet))[elemmarkerindex] |= (int) 2; -} - -inline void tetgenmesh::unmarktest(triface& t) { - ((int *) (t.tet))[elemmarkerindex] &= ~(int) 2; -} - -inline bool tetgenmesh::marktested(triface& t) { - return (((int *) (t.tet))[elemmarkerindex] & (int) 2) != 0; -} - -// 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] |= (int) (4<<(t).loc); -} - -inline void tetgenmesh::unmarkface(triface& t) { - ((int *) (t.tet))[elemmarkerindex] &= ~(int) (4<<(t).loc); -} - -inline bool tetgenmesh::facemarked(triface& t) { - return (((int *) (t.tet))[elemmarkerindex] & (int) (4<<(t).loc)) != 0; -} - -// 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. - -inline void tetgenmesh::markedge(triface& t) { - ((int *) (t.tet))[elemmarkerindex] |= - (int) (64<> (int) 2; - // return ((int *) (s.sh))[shmarkindex]; -} - -inline void tetgenmesh::setshellmark(face& s, int value) { - ((int *) ((s).sh))[shmarkindex] = (value << (int) 2) + - ((((int *) ((s).sh))[shmarkindex]) & (int) 3); - // ((int *) (s.sh))[shmarkindex] = value; -} - -// These two primitives set or read the type of the subface or subsegment. - -inline enum tetgenmesh::shestype tetgenmesh::shelltype(face& s) { - return (enum shestype) ((int *) (s.sh))[shmarkindex + 1]; -} - -inline void tetgenmesh::setshelltype(face& s, enum shestype value) { - ((int *) (s.sh))[shmarkindex + 1] = (int) value; -} - -// 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] is flaged. - -inline void tetgenmesh::sinfect(face& s) { - ((int *) ((s).sh))[shmarkindex] = - (((int *) ((s).sh))[shmarkindex] | (int) 1); - // s.sh[6] = (shellface) ((unsigned long) s.sh[6] | (unsigned long) 4l); -} - -inline void tetgenmesh::suninfect(face& s) { - ((int *) ((s).sh))[shmarkindex] = - (((int *) ((s).sh))[shmarkindex] & ~(int) 1); - // s.sh[6] = (shellface)((unsigned long) s.sh[6] & ~(unsigned long) 4l); -} - -// Test a subface for viral infection. - -inline bool tetgenmesh::sinfected(face& s) { - return (((int *) ((s).sh))[shmarkindex] & (int) 1) != 0; -} - -// smarktest(), smarktested(), sunmarktest() -- primitives to flag or unflag -// a subface. The last 2nd bit of ((int *) ((s).sh))[shmarkindex] is flaged. - -#define smarktest(s) \ - ((int *) ((s).sh))[shmarkindex] = (((int *)((s).sh))[shmarkindex] | (int) 2) - -#define sunmarktest(s) \ - ((int *) ((s).sh))[shmarkindex] = (((int *)((s).sh))[shmarkindex] & ~(int) 2) - -#define smarktested(s) ((((int *) ((s).sh))[shmarkindex] & (int) 2) != 0) - -// -// End of primitives for subfaces/subsegments -// - -// -// Begin of primitives for interacting between tetrahedra and subfaces -// - -// tspivot() finds a subface abutting on this tetrahdera. - -inline void tetgenmesh::tspivot(triface& t, face& s) { - if ((t).tet[9] != NULL) { - sdecode(((shellface *) (t).tet[9])[(t).loc], s); - } else { - (s).sh = dummysh; - } - //shellface sptr = (shellface) t.tet[8 + t.loc]; - //sdecode(sptr, s); -} - -// stpivot() finds a tetrahedron abutting a subface. - -inline void tetgenmesh::stpivot(face& s, triface& t) { - tetrahedron ptr = (tetrahedron) s.sh[6 + EdgeRing(s.shver)]; - decode(ptr, t); -} - -// tsbond() bond a tetrahedron to a subface. - -inline void tetgenmesh::tsbond(triface& t, face& s) { - if ((t).tet[9] == NULL) { - // Allocate space for this tet. - (t).tet[9] = (tetrahedron) tet2subpool->alloc(); - // NULL all fields in this space. - for (int i = 0; i < 4; i++) { - ((shellface *) (t).tet[9])[i] = (shellface) dummysh; - } - } - // Bond t <==> s. - ((shellface *) (t).tet[9])[(t).loc] = sencode(s); - //t.tet[8 + t.loc] = (tetrahedron) sencode(s); - s.sh[6 + EdgeRing(s.shver)] = (shellface) encode(t); -} - -// tsdissolve() dissolve a bond (from the tetrahedron side). - -inline void tetgenmesh::tsdissolve(triface& t) { - if ((t).tet[9] != NULL) { - ((shellface *) (t).tet[9])[(t).loc] = (shellface) dummysh; - } - // t.tet[8 + t.loc] = (tetrahedron) dummysh; -} - -// stdissolve() dissolve a bond (from the subface side). - -inline void tetgenmesh::stdissolve(face& s) { - s.sh[6 + EdgeRing(s.shver)] = (shellface) dummytet; -} - -// -// End of primitives for interacting between tetrahedra and subfaces -// - -// -// Begin of primitives for interacting between subfaces and subsegs -// - -// sspivot() finds a subsegment abutting a subface. - -inline void tetgenmesh::sspivot(face& s, face& edge) { - shellface sptr = (shellface) s.sh[8 + Orient(s.shver)]; - sdecode(sptr, edge); -} - -// ssbond() bond a subface to a subsegment. - -inline void tetgenmesh::ssbond(face& s, face& edge) { - s.sh[8 + Orient(s.shver)] = sencode(edge); - edge.sh[0] = sencode(s); -} - -// ssdisolve() dissolve a bond (from the subface side) - -inline void tetgenmesh::ssdissolve(face& s) { - s.sh[8 + Orient(s.shver)] = (shellface) dummysh; -} - -// -// End of primitives for interacting between subfaces and subsegs -// - -// -// Begin of primitives for interacting between tet and subsegs. -// - -inline void tetgenmesh::tsspivot1(triface& t, face& s) -{ - if ((t).tet[8] != NULL) { - sdecode(((shellface *) (t).tet[8])[locver2edge[(t).loc][(t).ver]], s); - } else { - (s).sh = dummysh; - } - // shellface sptr = (shellface) t.tet[8 + locver2edge[t.loc][t.ver]]; - // sdecode(sptr, seg); -} - -// Only bond/dissolve at tet's side, but not vice versa. - -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. - for (int i = 0; i < 6; i++) { - ((shellface *) (t).tet[8])[i] = (shellface) dummysh; - } - } - // Bond the segment. - ((shellface *) (t).tet[8])[locver2edge[(t).loc][(t).ver]] = sencode((s)); - // t.tet[8 + locver2edge[t.loc][t.ver]] = (tetrahedron) sencode(seg); -} - -inline void tetgenmesh::tssdissolve1(triface& t) -{ - if ((t).tet[8] != NULL) { - ((shellface *) (t).tet[8])[locver2edge[(t).loc][(t).ver]] - = (shellface) dummysh; - } - // t.tet[8 + locver2edge[t.loc][t.ver]] = (tetrahedron) dummysh; -} - -// -// End of primitives for interacting between tet and subsegs. -// - -// -// Begin of primitives for points -// - -inline int tetgenmesh::pointmark(point pt) { - return ((int *) (pt))[pointmarkindex]; -} - -inline void tetgenmesh::setpointmark(point pt, int value) { - ((int *) (pt))[pointmarkindex] = value; -} - -// These two primitives set and read the type of the point. -// The last significant bit of this integer is used by pinfect/puninfect. - -inline enum tetgenmesh::verttype tetgenmesh::pointtype(point pt) { - return (enum verttype) (((int *) (pt))[pointmarkindex + 1] >> (int) 1); -} - -inline void tetgenmesh::setpointtype(point pt, enum verttype value) { - ((int *) (pt))[pointmarkindex + 1] = - ((int) value << 1) + (((int *) (pt))[pointmarkindex + 1] & (int) 1); -} - -// pinfect(), puninfect(), pinfected() -- primitives to flag or unflag -// a point. The last bit of the integer '[pointindex+1]' is flaged. - -inline void tetgenmesh::pinfect(point pt) { - ((int *) (pt))[pointmarkindex + 1] |= (int) 1; -} - -inline void tetgenmesh::puninfect(point pt) { - ((int *) (pt))[pointmarkindex + 1] &= ~(int) 1; -} - -inline bool tetgenmesh::pinfected(point pt) { - return (((int *) (pt))[pointmarkindex + 1] & (int) 1) != 0; -} - -// These following primitives set and read a pointer to a tetrahedron -// a subface/subsegment, a point, or a tet of background mesh. - -inline tetgenmesh::tetrahedron tetgenmesh::point2tet(point pt) { - return ((tetrahedron *) (pt))[point2simindex]; -} - -inline void tetgenmesh::setpoint2tet(point pt, tetrahedron value) { - ((tetrahedron *) (pt))[point2simindex] = value; -} - -inline tetgenmesh::shellface tetgenmesh::point2sh(point pt) { - return (shellface) ((tetrahedron *) (pt))[point2simindex + 1]; -} - -inline void tetgenmesh::setpoint2sh(point pt, shellface value) { - ((tetrahedron *) (pt))[point2simindex + 1] = (tetrahedron) value; -} - -inline tetgenmesh::shellface tetgenmesh::point2seg(point pt) { - return (shellface) ((tetrahedron *) (pt))[point2simindex + 2]; -} - -inline void tetgenmesh::setpoint2seg(point pt, shellface value) { - ((tetrahedron *) (pt))[point2simindex + 2] = (tetrahedron) value; -} - -inline tetgenmesh::point tetgenmesh::point2ppt(point pt) { - return (point) ((tetrahedron *) (pt))[point2simindex + 3]; -} - -inline void tetgenmesh::setpoint2ppt(point pt, point value) { - ((tetrahedron *) (pt))[point2simindex + 3] = (tetrahedron) value; -} - -inline tetgenmesh::tetrahedron tetgenmesh::point2bgmtet(point pt) { - return ((tetrahedron *) (pt))[point2simindex + 4]; -} - -inline void tetgenmesh::setpoint2bgmtet(point pt, tetrahedron value) { - ((tetrahedron *) (pt))[point2simindex + 4] = 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; -} - -// -// End of primitives for points -// - -// -// Begin of advanced primitives -// - -// adjustedgering() adjusts the edge version so that it belongs to the -// indicated edge ring. The 'direction' only can be 0(CCW) or 1(CW). -// If the edge is not in the wanted edge ring, reverse it. - -inline void tetgenmesh::adjustedgering(triface& t, int direction) { - if (EdgeRing(t.ver) != direction) { - esymself(t); - } -} - -inline void tetgenmesh::adjustedgering(face& s, int direction) { - if (EdgeRing(s.shver) != direction) { - sesymself(s); - } -} - -// isdead() returns TRUE if the tetrahedron or subface has been dealloced. - -inline bool tetgenmesh::isdead(triface* t) { - if (t->tet == (tetrahedron *) NULL) return true; - else return t->tet[4] == (tetrahedron) NULL; -} - -inline bool tetgenmesh::isdead(face* s) { - if (s->sh == (shellface *) NULL) return true; - else return s->sh[3] == (shellface) NULL; -} - -// isfacehaspoint() returns TRUE if the 'testpoint' is one of the vertices -// of the tetface 't' subface 's'. - -inline bool tetgenmesh::isfacehaspoint(triface* t, point testpoint) { - return ((org(*t) == testpoint) || (dest(*t) == testpoint) || - (apex(*t) == testpoint)); -} - -inline bool tetgenmesh::isfacehaspoint(face* s, point testpoint) { - return (s->sh[3] == (shellface) testpoint) || - (s->sh[4] == (shellface) testpoint) || - (s->sh[5] == (shellface) testpoint); -} - -// isfacehasedge() returns TRUE if the edge (given by its two endpoints) is -// one of the three edges of the subface 's'. - -inline bool tetgenmesh::isfacehasedge(face* s, point tend1, point tend2) { - return (isfacehaspoint(s, tend1) && isfacehaspoint(s, tend2)); -} - -// issymexist() returns TRUE if the adjoining tetrahedron is not 'duumytet'. - -inline bool tetgenmesh::issymexist(triface* t) { - tetrahedron *ptr = (tetrahedron *) - ((unsigned long)(t->tet[t->loc]) & ~(unsigned long)7l); - return ptr != dummytet; -} - -// 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]; - n[1] = -(v1[0] * v2[2] - v2[0] * v1[2]); - n[2] = v1[0] * v2[1] - v2[0] * v1[1]; -} - -// 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]) + - (p2[1] - p1[1]) * (p2[1] - p1[1]) + - (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) - -#define SWAP2(a0, a1, tmp) (tmp) = (a0); (a0) = (a1); (a1) = (tmp) - -/////////////////////////////////////////////////////////////////////////////// -// // -// Two inline functions used in read/write VTK files. // -// // -/////////////////////////////////////////////////////////////////////////////// - -inline void swapBytes(unsigned char* var, int size) -{ - int i = 0; - int j = size - 1; - char c; - - while (i < j) { - c = var[i]; var[i] = var[j]; var[j] = c; - i++, j--; - } -} - -inline bool testIsBigEndian() -{ - short word = 0x4321; - if((*(char *)& word) != 0x21) - return true; - else - return false; -} #endif // #ifndef tetgenH diff --git a/src/cpp/wrap_tetgen.cpp b/src/cpp/wrap_tetgen.cpp index 9a9c06b1714d4514973db2647a7d5fb3b315da56..8c84601f39a7614802e17b7aca4f162605612719 100644 --- a/src/cpp/wrap_tetgen.cpp +++ b/src/cpp/wrap_tetgen.cpp @@ -147,7 +147,6 @@ namespace OVERRIDE_LOAD_WITH_ERROR_CHECK(ply,); OVERRIDE_LOAD_WITH_ERROR_CHECK(stl,); OVERRIDE_LOAD_WITH_ERROR_CHECK(medit,); - OVERRIDE_LOAD_WITH_ERROR_CHECK(vtk,); void load_plc(char* filename, int object) { @@ -158,6 +157,7 @@ namespace OVERRIDE_LOAD_WITH_ERROR_CHECK(tetmesh, Elements.fixUnit(numberofcorners); ); + OVERRIDE_LOAD_WITH_ERROR_CHECK(voronoi,); /* tTriangulationParameters &operator=(const tTriangulationParameters &src) @@ -362,6 +362,7 @@ BOOST_PYTHON_MODULE(_tetgen) .DEF_METHOD(save_poly) .DEF_METHOD(load_node) + .DEF_METHOD(load_pbc) .DEF_METHOD(load_var) .DEF_METHOD(load_mtr) .DEF_METHOD(load_poly)