From 7f798c2b016e6d4474d4ab30fe8bd012ed8c7e69 Mon Sep 17 00:00:00 2001 From: Andreas Kloeckner Date: Mon, 10 Feb 2014 17:23:47 -0600 Subject: [PATCH] Towards Tetgen 1.5 --- src/cpp/tetgen.cpp | 50583 ++++++++++++++++++-------------------- src/cpp/tetgen.h | 4395 ++-- src/cpp/wrap_tetgen.cpp | 146 +- 3 files changed, 25720 insertions(+), 29404 deletions(-) diff --git a/src/cpp/tetgen.cpp b/src/cpp/tetgen.cpp index b4743b8..6468509 100644 --- a/src/cpp/tetgen.cpp +++ b/src/cpp/tetgen.cpp @@ -2,34 +2,21 @@ // // // TetGen // // // -// A Quality Tetrahedral Mesh Generator and 3D Delaunay Triangulator // +// A Quality Tetrahedral Mesh Generator and A 3D Delaunay Triangulator // // // -// Version 1.4 // -// September 6, December 13, 2009 // +// Version 1.5 // +// November 4, 2013 // // // -// Copyright (C) 2002--2009 // -// Hang Si // -// Research Group: Numerical Mathematics and Scientific Computing // -// Weierstrass Institute for Applied Analysis and Stochastics (WIAS) // -// Mohrenstr. 39, 10117 Berlin, Germany // -// si@wias-berlin.de // -// // -// TetGen is freely available through the website: http://tetgen.berlios.de. // +// TetGen is freely available through the website: http://www.tetgen.org. // // It may be copied, modified, and redistributed for non-commercial use. // // Please consult the file LICENSE for the detailed copyright notices. // // // /////////////////////////////////////////////////////////////////////////////// -/////////////////////////////////////////////////////////////////////////////// -// // -// tetgen.cxx // -// // -// The TetGen library and program. // -// // -/////////////////////////////////////////////////////////////////////////////// - #include "tetgen.h" +extern void exactdeinit(); + //// io_cxx /////////////////////////////////////////////////////////////////// //// //// //// //// @@ -62,33 +49,23 @@ tetgenio::facet::~facet() 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. // // // -// 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. // +// '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. 'uvflag' indicates each node contains // +// u,v coordinates or not. It is reuqired by a PSC. 'infilename' is the name // +// of the file being read, it is only used in error messages. // // // // The 'firstnumber' (0 or 1) is automatically determined by the number of // // the first index of the first point. // // // /////////////////////////////////////////////////////////////////////////////// -bool tetgenio::load_node_call(FILE* infile, int markers, char* infilename) +bool tetgenio::load_node_call(FILE* infile, int markers, int uvflag, + char* infilename) { char inputline[INPUTLINESIZE]; char *stringptr; @@ -100,18 +77,24 @@ 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) { - terminatetetgen(1); + terminatetetgen(NULL, 1); } if (numberofpointattributes > 0) { pointattributelist = new REAL[numberofpoints * numberofpointattributes]; if (pointattributelist == (REAL *) NULL) { - terminatetetgen(1); + terminatetetgen(NULL, 1); } } if (markers) { pointmarkerlist = new int[numberofpoints]; if (pointmarkerlist == (int *) NULL) { - terminatetetgen(1); + terminatetetgen(NULL, 1); + } + } + if (uvflag) { + pointparamlist = new pointparam[numberofpoints]; + if (pointparamlist == NULL) { + terminatetetgen(NULL, 1); } } @@ -173,6 +156,37 @@ bool tetgenio::load_node_call(FILE* infile, int markers, char* infilename) } pointmarkerlist[i] = currentmarker; } + if (uvflag) { + // Read point paramteters. + stringptr = findnextnumber(stringptr); + if (*stringptr == '\0') { + printf("Error: Point %d has no uv[0].\n", firstnumber + i); + break; + } + pointparamlist[i].uv[0] = (REAL) strtod(stringptr, &stringptr); + stringptr = findnextnumber(stringptr); + if (*stringptr == '\0') { + printf("Error: Point %d has no uv[1].\n", firstnumber + i); + break; + } + pointparamlist[i].uv[1] = (REAL) strtod(stringptr, &stringptr); + stringptr = findnextnumber(stringptr); + if (*stringptr == '\0') { + printf("Error: Point %d has no tag.\n", firstnumber + i); + break; + } + pointparamlist[i].tag = (int) strtol (stringptr, &stringptr, 0); + stringptr = findnextnumber(stringptr); + if (*stringptr == '\0') { + printf("Error: Point %d has no type.\n", firstnumber + i); + break; + } + pointparamlist[i].type = (int) strtol (stringptr, &stringptr, 0); + if ((pointparamlist[i].type < 0) || (pointparamlist[i].type > 2)) { + printf("Error: Point %d has an invalid type.\n", firstnumber + i); + break; + } + } } if (i < numberofpoints) { // Failed to read points due to some error. @@ -186,6 +200,10 @@ bool tetgenio::load_node_call(FILE* infile, int markers, char* infilename) delete [] pointattributelist; pointattributelist = (REAL *) NULL; } + if (uvflag) { + delete [] pointparamlist; + pointparamlist = NULL; + } numberofpoints = 0; return false; } @@ -206,6 +224,7 @@ bool tetgenio::load_node(char* filebasename) char *stringptr; bool okflag; int markers; + int uvflag; // for psc input. // Assembling the actual file names we want to open. strcpy(innodefilename, filebasename); @@ -214,13 +233,20 @@ bool tetgenio::load_node(char* filebasename) // Try to open a .node file. infile = fopen(innodefilename, "r"); if (infile == (FILE *) NULL) { - printf("File I/O Error: Cannot access file %s.\n", innodefilename); + printf(" Cannot access file %s.\n", innodefilename); return false; } - printf("Opening %s.\n", innodefilename); + printf("Opening %s.\n", innodefilename); + + // Set initial flags. + mesh_dim = 3; + numberofpointattributes = 0; // no point attribute. + markers = 0; // no boundary marker. + uvflag = 0; // no uv parameters (required by a PSC). + // Read the first line of the file. stringptr = readnumberline(inputline, infile, innodefilename); - // Does this file contain an index colume? + // Does this file contain an index column? stringptr = strstr(inputline, "rbox"); if (stringptr == NULL) { // Read number of points, number of dimensions, number of point @@ -228,23 +254,21 @@ bool tetgenio::load_node(char* filebasename) stringptr = inputline; numberofpoints = (int) strtol (stringptr, &stringptr, 0); stringptr = findnextnumber(stringptr); - if (*stringptr == '\0') { - mesh_dim = 3; - } else { + if (*stringptr != '\0') { mesh_dim = (int) strtol (stringptr, &stringptr, 0); } stringptr = findnextnumber(stringptr); - if (*stringptr == '\0') { - numberofpointattributes = 0; - } else { + if (*stringptr != '\0') { numberofpointattributes = (int) strtol (stringptr, &stringptr, 0); } stringptr = findnextnumber(stringptr); - if (*stringptr == '\0') { - markers = 0; - } else { + if (*stringptr != '\0') { markers = (int) strtol (stringptr, &stringptr, 0); } + stringptr = findnextnumber(stringptr); + if (*stringptr != '\0') { + uvflag = (int) strtol (stringptr, &stringptr, 0); + } } else { // It is a rbox (qhull) input file. stringptr = inputline; @@ -258,7 +282,7 @@ bool tetgenio::load_node(char* filebasename) } // Load the list of nodes. - okflag = load_node_call(infile, markers, innodefilename); + okflag = load_node_call(infile, markers, uvflag, innodefilename); fclose(infile); return okflag; @@ -266,230 +290,583 @@ bool tetgenio::load_node(char* filebasename) /////////////////////////////////////////////////////////////////////////////// // // -// load_var() Load constraints applied on facets, segments, and nodes // -// from a .var file. // +// load_edge() Load a list of edges from a .edge file. // // // /////////////////////////////////////////////////////////////////////////////// -bool tetgenio::load_var(char* filebasename) +bool tetgenio::load_edge(char* filebasename) { FILE *infile; - char varfilename[FILENAMESIZE]; + char inedgefilename[FILENAMESIZE]; char inputline[INPUTLINESIZE]; char *stringptr; + int markers, corner; int index; - int i; + int i, j; - // Variant constraints are saved in file "filename.var". - strcpy(varfilename, filebasename); - strcat(varfilename, ".var"); - infile = fopen(varfilename, "r"); + strcpy(inedgefilename, filebasename); + strcat(inedgefilename, ".edge"); + + infile = fopen(inedgefilename, "r"); if (infile != (FILE *) NULL) { - printf("Opening %s.\n", varfilename); + printf("Opening %s.\n", inedgefilename); } else { - // No such file. Ignore it without a message. + //printf(" Cannot access file %s.\n", inedgefilename); return false; } - // Read the facet constraint section. - stringptr = readnumberline(inputline, infile, varfilename); - if (*stringptr != '\0') { - numberoffacetconstraints = (int) strtol (stringptr, &stringptr, 0); - } else { - numberoffacetconstraints = 0; + // Read number of boundary edges. + stringptr = readnumberline(inputline, infile, inedgefilename); + numberofedges = (int) strtol (stringptr, &stringptr, 0); + if (numberofedges > 0) { + edgelist = new int[numberofedges * 2]; + if (edgelist == (int *) NULL) { + terminatetetgen(NULL, 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]; + } } - if (numberoffacetconstraints > 0) { - // Initialize 'facetconstraintlist'. - facetconstraintlist = new REAL[numberoffacetconstraints * 2]; - index = 0; - for (i = 0; i < numberoffacetconstraints; i++) { - stringptr = readnumberline(inputline, infile, varfilename); + + // Read the list of edges. + index = 0; + for (i = 0; i < numberofedges; i++) { + // Read edge index and the edge's two endpoints. + stringptr = readnumberline(inputline, infile, inedgefilename); + for (j = 0; j < 2; j++) { stringptr = findnextnumber(stringptr); if (*stringptr == '\0') { - printf("Error: facet constraint %d has no facet marker.\n", - firstnumber + i); - break; - } else { - facetconstraintlist[index++] = (REAL) strtod(stringptr, &stringptr); + printf("Error: Edge %d is missing vertex %d in %s.\n", + i + firstnumber, j + 1, inedgefilename); + terminatetetgen(NULL, 1); } - stringptr = findnextnumber(stringptr); - if (*stringptr == '\0') { - printf("Error: facet constraint %d has no maximum area bound.\n", - firstnumber + i); - break; - } else { - facetconstraintlist[index++] = (REAL) strtod(stringptr, &stringptr); + corner = (int) strtol(stringptr, &stringptr, 0); + if (corner < firstnumber || corner >= numberofpoints + firstnumber) { + printf("Error: Edge %d has an invalid vertex index.\n", + i + firstnumber); + terminatetetgen(NULL, 1); } + edgelist[index++] = corner; } - if (i < numberoffacetconstraints) { - // This must be caused by an error. - fclose(infile); - return false; + if (numberofcorners == 10) { + // Skip an extra vertex (generated by a previous -o2 option). + stringptr = findnextnumber(stringptr); + } + // Read the edge marker if it has. + if (markers) { + stringptr = findnextnumber(stringptr); + edgemarkerlist[i] = (int) strtol(stringptr, &stringptr, 0); } } - // Read the segment constraint section. - stringptr = readnumberline(inputline, infile, varfilename); - if (*stringptr != '\0') { - numberofsegmentconstraints = (int) strtol (stringptr, &stringptr, 0); + fclose(infile); + return true; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// load_face() Load a list of faces (triangles) from a .face file. // +// // +/////////////////////////////////////////////////////////////////////////////// + +bool tetgenio::load_face(char* filebasename) +{ + FILE *infile; + char infilename[FILENAMESIZE]; + char inputline[INPUTLINESIZE]; + char *stringptr; + REAL attrib; + int markers, corner; + int index; + int i, j; + + strcpy(infilename, filebasename); + strcat(infilename, ".face"); + + infile = fopen(infilename, "r"); + if (infile != (FILE *) NULL) { + printf("Opening %s.\n", infilename); } else { - numberofsegmentconstraints = 0; + return false; } - if (numberofsegmentconstraints > 0) { - // Initialize 'segmentconstraintlist'. - segmentconstraintlist = new REAL[numberofsegmentconstraints * 3]; - index = 0; - for (i = 0; i < numberofsegmentconstraints; i++) { - stringptr = readnumberline(inputline, infile, varfilename); - stringptr = findnextnumber(stringptr); - if (*stringptr == '\0') { - printf("Error: segment constraint %d has no frist endpoint.\n", - firstnumber + i); - break; - } else { - segmentconstraintlist[index++] = (REAL) strtod(stringptr, &stringptr); + + // Read number of faces, boundary markers. + stringptr = readnumberline(inputline, infile, infilename); + numberoftrifaces = (int) strtol (stringptr, &stringptr, 0); + stringptr = findnextnumber(stringptr); + if (mesh_dim == 2) { + // Skip a number. + stringptr = findnextnumber(stringptr); + } + if (*stringptr == '\0') { + markers = 0; // Default there is no marker per face. + } else { + markers = (int) strtol (stringptr, &stringptr, 0); + } + if (numberoftrifaces > 0) { + trifacelist = new int[numberoftrifaces * 3]; + if (trifacelist == (int *) NULL) { + terminatetetgen(NULL, 1); + } + if (markers) { + trifacemarkerlist = new int[numberoftrifaces]; + if (trifacemarkerlist == (int *) NULL) { + terminatetetgen(NULL, 1); } + } + } + + // Read the list of faces. + index = 0; + for (i = 0; i < numberoftrifaces; i++) { + // Read face index and the face's three corners. + stringptr = readnumberline(inputline, infile, infilename); + for (j = 0; j < 3; j++) { stringptr = findnextnumber(stringptr); if (*stringptr == '\0') { - printf("Error: segment constraint %d has no second endpoint.\n", - firstnumber + i); - break; - } else { - segmentconstraintlist[index++] = (REAL) strtod(stringptr, &stringptr); + printf("Error: Face %d is missing vertex %d in %s.\n", + i + firstnumber, j + 1, infilename); + terminatetetgen(NULL, 1); + } + corner = (int) strtol(stringptr, &stringptr, 0); + if (corner < firstnumber || corner >= numberofpoints + firstnumber) { + printf("Error: Face %d has an invalid vertex index.\n", + i + firstnumber); + terminatetetgen(NULL, 1); + } + trifacelist[index++] = corner; + } + if (numberofcorners == 10) { + // Skip 3 extra vertices (generated by a previous -o2 option). + for (j = 0; j < 3; j++) { + stringptr = findnextnumber(stringptr); } + } + // Read the boundary marker if it exists. + if (markers) { stringptr = findnextnumber(stringptr); if (*stringptr == '\0') { - printf("Error: segment constraint %d has no maximum length bound.\n", - firstnumber + i); - break; + attrib = 0.0; } else { - segmentconstraintlist[index++] = (REAL) strtod(stringptr, &stringptr); + attrib = (REAL) strtod(stringptr, &stringptr); } - } - if (i < numberofsegmentconstraints) { - // This must be caused by an error. - fclose(infile); - return false; + trifacemarkerlist[i] = (int) attrib; } } fclose(infile); + return true; } /////////////////////////////////////////////////////////////////////////////// // // -// load_mtr() Load a size specification map from a .mtr file. // +// load_tet() Load a list of tetrahedra from a .ele file. // // // /////////////////////////////////////////////////////////////////////////////// -bool tetgenio::load_mtr(char* filebasename) +bool tetgenio::load_tet(char* filebasename) { FILE *infile; - char mtrfilename[FILENAMESIZE]; + char infilename[FILENAMESIZE]; char inputline[INPUTLINESIZE]; char *stringptr; - REAL mtr; - int mtrindex; + REAL attrib; + int corner; + int index, attribindex; int i, j; - strcpy(mtrfilename, filebasename); - strcat(mtrfilename, ".mtr"); - infile = fopen(mtrfilename, "r"); + strcpy(infilename, filebasename); + strcat(infilename, ".ele"); + + infile = fopen(infilename, "r"); if (infile != (FILE *) NULL) { - printf("Opening %s.\n", mtrfilename); + printf("Opening %s.\n", infilename); } else { - // No such file. Return. return false; } - // Read number of points, number of columns (1, 3, or 6). - stringptr = readnumberline(inputline, infile, mtrfilename); - stringptr = findnextnumber(stringptr); // Skip number of points. - if (*stringptr != '\0') { - numberofpointmtrs = (int) strtol (stringptr, &stringptr, 0); + // Read number of elements, number of corners (4 or 10), number of + // element attributes. + stringptr = readnumberline(inputline, infile, infilename); + numberoftetrahedra = (int) strtol (stringptr, &stringptr, 0); + if (numberoftetrahedra <= 0) { + printf("Error: Invalid number of tetrahedra.\n"); + fclose(infile); + return false; } - if (numberofpointmtrs == 0) { - // Column number doesn't match. Set a default number (1). - numberofpointmtrs = 1; + stringptr = findnextnumber(stringptr); + if (*stringptr == '\0') { + numberofcorners = 4; // Default read 4 nodes per element. + } else { + numberofcorners = (int) strtol(stringptr, &stringptr, 0); + } + stringptr = findnextnumber(stringptr); + if (*stringptr == '\0') { + numberoftetrahedronattributes = 0; // Default no attribute. + } else { + numberoftetrahedronattributes = (int) strtol(stringptr, &stringptr, 0); + } + if (numberofcorners != 4 && numberofcorners != 10) { + printf("Error: Wrong number of corners %d (should be 4 or 10).\n", + numberofcorners); + fclose(infile); + return false; } - // Allocate space for pointmtrlist. - pointmtrlist = new REAL[numberofpoints * numberofpointmtrs]; - if (pointmtrlist == (REAL *) NULL) { - terminatetetgen(1); + // Allocate memory for tetrahedra. + tetrahedronlist = new int[numberoftetrahedra * numberofcorners]; + if (tetrahedronlist == (int *) NULL) { + terminatetetgen(NULL, 1); } - mtrindex = 0; - for (i = 0; i < numberofpoints; i++) { - // Read metrics. - stringptr = readnumberline(inputline, infile, mtrfilename); - for (j = 0; j < numberofpointmtrs; j++) { + // Allocate memory for output tetrahedron attributes if necessary. + if (numberoftetrahedronattributes > 0) { + tetrahedronattributelist = new REAL[numberoftetrahedra * + numberoftetrahedronattributes]; + if (tetrahedronattributelist == (REAL *) NULL) { + terminatetetgen(NULL, 1); + } + } + + // Read the list of tetrahedra. + index = 0; + attribindex = 0; + for (i = 0; i < numberoftetrahedra; i++) { + // Read tetrahedron index and the tetrahedron's corners. + stringptr = readnumberline(inputline, infile, infilename); + for (j = 0; j < numberofcorners; j++) { + stringptr = findnextnumber(stringptr); if (*stringptr == '\0') { - printf("Error: Metric %d is missing value #%d in %s.\n", - i + firstnumber, j + 1, mtrfilename); - terminatetetgen(1); + printf("Error: Tetrahedron %d is missing vertex %d in %s.\n", + i + firstnumber, j + 1, infilename); + terminatetetgen(NULL, 1); } - mtr = (REAL) strtod(stringptr, &stringptr); - pointmtrlist[mtrindex++] = mtr; + corner = (int) strtol(stringptr, &stringptr, 0); + if (corner < firstnumber || corner >= numberofpoints + firstnumber) { + printf("Error: Tetrahedron %d has an invalid vertex index.\n", + i + firstnumber); + terminatetetgen(NULL, 1); + } + tetrahedronlist[index++] = corner; + } + // Read the tetrahedron's attributes. + for (j = 0; j < numberoftetrahedronattributes; j++) { stringptr = findnextnumber(stringptr); + if (*stringptr == '\0') { + attrib = 0.0; + } else { + attrib = (REAL) strtod(stringptr, &stringptr); + } + tetrahedronattributelist[attribindex++] = attrib; } } fclose(infile); + return true; } /////////////////////////////////////////////////////////////////////////////// // // -// load_poly() Load a PL complex from a .poly or a .smesh file. // +// load_vol() Load a list of volume constraints from a .vol file. // // // /////////////////////////////////////////////////////////////////////////////// -bool tetgenio::load_poly(char* filebasename) +bool tetgenio::load_vol(char* filebasename) { - FILE *infile, *polyfile; - char innodefilename[FILENAMESIZE]; - char inpolyfilename[FILENAMESIZE]; + FILE *infile; + char inelefilename[FILENAMESIZE]; + char infilename[FILENAMESIZE]; + char inputline[INPUTLINESIZE]; + char *stringptr; + REAL volume; + int volelements; + int i; + + strcpy(infilename, filebasename); + strcat(infilename, ".vol"); + + infile = fopen(infilename, "r"); + if (infile != (FILE *) NULL) { + printf("Opening %s.\n", infilename); + } else { + return false; + } + + // Read number of tetrahedra. + stringptr = readnumberline(inputline, infile, infilename); + volelements = (int) strtol (stringptr, &stringptr, 0); + if (volelements != numberoftetrahedra) { + strcpy(inelefilename, filebasename); + strcat(infilename, ".ele"); + printf("Warning: %s and %s disagree on number of tetrahedra.\n", + inelefilename, infilename); + fclose(infile); + return false; + } + + tetrahedronvolumelist = new REAL[volelements]; + if (tetrahedronvolumelist == (REAL *) NULL) { + terminatetetgen(NULL, 1); + } + + // Read the list of volume constraints. + for (i = 0; i < volelements; i++) { + stringptr = readnumberline(inputline, infile, infilename); + stringptr = findnextnumber(stringptr); + if (*stringptr == '\0') { + volume = -1.0; // No constraint on this tetrahedron. + } else { + volume = (REAL) strtod(stringptr, &stringptr); + } + tetrahedronvolumelist[i] = volume; + } + + fclose(infile); + + return true; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// load_var() Load constraints applied on facets, segments, and nodes // +// from a .var file. // +// // +/////////////////////////////////////////////////////////////////////////////// + +bool tetgenio::load_var(char* filebasename) +{ + FILE *infile; + char varfilename[FILENAMESIZE]; + char inputline[INPUTLINESIZE]; + char *stringptr; + int index; + int i; + + // Variant constraints are saved in file "filename.var". + strcpy(varfilename, filebasename); + strcat(varfilename, ".var"); + infile = fopen(varfilename, "r"); + if (infile != (FILE *) NULL) { + printf("Opening %s.\n", varfilename); + } else { + return false; + } + + // Read the facet constraint section. + stringptr = readnumberline(inputline, infile, varfilename); + if (*stringptr != '\0') { + numberoffacetconstraints = (int) strtol (stringptr, &stringptr, 0); + } else { + numberoffacetconstraints = 0; + } + if (numberoffacetconstraints > 0) { + // Initialize 'facetconstraintlist'. + facetconstraintlist = new REAL[numberoffacetconstraints * 2]; + index = 0; + for (i = 0; i < numberoffacetconstraints; i++) { + stringptr = readnumberline(inputline, infile, varfilename); + stringptr = findnextnumber(stringptr); + if (*stringptr == '\0') { + printf("Error: facet constraint %d has no facet marker.\n", + firstnumber + i); + break; + } else { + facetconstraintlist[index++] = (REAL) strtod(stringptr, &stringptr); + } + stringptr = findnextnumber(stringptr); + if (*stringptr == '\0') { + printf("Error: facet constraint %d has no maximum area bound.\n", + firstnumber + i); + break; + } else { + facetconstraintlist[index++] = (REAL) strtod(stringptr, &stringptr); + } + } + if (i < numberoffacetconstraints) { + // This must be caused by an error. + fclose(infile); + return false; + } + } + + // Read the segment constraint section. + stringptr = readnumberline(inputline, infile, varfilename); + if (*stringptr != '\0') { + numberofsegmentconstraints = (int) strtol (stringptr, &stringptr, 0); + } else { + numberofsegmentconstraints = 0; + } + if (numberofsegmentconstraints > 0) { + // Initialize 'segmentconstraintlist'. + segmentconstraintlist = new REAL[numberofsegmentconstraints * 3]; + index = 0; + for (i = 0; i < numberofsegmentconstraints; i++) { + stringptr = readnumberline(inputline, infile, varfilename); + stringptr = findnextnumber(stringptr); + if (*stringptr == '\0') { + printf("Error: segment constraint %d has no frist endpoint.\n", + firstnumber + i); + break; + } else { + segmentconstraintlist[index++] = (REAL) strtod(stringptr, &stringptr); + } + stringptr = findnextnumber(stringptr); + if (*stringptr == '\0') { + printf("Error: segment constraint %d has no second endpoint.\n", + firstnumber + i); + break; + } else { + segmentconstraintlist[index++] = (REAL) strtod(stringptr, &stringptr); + } + stringptr = findnextnumber(stringptr); + if (*stringptr == '\0') { + printf("Error: segment constraint %d has no maximum length bound.\n", + firstnumber + i); + break; + } else { + segmentconstraintlist[index++] = (REAL) strtod(stringptr, &stringptr); + } + } + if (i < numberofsegmentconstraints) { + // This must be caused by an error. + fclose(infile); + return false; + } + } + + fclose(infile); + return true; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// load_mtr() Load a size specification map from a .mtr file. // +// // +/////////////////////////////////////////////////////////////////////////////// + +bool tetgenio::load_mtr(char* filebasename) +{ + FILE *infile; + char mtrfilename[FILENAMESIZE]; + char inputline[INPUTLINESIZE]; + char *stringptr; + REAL mtr; + int ptnum; + int mtrindex; + int i, j; + + strcpy(mtrfilename, filebasename); + strcat(mtrfilename, ".mtr"); + infile = fopen(mtrfilename, "r"); + if (infile != (FILE *) NULL) { + printf("Opening %s.\n", mtrfilename); + } else { + return false; + } + + // Read the number of points. + stringptr = readnumberline(inputline, infile, mtrfilename); + ptnum = (int) strtol (stringptr, &stringptr, 0); + if (ptnum != numberofpoints) { + printf(" !! Point numbers are not equal. Ignored.\n"); + fclose(infile); + return false; + } + // Read the number of columns (1, 3, or 6). + stringptr = findnextnumber(stringptr); // Skip number of points. + if (*stringptr != '\0') { + numberofpointmtrs = (int) strtol (stringptr, &stringptr, 0); + } + if (numberofpointmtrs == 0) { + // Column number doesn't match. Set a default number (1). + numberofpointmtrs = 1; + } + + // Allocate space for pointmtrlist. + pointmtrlist = new REAL[numberofpoints * numberofpointmtrs]; + if (pointmtrlist == (REAL *) NULL) { + terminatetetgen(NULL, 1); + } + mtrindex = 0; + for (i = 0; i < numberofpoints; i++) { + // Read metrics. + stringptr = readnumberline(inputline, infile, mtrfilename); + for (j = 0; j < numberofpointmtrs; j++) { + if (*stringptr == '\0') { + printf("Error: Metric %d is missing value #%d in %s.\n", + i + firstnumber, j + 1, mtrfilename); + terminatetetgen(NULL, 1); + } + mtr = (REAL) strtod(stringptr, &stringptr); + pointmtrlist[mtrindex++] = mtr; + stringptr = findnextnumber(stringptr); + } + } + + fclose(infile); + return true; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// load_poly() Load a PL complex from a .poly or a .smesh file. // +// // +/////////////////////////////////////////////////////////////////////////////// + +bool tetgenio::load_poly(char* filebasename) +{ + FILE *infile; + char inpolyfilename[FILENAMESIZE]; char insmeshfilename[FILENAMESIZE]; char inputline[INPUTLINESIZE]; char *stringptr, *infilename; - int smesh, markers, currentmarker; - int readnodefile, index; + int smesh, markers, uvflag, currentmarker; + int index; int i, j, k; // Assembling the actual file names we want to open. - strcpy(innodefilename, filebasename); strcpy(inpolyfilename, filebasename); strcpy(insmeshfilename, filebasename); - strcat(innodefilename, ".node"); strcat(inpolyfilename, ".poly"); strcat(insmeshfilename, ".smesh"); // First assume it is a .poly file. smesh = 0; // Try to open a .poly file. - polyfile = fopen(inpolyfilename, "r"); - if (polyfile == (FILE *) NULL) { + infile = fopen(inpolyfilename, "r"); + if (infile == (FILE *) NULL) { // .poly doesn't exist! Try to open a .smesh file. - polyfile = fopen(insmeshfilename, "r"); - if (polyfile == (FILE *) NULL) { - printf("File I/O Error: Cannot access file %s and %s.\n", + infile = fopen(insmeshfilename, "r"); + if (infile == (FILE *) NULL) { + printf(" Cannot access file %s and %s.\n", inpolyfilename, insmeshfilename); return false; } else { printf("Opening %s.\n", insmeshfilename); + infilename = insmeshfilename; } smesh = 1; } else { printf("Opening %s.\n", inpolyfilename); + infilename = inpolyfilename; } + // Initialize the default values. - mesh_dim = 3; // Three-dimemsional accoordinates. + mesh_dim = 3; // Three-dimensional coordinates. numberofpointattributes = 0; // no point attribute. markers = 0; // no boundary marker. + uvflag = 0; // no uv parameters (required by a PSC). + // Read number of points, number of dimensions, number of point // attributes, and number of boundary markers. - stringptr = readnumberline(inputline, polyfile, inpolyfilename); + stringptr = readnumberline(inputline, infile, infilename); numberofpoints = (int) strtol (stringptr, &stringptr, 0); stringptr = findnextnumber(stringptr); if (*stringptr != '\0') { @@ -503,49 +880,23 @@ bool tetgenio::load_poly(char* filebasename) if (*stringptr != '\0') { markers = (int) strtol (stringptr, &stringptr, 0); } + if (*stringptr != '\0') { + uvflag = (int) strtol (stringptr, &stringptr, 0); + } + if (numberofpoints > 0) { - readnodefile = 0; - if (smesh) { - infilename = insmeshfilename; - } else { - infilename = inpolyfilename; - } - infile = polyfile; + // Load the list of nodes. + if (!load_node_call(infile, markers, uvflag, infilename)) { + fclose(infile); + return false; + } } else { // If the .poly or .smesh file claims there are zero points, that // means the points should be read from a separate .node file. - readnodefile = 1; - infilename = innodefilename; - } - - if (readnodefile) { - // Read the points from the .node file. - printf("Opening %s.\n", innodefilename); - infile = fopen(innodefilename, "r"); - if (infile == (FILE *) NULL) { - printf("File I/O Error: Cannot access file %s.\n", innodefilename); + if (!load_node(filebasename)) { + fclose(infile); return false; } - // Initialize the default values. - mesh_dim = 3; // Three-dimemsional accoordinates. - numberofpointattributes = 0; // no point attribute. - markers = 0; // no boundary marker. - // Read number of points, number of dimensions, number of point - // attributes, and number of boundary markers. - stringptr = readnumberline(inputline, infile, innodefilename); - numberofpoints = (int) strtol (stringptr, &stringptr, 0); - stringptr = findnextnumber(stringptr); - if (*stringptr != '\0') { - mesh_dim = (int) strtol (stringptr, &stringptr, 0); - } - stringptr = findnextnumber(stringptr); - if (*stringptr != '\0') { - numberofpointattributes = (int) strtol (stringptr, &stringptr, 0); - } - stringptr = findnextnumber(stringptr); - if (*stringptr != '\0') { - markers = (int) strtol (stringptr, &stringptr, 0); - } } if ((mesh_dim != 3) && (mesh_dim != 2)) { @@ -559,27 +910,22 @@ bool tetgenio::load_poly(char* filebasename) return false; } - // Load the list of nodes. - if (!load_node_call(infile, markers, infilename)) { - fclose(infile); - return false; - } - - if (readnodefile) { - fclose(infile); - } - facet *f; polygon *p; if (mesh_dim == 3) { // Read number of facets and number of boundary markers. - stringptr = readnumberline(inputline, polyfile, inpolyfilename); + stringptr = readnumberline(inputline, infile, infilename); + if (stringptr == NULL) { + // No facet list, return. + fclose(infile); + return true; + } numberoffacets = (int) strtol (stringptr, &stringptr, 0); if (numberoffacets <= 0) { // No facet list, return. - fclose(polyfile); + fclose(infile); return true; } stringptr = findnextnumber(stringptr); @@ -604,7 +950,7 @@ bool tetgenio::load_poly(char* filebasename) f->numberofholes = 0; currentmarker = 0; // Read number of polygons, number of holes, and a boundary marker. - stringptr = readnumberline(inputline, polyfile, inpolyfilename); + stringptr = readnumberline(inputline, infile, infilename); f->numberofpolygons = (int) strtol (stringptr, &stringptr, 0); stringptr = findnextnumber(stringptr); if (*stringptr != '\0') { @@ -632,7 +978,7 @@ bool tetgenio::load_poly(char* filebasename) p = &(f->polygonlist[j - 1]); init(p); // Read number of vertices of this polygon. - stringptr = readnumberline(inputline, polyfile, inpolyfilename); + stringptr = readnumberline(inputline, infile, infilename); p->numberofvertices = (int) strtol(stringptr, &stringptr, 0); if (p->numberofvertices < 1) { printf("Error: Wrong polygon %d in facet %d\n", j, i); @@ -646,7 +992,7 @@ bool tetgenio::load_poly(char* filebasename) if (*stringptr == '\0') { // Try to load another non-empty line and continue to read the // rest of vertices. - stringptr = readnumberline(inputline, polyfile, inpolyfilename); + stringptr = readnumberline(inputline, infile, infilename); if (*stringptr == '\0') { printf("Error: Missing %d endpoints of polygon %d in facet %d", p->numberofvertices - k, j, i); @@ -675,7 +1021,7 @@ bool tetgenio::load_poly(char* filebasename) // Read the holes' coordinates. index = 0; for (j = 1; j <= f->numberofholes; j++) { - stringptr = readnumberline(inputline, polyfile, inpolyfilename); + stringptr = readnumberline(inputline, infile, infilename); for (k = 1; k <= 3; k++) { stringptr = findnextnumber(stringptr); if (*stringptr == '\0') { @@ -698,7 +1044,7 @@ bool tetgenio::load_poly(char* filebasename) if (i <= numberoffacets) { // This must be caused by an error. numberoffacets = i - 1; - fclose(polyfile); + fclose(infile); return false; } } else { // poly == 0 @@ -713,7 +1059,7 @@ bool tetgenio::load_poly(char* filebasename) p = &(f->polygonlist[0]); init(p); // Read number of vertices of this polygon. - stringptr = readnumberline(inputline, polyfile, insmeshfilename); + stringptr = readnumberline(inputline, infile, insmeshfilename); p->numberofvertices = (int) strtol (stringptr, &stringptr, 0); if (p->numberofvertices < 1) { printf("Error: Wrong number of vertex in facet %d\n", i); @@ -726,7 +1072,7 @@ bool tetgenio::load_poly(char* filebasename) if (*stringptr == '\0') { // Try to load another non-empty line and continue to read the // rest of vertices. - stringptr = readnumberline(inputline, polyfile, inpolyfilename); + stringptr = readnumberline(inputline, infile, infilename); if (*stringptr == '\0') { printf("Error: Missing %d endpoints in facet %d", p->numberofvertices - k, i); @@ -753,13 +1099,18 @@ bool tetgenio::load_poly(char* filebasename) if (i <= numberoffacets) { // This must be caused by an error. numberoffacets = i - 1; - fclose(polyfile); + fclose(infile); return false; } } // Read the hole section. - stringptr = readnumberline(inputline, polyfile, inpolyfilename); + stringptr = readnumberline(inputline, infile, infilename); + if (stringptr == NULL) { + // No hole list, return. + fclose(infile); + return true; + } if (*stringptr != '\0') { numberofholes = (int) strtol (stringptr, &stringptr, 0); } else { @@ -769,7 +1120,7 @@ bool tetgenio::load_poly(char* filebasename) // Initialize 'holelist'. holelist = new REAL[numberofholes * 3]; for (i = 0; i < 3 * numberofholes; i += 3) { - stringptr = readnumberline(inputline, polyfile, inpolyfilename); + stringptr = readnumberline(inputline, infile, infilename); stringptr = findnextnumber(stringptr); if (*stringptr == '\0') { printf("Error: Hole %d has no x coord.\n", firstnumber + (i / 3)); @@ -794,14 +1145,14 @@ bool tetgenio::load_poly(char* filebasename) } if (i < 3 * numberofholes) { // This must be caused by an error. - fclose(polyfile); + fclose(infile); return false; } } // Read the region section. The 'region' section is optional, if we // don't reach the end-of-file, try read it in. - stringptr = readnumberline(inputline, polyfile, NULL); + stringptr = readnumberline(inputline, infile, NULL); if (stringptr != (char *) NULL && *stringptr != '\0') { numberofregions = (int) strtol (stringptr, &stringptr, 0); } else { @@ -812,7 +1163,7 @@ bool tetgenio::load_poly(char* filebasename) regionlist = new REAL[numberofregions * 5]; index = 0; for (i = 0; i < numberofregions; i++) { - stringptr = readnumberline(inputline, polyfile, inpolyfilename); + stringptr = readnumberline(inputline, infile, infilename); stringptr = findnextnumber(stringptr); if (*stringptr == '\0') { printf("Error: Region %d has no x coordinate.\n", firstnumber + i); @@ -851,7 +1202,7 @@ bool tetgenio::load_poly(char* filebasename) } if (i < numberofregions) { // This must be caused by an error. - fclose(polyfile); + fclose(infile); return false; } } @@ -868,7 +1219,7 @@ bool tetgenio::load_poly(char* filebasename) f = &(facetlist[0]); init(f); // Read number of segments. - stringptr = readnumberline(inputline, polyfile, inpolyfilename); + stringptr = readnumberline(inputline, infile, infilename); // Segments are degenerate polygons. f->numberofpolygons = (int) strtol (stringptr, &stringptr, 0); if (f->numberofpolygons > 0) { @@ -879,7 +1230,7 @@ bool tetgenio::load_poly(char* filebasename) p = &(f->polygonlist[j]); init(p); // Read in a segment. - stringptr = readnumberline(inputline, polyfile, inpolyfilename); + stringptr = readnumberline(inputline, infile, infilename); stringptr = findnextnumber(stringptr); // Skip its index. p->numberofvertices = 2; // A segment always has two vertices. p->vertexlist = new int[p->numberofvertices]; @@ -888,7 +1239,7 @@ bool tetgenio::load_poly(char* filebasename) p->vertexlist[1] = (int) strtol (stringptr, &stringptr, 0); } // Read number of holes. - stringptr = readnumberline(inputline, polyfile, inpolyfilename); + stringptr = readnumberline(inputline, infile, infilename); f->numberofholes = (int) strtol (stringptr, &stringptr, 0); if (f->numberofholes > 0) { // Initialize 'f->holelist'. @@ -896,7 +1247,7 @@ bool tetgenio::load_poly(char* filebasename) // Read the holes' coordinates. for (j = 0; j < f->numberofholes; j++) { // Read a 2D hole point. - stringptr = readnumberline(inputline, polyfile, inpolyfilename); + stringptr = readnumberline(inputline, infile, infilename); stringptr = findnextnumber(stringptr); // Skip its index. f->holelist[j * 3] = (REAL) strtod (stringptr, &stringptr); stringptr = findnextnumber(stringptr); @@ -909,14 +1260,7 @@ bool tetgenio::load_poly(char* filebasename) } // End of reading poly/smesh file. - fclose(polyfile); - - // Try to load a .var file if it exists. - load_var(filebasename); - - // Try to load a .mtr file if it exists. - load_mtr(filebasename); - + fclose(infile); return true; } @@ -944,6 +1288,10 @@ bool tetgenio::load_off(char* filebasename) int nedges = 0; int line_count = 0, i; + // Default, the off file's index is from '0'. We check it by remembering the + // smallest index we found in the file. It should be either 0 or 1. + int smallestidx = 0; + strncpy(infilename, filebasename, 1024 - 1); infilename[FILENAMESIZE - 1] = '\0'; if (infilename[0] == '\0') { @@ -955,14 +1303,11 @@ bool tetgenio::load_off(char* filebasename) } if (!(fp = fopen(infilename, "r"))) { - printf("File I/O Error: Unable to open file %s\n", infilename); + printf(" Unable to open file %s\n", infilename); return false; } printf("Opening %s.\n", infilename); - // OFF requires the index starts from '0'. - firstnumber = 0; - while ((bufferp = readline(buffer, fp, &line_count)) != NULL) { // Check section if (nverts == 0) { @@ -986,6 +1331,7 @@ bool tetgenio::load_off(char* filebasename) if (nverts > 0) { numberofpoints = nverts; pointlist = new REAL[nverts * 3]; + smallestidx = nverts + 1; // A bigger enough number. } if (nfaces > 0) { numberoffacets = nfaces; @@ -1034,6 +1380,10 @@ bool tetgenio::load_off(char* filebasename) return false; } p->vertexlist[i] = (int) strtol(bufferp, &bufferp, 0); + // Detect the smallest index. + if (p->vertexlist[i] < smallestidx) { + smallestidx = p->vertexlist[i]; + } } ifaces++; } else { @@ -1047,14 +1397,22 @@ bool tetgenio::load_off(char* filebasename) // Close file fclose(fp); - // Check whether read all points + // Decide the firstnumber of the index. + if (smallestidx == 0) { + firstnumber = 0; + } else if (smallestidx == 1) { + firstnumber = 1; + } else { + printf("A wrong smallest index (%d) was detected in file %s\n", + smallestidx, infilename); + return false; + } + if (iverts != nverts) { printf("Expected %d vertices, but read only %d vertices in file %s\n", nverts, iverts, infilename); return false; } - - // Check whether read all faces if (ifaces != nfaces) { printf("Expected %d faces, but read only %d faces in file %s\n", nfaces, ifaces, infilename); @@ -1092,6 +1450,10 @@ bool tetgenio::load_ply(char* filebasename) int nfaces = 0, ifaces = 0; int line_count = 0, i; + // Default, the ply file's index is from '0'. We check it by remembering the + // smallest index we found in the file. It should be either 0 or 1. + int smallestidx = 0; + strncpy(infilename, filebasename, FILENAMESIZE - 1); infilename[FILENAMESIZE - 1] = '\0'; if (infilename[0] == '\0') { @@ -1108,9 +1470,6 @@ bool tetgenio::load_ply(char* filebasename) } printf("Opening %s.\n", infilename); - // PLY requires the index starts from '0'. - firstnumber = 0; - while ((bufferp = readline(buffer, fp, &line_count)) != NULL) { if (!endheader) { // Find if it is the keyword "end_header". @@ -1153,6 +1512,7 @@ bool tetgenio::load_ply(char* filebasename) if (nverts > 0) { numberofpoints = nverts; pointlist = new REAL[nverts * 3]; + smallestidx = nverts + 1; // A big enough index. } } } @@ -1240,6 +1600,9 @@ bool tetgenio::load_ply(char* filebasename) return false; } p->vertexlist[i] = (int) strtol(bufferp, &bufferp, 0); + if (p->vertexlist[i] < smallestidx) { + smallestidx = p->vertexlist[i]; + } } ifaces++; } else { @@ -1253,14 +1616,22 @@ bool tetgenio::load_ply(char* filebasename) // Close file fclose(fp); - // Check whether read all points + // Decide the firstnumber of the index. + if (smallestidx == 0) { + firstnumber = 0; + } else if (smallestidx == 1) { + firstnumber = 1; + } else { + printf("A wrong smallest index (%d) was detected in file %s\n", + smallestidx, infilename); + return false; + } + if (iverts != nverts) { printf("Expected %d vertices, but read only %d vertices in file %s\n", nverts, iverts, infilename); return false; } - - // Check whether read all faces if (ifaces != nfaces) { printf("Expected %d faces, but read only %d faces in file %s\n", nfaces, ifaces, infilename); @@ -1287,7 +1658,7 @@ bool tetgenio::load_ply(char* filebasename) bool tetgenio::load_stl(char* filebasename) { FILE *fp; - tetgenmesh::list *plist; + tetgenmesh::arraypool *plist; tetgenio::facet *f; tetgenio::polygon *p; char infilename[FILENAMESIZE]; @@ -1316,7 +1687,7 @@ bool tetgenio::load_stl(char* filebasename) printf("Opening %s.\n", infilename); // STL file has no number of points available. Use a list to read points. - plist = new tetgenmesh::list(sizeof(double) * 3, NULL, 1024); + plist = new tetgenmesh::arraypool(sizeof(double) * 3, 10); while ((bufferp = readline(buffer, fp, &line_count)) != NULL) { // The ASCII .stl file must start with the lower case keyword solid and @@ -1339,7 +1710,7 @@ bool tetgenio::load_stl(char* filebasename) bufferp = str; bufferp = strstr(bufferp, "vertex"); if (bufferp != NULL) { - coord = (double *) plist->append(NULL); + plist->newindex((void **) &coord); for (i = 0; i < 3; i++) { bufferp = findnextnumber(bufferp); if (*bufferp == '\0') { @@ -1357,7 +1728,7 @@ bool tetgenio::load_stl(char* filebasename) } fclose(fp); - nverts = plist->len(); + nverts = (int) plist->objects; // nverts should be an integer times 3 (every 3 vertices denote a face). if (nverts == 0 || (nverts % 3 != 0)) { printf("Error: Wrong number of vertices in file %s.\n", infilename); @@ -1367,7 +1738,7 @@ bool tetgenio::load_stl(char* filebasename) numberofpoints = nverts; pointlist = new REAL[nverts * 3]; for (i = 0; i < nverts; i++) { - coord = (double *) (* plist)[i]; + coord = (double *) fastlookup(plist, i); iverts = i * 3; pointlist[iverts] = (REAL) coord[0]; pointlist[iverts + 1] = (REAL) coord[1]; @@ -1409,12 +1780,9 @@ bool tetgenio::load_stl(char* filebasename) // The .mesh format is the file format of Medit, a user-friendly interactive // // mesh viewer 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* filebasename, int istetmesh) { FILE *fp; tetgenio::facet *tmpflist, *f; @@ -1427,10 +1795,14 @@ bool tetgenio::load_medit(char* filebasename) int dimension = 0; int nverts = 0; int nfaces = 0; + int ntets = 0; int line_count = 0; int corners = 0; // 3 (triangle) or 4 (quad). + int *plist; int i, j; + int smallestidx = 0; + strncpy(infilename, filebasename, FILENAMESIZE - 1); infilename[FILENAMESIZE - 1] = '\0'; if (infilename[0] == '\0') { @@ -1447,9 +1819,6 @@ bool tetgenio::load_medit(char* filebasename) } printf("Opening %s.\n", infilename); - // Default uses the index starts from '1'. - firstnumber = 1; - while ((bufferp = readline(buffer, fp, &line_count)) != NULL) { if (*bufferp == '#') continue; // A comment line is skipped. if (dimension == 0) { @@ -1487,6 +1856,8 @@ bool tetgenio::load_medit(char* filebasename) bufferp = readline(buffer, fp, &line_count); } nverts = (int) strtol(bufferp, &bufferp, 0); + // Initialize the smallest index. + smallestidx = nverts + 1; // Allocate memory for 'tetgenio' if (nverts > 0) { numberofpoints = nverts; @@ -1521,7 +1892,63 @@ bool tetgenio::load_medit(char* filebasename) } continue; } - } + } + if (ntets == 0) { + // Find if it is the keyword "Tetrahedra" + corners = 0; + str = strstr(bufferp, "Tetrahedra"); + if (!str) str = strstr(bufferp, "tetrahedra"); + if (!str) str = strstr(bufferp, "TETRAHEDRA"); + if (str) { + corners = 4; + } + if (corners == 4) { + // Read the number of tetrahedra + bufferp = findnextnumber(str); // Skip field "Tetrahedra". + if (*bufferp == '\0') { + // Read a non-empty line. + bufferp = readline(buffer, fp, &line_count); + } + ntets = strtol(bufferp, &bufferp, 0); + if (ntets > 0) { + // It is a tetrahedral mesh. + numberoftetrahedra = ntets; + numberofcorners = 4; + numberoftetrahedronattributes = 1; + tetrahedronlist = new int[ntets * 4]; + tetrahedronattributelist = new REAL[ntets]; + } + } // if (corners == 4) + // Read the list of tetrahedra. + for (i = 0; i < numberoftetrahedra; i++) { + plist = &(tetrahedronlist[i * 4]); + bufferp = readline(buffer, 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 the vertices of the tet. + for (j = 0; j < corners; j++) { + if (*bufferp == '\0') { + printf("Syntax error reading face on line %d in file %s\n", + line_count, infilename); + fclose(fp); + return false; + } + plist[j] = (int) strtol(bufferp, &bufferp, 0); + // Remember the smallest index. + if (plist[j] < smallestidx) smallestidx = plist[j]; + bufferp = findnextnumber(bufferp); + } + // Read the attribute of the tet if it exists. + tetrahedronattributelist[i] = 0; + if (*bufferp != '\0') { + tetrahedronattributelist[i] = (REAL) strtol(bufferp, &bufferp, 0); + } + } // i + } // Tetrahedra if (nfaces == 0) { // Find if it is the keyword "Triangles" or "Quadrilaterals". corners = 0; @@ -1548,83 +1975,137 @@ bool tetgenio::load_medit(char* filebasename) nfaces = strtol(bufferp, &bufferp, 0); // Allocate memory for 'tetgenio' if (nfaces > 0) { - if (numberoffacets > 0) { - // facetlist has already been allocated. Enlarge arrays. - tmpflist = new tetgenio::facet[numberoffacets + nfaces]; - tmpfmlist = new int[numberoffacets + nfaces]; - // Copy the data of old arrays into new arrays. - for (i = 0; i < numberoffacets; i++) { - f = &(tmpflist[i]); - tetgenio::init(f); - *f = facetlist[i]; - tmpfmlist[i] = facetmarkerlist[i]; + if (!istetmesh) { + // It is a PLC surface mesh. + if (numberoffacets > 0) { + // facetlist has already been allocated. Enlarge arrays. + // This happens when the surface mesh contains mixed cells. + tmpflist = new tetgenio::facet[numberoffacets + nfaces]; + tmpfmlist = new int[numberoffacets + nfaces]; + // Copy the data of old arrays into new arrays. + for (i = 0; i < numberoffacets; i++) { + f = &(tmpflist[i]); + tetgenio::init(f); + *f = facetlist[i]; + tmpfmlist[i] = facetmarkerlist[i]; + } + // Release old arrays. + delete [] facetlist; + delete [] facetmarkerlist; + // Remember the new arrays. + facetlist = tmpflist; + facetmarkerlist = tmpfmlist; + } else { + // This is the first time to allocate facetlist. + facetlist = new tetgenio::facet[nfaces]; + facetmarkerlist = new int[nfaces]; } - // Release old arrays. - delete [] facetlist; - delete [] facetmarkerlist; - // Remember the new arrays. - facetlist = tmpflist; - facetmarkerlist = tmpfmlist; } else { - // This is the first time to allocate facetlist. - facetlist = new tetgenio::facet[nfaces]; - facetmarkerlist = new int[nfaces]; + if (corners == 3) { + // It is a surface mesh of a tetrahedral mesh. + numberoftrifaces = nfaces; + trifacelist = new int[nfaces * 3]; + trifacemarkerlist = new int[nfaces]; + } } - } + } // if (nfaces > 0) // Read the following list of faces. - for (i = numberoffacets; i < numberoffacets + nfaces; i++) { - bufferp = readline(buffer, 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; - } - f = &facetlist[i]; - tetgenio::init(f); - // In .mesh format, each facet has one polygon, no hole. - f->numberofpolygons = 1; - f->polygonlist = new tetgenio::polygon[1]; - p = &f->polygonlist[0]; - tetgenio::init(p); - p->numberofvertices = corners; - // Allocate memory for face vertices - p->vertexlist = new int[p->numberofvertices]; - // Read the vertices of the face. - for (j = 0; j < corners; j++) { - if (*bufferp == '\0') { - printf("Syntax error reading face on line %d in file %s\n", + if (!istetmesh) { + for (i = numberoffacets; i < numberoffacets + nfaces; i++) { + bufferp = readline(buffer, 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; } - p->vertexlist[j] = (int) strtol(bufferp, &bufferp, 0); - if (firstnumber == 1) { - // Check if a '0' index appears. - if (p->vertexlist[j] == 0) { - // The first index is set to be 0. - firstnumber = 0; + f = &facetlist[i]; + tetgenio::init(f); + // In .mesh format, each facet has one polygon, no hole. + f->numberofpolygons = 1; + f->polygonlist = new tetgenio::polygon[1]; + p = &f->polygonlist[0]; + tetgenio::init(p); + p->numberofvertices = corners; + // Allocate memory for face vertices + p->vertexlist = new int[p->numberofvertices]; + // Read the vertices of the face. + for (j = 0; j < corners; j++) { + if (*bufferp == '\0') { + printf("Syntax error reading face on line %d in file %s\n", + line_count, infilename); + fclose(fp); + return false; + } + p->vertexlist[j] = (int) strtol(bufferp, &bufferp, 0); + // Remember the smallest index. + if (p->vertexlist[j] < smallestidx) { + smallestidx = p->vertexlist[j]; } + bufferp = findnextnumber(bufferp); + } + // Read the marker of the face if it exists. + facetmarkerlist[i] = 0; + if (*bufferp != '\0') { + facetmarkerlist[i] = (int) strtol(bufferp, &bufferp, 0); } - bufferp = findnextnumber(bufferp); - } - // Read the marker of the face if it exists. - facetmarkerlist[i] = 0; - if (*bufferp != '\0') { - facetmarkerlist[i] = (int) strtol(bufferp, &bufferp, 0); } - } - // Have read in a list of triangles/quads. - numberoffacets += nfaces; - nfaces = 0; - } + // Have read in a list of triangles/quads. + numberoffacets += nfaces; + nfaces = 0; + } else { + // It is a surface mesh of a tetrahedral mesh. + if (corners == 3) { + for (i = 0; i < numberoftrifaces; i++) { + plist = &(trifacelist[i * 3]); + bufferp = readline(buffer, 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 the vertices of the face. + for (j = 0; j < corners; j++) { + if (*bufferp == '\0') { + printf("Syntax error reading face on line %d in file %s\n", + line_count, infilename); + fclose(fp); + return false; + } + plist[j] = (int) strtol(bufferp, &bufferp, 0); + // Remember the smallest index. + if (plist[j] < smallestidx) { + smallestidx = plist[j]; + } + bufferp = findnextnumber(bufferp); + } + // Read the marker of the face if it exists. + trifacemarkerlist[i] = 0; + if (*bufferp != '\0') { + trifacemarkerlist[i] = (int) strtol(bufferp, &bufferp, 0); + } + } // i + } // if (corners == 3) + } // if (b->refine) + } // if (corners == 3 || corners == 4) } - // if (nverts > 0 && nfaces > 0) break; // Ignore other data. } // Close file fclose(fp); + // Decide the firstnumber of the index. + if (smallestidx == 0) { + firstnumber = 0; + } else if (smallestidx == 1) { + firstnumber = 1; + } else { + printf("A wrong smallest index (%d) was detected in file %s\n", + smallestidx, infilename); + return false; + } + return true; } @@ -1632,11 +2113,35 @@ 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, // +// This function is contributed by: Bryn Lloyd, Computer Vision Laboratory, // // ETH, Zuerich. May 7, 2007. // // // /////////////////////////////////////////////////////////////////////////////// +// Two inline functions used in read/write VTK files. + +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--; + } +} + +bool testIsBigEndian() +{ + short word = 0x4321; + if((*(char *)& word) != 0x21) + return true; + else + return false; +} + + bool tetgenio::load_vtk(char* filebasename) { FILE *fp; @@ -1658,6 +2163,8 @@ bool tetgenio::load_vtk(char* filebasename) int i, j; bool ImALittleEndian = !testIsBigEndian(); + int smallestidx = 0; + strncpy(infilename, filebasename, FILENAMESIZE - 1); infilename[FILENAMESIZE - 1] = '\0'; if (infilename[0] == '\0') { @@ -1693,6 +2200,7 @@ bool tetgenio::load_vtk(char* filebasename) if (nverts > 0) { numberofpoints = nverts; pointlist = new REAL[nverts * 3]; + smallestidx = nverts + 1; } if(!strcmp(mode, "BINARY")) { @@ -1793,6 +2301,12 @@ bool tetgenio::load_vtk(char* filebasename) p->vertexlist[0] = id1; p->vertexlist[1] = id2; p->vertexlist[2] = id3; + // Detect the smallest index. + for (j = 0; j < 3; j++) { + if (p->vertexlist[j] < smallestidx) { + smallestidx = p->vertexlist[j]; + } + } } else { printf("Error: Only triangles are supported\n"); return false; @@ -1830,6 +2344,12 @@ bool tetgenio::load_vtk(char* filebasename) p->vertexlist[0] = id1; p->vertexlist[1] = id2; p->vertexlist[2] = id3; + // Detect the smallest index. + for (j = 0; j < 3; j++) { + if (p->vertexlist[j] < smallestidx) { + smallestidx = p->vertexlist[j]; + } + } } else { printf("Error: Only triangles are supported.\n"); return false; @@ -1838,6 +2358,18 @@ bool tetgenio::load_vtk(char* filebasename) } fclose(fp); + + // Decide the firstnumber of the index. + if (smallestidx == 0) { + firstnumber = 0; + } else if (smallestidx == 1) { + firstnumber = 1; + } else { + printf("A wrong smallest index (%d) was detected in file %s\n", + smallestidx, infilename); + return false; + } + return true; } @@ -1853,416 +2385,126 @@ bool tetgenio::load_vtk(char* filebasename) // // // load_plc() Load a piecewise linear complex from file(s). // // // -// 'object' indicates which file format is used to describ the plc. // -// // /////////////////////////////////////////////////////////////////////////////// bool tetgenio::load_plc(char* filebasename, int object) { - enum tetgenbehavior::objecttype type; - - type = (enum tetgenbehavior::objecttype) object; - switch (type) { - case tetgenbehavior::NODES: - return load_node(filebasename); - case tetgenbehavior::POLY: - return load_poly(filebasename); - case tetgenbehavior::OFF: - return load_off(filebasename); - case tetgenbehavior::PLY: - return load_ply(filebasename); - case tetgenbehavior::STL: - return load_stl(filebasename); - case tetgenbehavior::MEDIT: - return load_medit(filebasename); - case tetgenbehavior::VTK: - return load_vtk(filebasename); - default: - return load_poly(filebasename); + bool success; + + if (object == (int) tetgenbehavior::NODES) { + success = load_node(filebasename); + } else if (object == (int) tetgenbehavior::POLY) { + success = load_poly(filebasename); + } else if (object == (int) tetgenbehavior::OFF) { + success = load_off(filebasename); + } else if (object == (int) tetgenbehavior::PLY) { + success = load_ply(filebasename); + } else if (object == (int) tetgenbehavior::STL) { + success = load_stl(filebasename); + } else if (object == (int) tetgenbehavior::MEDIT) { + success = load_medit(filebasename, 0); + } else if (object == (int) tetgenbehavior::VTK) { + success = load_vtk(filebasename); + } else { + success = load_poly(filebasename); } + + if (success) { + // Try to load the following files (.edge, .var, .mtr). + load_edge(filebasename); + load_var(filebasename); + load_mtr(filebasename); + } + + return success; } /////////////////////////////////////////////////////////////////////////////// // // -// load_tetmesh() Load a tetrahedral mesh from files. // +// load_mesh() Load a tetrahedral mesh from file(s). // // // /////////////////////////////////////////////////////////////////////////////// -bool tetgenio::load_tetmesh(char* filebasename) +bool tetgenio::load_tetmesh(char* filebasename, int object) { - FILE *infile; - char innodefilename[FILENAMESIZE]; - char inelefilename[FILENAMESIZE]; - char infacefilename[FILENAMESIZE]; - char inedgefilename[FILENAMESIZE]; - char involfilename[FILENAMESIZE]; - char inputline[INPUTLINESIZE]; - char *stringptr, *infilename; - REAL attrib, volume; - int volelements; - int markers, corner; - int index, attribindex; - int i, j; + bool success; - // Assembling the actual file names we want to open. - strcpy(innodefilename, filebasename); - strcpy(inelefilename, filebasename); - strcpy(infacefilename, filebasename); - strcpy(inedgefilename, filebasename); - strcpy(involfilename, filebasename); - strcat(innodefilename, ".node"); - strcat(inelefilename, ".ele"); - strcat(infacefilename, ".face"); - strcat(inedgefilename, ".edge"); - strcat(involfilename, ".vol"); + if (object == (int) tetgenbehavior::MEDIT) { + success = load_medit(filebasename, 1); + } else { + success = load_node(filebasename); + if (success) { + success = load_tet(filebasename); + } + if (success) { + // Try to load the following files (.face, .edge, .vol). + load_face(filebasename); + load_edge(filebasename); + load_vol(filebasename); + } + } - // Read the points from a .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; + if (success) { + // Try to load the following files (.var, .mtr). + load_var(filebasename); + load_mtr(filebasename); } - // 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; - numberofpoints = (int) strtol (stringptr, &stringptr, 0); - stringptr = findnextnumber(stringptr); - if (*stringptr == '\0') { - mesh_dim = 3; + + return success; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// save_nodes() Save points to a .node file. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenio::save_nodes(char* filebasename) +{ + FILE *fout; + char outnodefilename[FILENAMESIZE]; + char outmtrfilename[FILENAMESIZE]; + 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); + for (i = 0; i < numberofpoints; i++) { + if (mesh_dim == 2) { + fprintf(fout, "%d %.16g %.16g", i + firstnumber, pointlist[i * 3], + pointlist[i * 3 + 1]); } else { - mesh_dim = (int) strtol (stringptr, &stringptr, 0); + fprintf(fout, "%d %.16g %.16g %.16g", i + firstnumber, + pointlist[i * 3], pointlist[i * 3 + 1], pointlist[i * 3 + 2]); } - stringptr = findnextnumber(stringptr); - if (*stringptr == '\0') { - numberofpointattributes = 0; - } else { - numberofpointattributes = (int) strtol (stringptr, &stringptr, 0); + for (j = 0; j < numberofpointattributes; j++) { + fprintf(fout, " %.16g", + pointattributelist[i * numberofpointattributes + j]); } - stringptr = findnextnumber(stringptr); - if (*stringptr == '\0') { - markers = 0; // Default value. - } else { - markers = (int) strtol (stringptr, &stringptr, 0); + if (pointmarkerlist != NULL) { + fprintf(fout, " %d", pointmarkerlist[i]); } - } 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); - numberofpoints = (int) strtol (stringptr, &stringptr, 0); - // There is no index column. - useindex = 0; + fprintf(fout, "\n"); } + fclose(fout); - // Load the list of nodes. - if (!load_node_call(infile, markers, infilename)) { - fclose(infile); - return false; + // If the point metrics exist, output them to a .mtr file. + if ((numberofpointmtrs > 0) && (pointmtrlist != (REAL *) NULL)) { + sprintf(outmtrfilename, "%s.mtr", filebasename); + printf("Saving metrics to %s\n", outmtrfilename); + fout = fopen(outmtrfilename, "w"); + fprintf(fout, "%d %d\n", numberofpoints, numberofpointmtrs); + for (i = 0; i < numberofpoints; i++) { + for (j = 0; j < numberofpointmtrs; j++) { + fprintf(fout, "%.16g ", pointmtrlist[i * numberofpointmtrs + j]); + } + fprintf(fout, "\n"); + } + fclose(fout); } - fclose(infile); - - // Read the elements from an .ele file. - if (mesh_dim == 3) { - infilename = inelefilename; - infile = fopen(infilename, "r"); - if (infile != (FILE *) NULL) { - printf("Opening %s.\n", infilename); - // Read number of elements, number of corners (4 or 10), number of - // element attributes. - stringptr = readnumberline(inputline, infile, infilename); - numberoftetrahedra = (int) strtol (stringptr, &stringptr, 0); - stringptr = findnextnumber(stringptr); - if (*stringptr == '\0') { - numberofcorners = 4; // Default read 4 nodes per element. - } else { - numberofcorners = (int) strtol(stringptr, &stringptr, 0); - } - stringptr = findnextnumber(stringptr); - if (*stringptr == '\0') { - numberoftetrahedronattributes = 0; // Default no attribute. - } else { - numberoftetrahedronattributes = (int) strtol(stringptr, &stringptr, 0); - } - if (numberofcorners != 4 && numberofcorners != 10) { - printf("Error: Wrong number of corners %d (should be 4 or 10).\n", - numberofcorners); - fclose(infile); - return false; - } - // Allocate memory for tetrahedra. - if (numberoftetrahedra > 0) { - tetrahedronlist = new int[numberoftetrahedra * numberofcorners]; - if (tetrahedronlist == (int *) NULL) { - terminatetetgen(1); - } - // Allocate memory for output tetrahedron attributes if necessary. - if (numberoftetrahedronattributes > 0) { - tetrahedronattributelist = new REAL[numberoftetrahedra * - numberoftetrahedronattributes]; - if (tetrahedronattributelist == (REAL *) NULL) { - terminatetetgen(1); - } - } - } - // Read the list of tetrahedra. - index = 0; - attribindex = 0; - for (i = 0; i < numberoftetrahedra; i++) { - // Read tetrahedron index and the tetrahedron's corners. - stringptr = readnumberline(inputline, infile, infilename); - for (j = 0; j < numberofcorners; j++) { - stringptr = findnextnumber(stringptr); - if (*stringptr == '\0') { - printf("Error: Tetrahedron %d is missing vertex %d in %s.\n", - i + firstnumber, j + 1, infilename); - terminatetetgen(1); - } - corner = (int) strtol(stringptr, &stringptr, 0); - if (corner < firstnumber || corner >= numberofpoints + firstnumber) { - printf("Error: Tetrahedron %d has an invalid vertex index.\n", - i + firstnumber); - terminatetetgen(1); - } - tetrahedronlist[index++] = corner; - } - // Read the tetrahedron's attributes. - for (j = 0; j < numberoftetrahedronattributes; j++) { - stringptr = findnextnumber(stringptr); - if (*stringptr == '\0') { - attrib = 0.0; - } else { - attrib = (REAL) strtod(stringptr, &stringptr); - } - tetrahedronattributelist[attribindex++] = attrib; - } - } - fclose(infile); - } - } // if (meshdim == 3) - - // Read the hullfaces or subfaces from a .face file if it exists. - if (mesh_dim == 3) { - infilename = infacefilename; - } else { - infilename = inelefilename; - } - infile = fopen(infilename, "r"); - if (infile != (FILE *) NULL) { - printf("Opening %s.\n", infilename); - // Read number of faces, boundary markers. - stringptr = readnumberline(inputline, infile, infilename); - numberoftrifaces = (int) strtol (stringptr, &stringptr, 0); - stringptr = findnextnumber(stringptr); - if (mesh_dim == 2) { - // Skip a number. - stringptr = findnextnumber(stringptr); - } - if (*stringptr == '\0') { - markers = 0; // Default there is no marker per face. - } else { - markers = (int) strtol (stringptr, &stringptr, 0); - } - if (numberoftrifaces > 0) { - trifacelist = new int[numberoftrifaces * 3]; - if (trifacelist == (int *) NULL) { - terminatetetgen(1); - } - if (markers) { - trifacemarkerlist = new int[numberoftrifaces]; - if (trifacemarkerlist == (int *) NULL) { - terminatetetgen(1); - } - } - } - // Read the list of faces. - index = 0; - for (i = 0; i < numberoftrifaces; i++) { - // Read face index and the face's three corners. - stringptr = readnumberline(inputline, infile, infilename); - for (j = 0; j < 3; j++) { - stringptr = findnextnumber(stringptr); - if (*stringptr == '\0') { - printf("Error: Face %d is missing vertex %d in %s.\n", - i + firstnumber, j + 1, infilename); - terminatetetgen(1); - } - corner = (int) strtol(stringptr, &stringptr, 0); - if (corner < firstnumber || corner >= numberofpoints + firstnumber) { - printf("Error: Face %d has an invalid vertex index.\n", - i + firstnumber); - terminatetetgen(1); - } - trifacelist[index++] = corner; - } - // Read the boundary marker if it exists. - if (markers) { - stringptr = findnextnumber(stringptr); - if (*stringptr == '\0') { - attrib = 0.0; - } else { - attrib = (REAL) strtod(stringptr, &stringptr); - } - trifacemarkerlist[i] = (int) attrib; - } - } - fclose(infile); - } - - // Read the boundary edges from a .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); - numberofedges = (int) strtol (stringptr, &stringptr, 0); - if (numberofedges > 0) { - edgelist = new int[numberofedges * 2]; - if (edgelist == (int *) NULL) { - 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; - for (i = 0; i < numberofedges; i++) { - // Read face index and the edge's two endpoints. - stringptr = readnumberline(inputline, infile, infilename); - 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); - if (corner < firstnumber || corner >= numberofpoints + firstnumber) { - printf("Error: Edge %d has an invalid vertex index.\n", - i + firstnumber); - terminatetetgen(1); - } - edgelist[index++] = corner; - } - // Read the edge marker if it has. - if (markers) { - stringptr = findnextnumber(stringptr); - edgemarkerlist[i] = (int) strtol(stringptr, &stringptr, 0); - } - } - fclose(infile); - } - - // Read the volume constraints from a .vol file if it exists. - infilename = involfilename; - infile = fopen(infilename, "r"); - if (infile != (FILE *) NULL) { - printf("Opening %s.\n", infilename); - // Read number of tetrahedra. - stringptr = readnumberline(inputline, infile, infilename); - volelements = (int) strtol (stringptr, &stringptr, 0); - if (volelements != numberoftetrahedra) { - printf("Warning: %s and %s disagree on number of tetrahedra.\n", - inelefilename, involfilename); - volelements = 0; - } - if (volelements > 0) { - tetrahedronvolumelist = new REAL[volelements]; - if (tetrahedronvolumelist == (REAL *) NULL) { - terminatetetgen(1); - } - } - // Read the list of volume constraints. - for (i = 0; i < volelements; i++) { - stringptr = readnumberline(inputline, infile, infilename); - stringptr = findnextnumber(stringptr); - if (*stringptr == '\0') { - volume = -1.0; // No constraint on this tetrahedron. - } else { - volume = (REAL) strtod(stringptr, &stringptr); - } - tetrahedronvolumelist[i] = volume; - } - fclose(infile); - } - - // Try to load a .mtr file if it exists. - load_mtr(filebasename); - - // Try to read a .pbc file if it exists. - // load_pbc(filebasename); - - return true; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// save_nodes() Save points to a .node file. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenio::save_nodes(char* filebasename) -{ - FILE *fout; - char outnodefilename[FILENAMESIZE]; - char outmtrfilename[FILENAMESIZE]; - 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); - for (i = 0; i < numberofpoints; i++) { - if (mesh_dim == 2) { - fprintf(fout, "%d %.16g %.16g", i + firstnumber, pointlist[i * 3], - pointlist[i * 3 + 1]); - } else { - fprintf(fout, "%d %.16g %.16g %.16g", i + firstnumber, - pointlist[i * 3], pointlist[i * 3 + 1], pointlist[i * 3 + 2]); - } - for (j = 0; j < numberofpointattributes; j++) { - fprintf(fout, " %.16g", - pointattributelist[i * numberofpointattributes + j]); - } - if (pointmarkerlist != NULL) { - fprintf(fout, " %d", pointmarkerlist[i]); - } - fprintf(fout, "\n"); - } - fclose(fout); - - // If the point metrics exist, output them to a .mtr file. - if ((numberofpointmtrs > 0) && (pointmtrlist != (REAL *) NULL)) { - sprintf(outmtrfilename, "%s.mtr", filebasename); - printf("Saving metrics to %s\n", outmtrfilename); - fout = fopen(outmtrfilename, "w"); - fprintf(fout, "%d %d\n", numberofpoints, numberofpointmtrs); - for (i = 0; i < numberofpoints; i++) { - for (j = 0; j < numberofpointmtrs; j++) { - fprintf(fout, "%.16g ", pointmtrlist[i * numberofpointmtrs + j]); - } - fprintf(fout, "\n"); - } - fclose(fout); - } -} +} /////////////////////////////////////////////////////////////////////////////// // // @@ -2499,6 +2741,52 @@ void tetgenio::save_poly(char* filebasename) fclose(fout); } +/////////////////////////////////////////////////////////////////////////////// +// // +// save_faces2smesh() Save triangular faces to a .smesh file. // +// // +// It only save the facets. No holes and regions. No .node file. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenio::save_faces2smesh(char* filebasename) +{ + FILE *fout; + char outsmeshfilename[FILENAMESIZE]; + int i, j; + + sprintf(outsmeshfilename, "%s.smesh", filebasename); + printf("Saving faces to %s\n", outsmeshfilename); + fout = fopen(outsmeshfilename, "w"); + + // The zero indicates that the vertices are in a separate .node file. + // Followed by number of dimensions, number of vertex attributes, + // and number of boundary markers (zero or one). + fprintf(fout, "%d %d %d %d\n", 0, mesh_dim, numberofpointattributes, + pointmarkerlist != NULL ? 1 : 0); + + // Number of facets, number of boundary markers (zero or one). + fprintf(fout, "%d %d\n", numberoftrifaces, + trifacemarkerlist != NULL ? 1 : 0); + + // Output triangular facets. + for (i = 0; i < numberoftrifaces; i++) { + j = i * 3; + fprintf(fout, "3 %d %d %d", trifacelist[j], trifacelist[j + 1], + trifacelist[j + 2]); + if (trifacemarkerlist != NULL) { + fprintf(fout, " %d", trifacemarkerlist[i]); + } + fprintf(fout, "\n"); + } + + // No holes and regions. + fprintf(fout, "0\n"); + fprintf(fout, "0\n"); + + fclose(fout); +} + /////////////////////////////////////////////////////////////////////////////// // // // readline() Read a nonempty line from a file. // @@ -2525,7 +2813,7 @@ char* tetgenio::readline(char *string, FILE *infile, int *linenumber) // Skip white spaces. while ((*result == ' ') || (*result == '\t')) result++; // If it's end of line, read another line and try again. - } while (*result == '\0'); + } while ((*result == '\0') || (*result == '\r') || (*result == '\n')); return result; } @@ -2574,10 +2862,6 @@ char* tetgenio::readnumberline(char *string, FILE *infile, char *infilename) do { result = fgets(string, INPUTLINESIZE, infile); if (result == (char *) NULL) { - if (infilename != (char *) NULL) { - printf(" Error: Unexpected end of file in %s.\n", infilename); - terminatetetgen(1); - } return result; } // Skip anything that doesn't look like a number, a comment, @@ -2642,33 +2926,36 @@ char* tetgenio::findnextnumber(char *string) void tetgenbehavior::syntax() { - printf(" tetgen [-prq_a_AiMYS_T_dzo_fenvgGOJBNEFICQVh] input_file\n"); + printf(" tetgen [-pYrq_Aa_miO_S_T_XMwcdzfenvgkJBNEFICQVh] input_file\n"); printf(" -p Tetrahedralizes a piecewise linear complex (PLC).\n"); + printf(" -Y Preserves the input surface mesh (does not modify it).\n"); printf(" -r Reconstructs a previously generated mesh.\n"); printf(" -q Refines mesh (to improve mesh quality).\n"); - printf(" -a Applies a maximum tetrahedron volume constraint.\n"); + printf(" -R Mesh coarsening (to reduce the mesh elements).\n"); printf(" -A Assigns attributes to tetrahedra in different 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(" -a Applies a maximum tetrahedron volume constraint.\n"); + printf(" -m Applies a mesh sizing function.\n"); + printf(" -i Inserts a list of additional points.\n"); + printf(" -O Specifies the level of mesh optimization.\n"); printf(" -S Specifies maximum number of added points.\n"); printf(" -T Sets a tolerance for coplanar test (default 1e-8).\n"); + printf(" -X Suppresses use of exact arithmetic.\n"); + printf(" -M No merge of coplanar facets or very close vertices.\n"); + printf(" -w Generates weighted Delaunay (regular) triangulation.\n"); + printf(" -c Retains the convex hull of the PLC.\n"); printf(" -d Detects self-intersections of facets of the PLC.\n"); printf(" -z Numbers all output items starting from zero.\n"); - printf(" -o2 Generates second-order subparametric elements.\n"); printf(" -f Outputs all faces to .face 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(" -k Outputs mesh to .vtk file for viewing by Paraview.\n"); printf(" -J No jettison of unused vertices from output .node file.\n"); printf(" -B Suppresses output of boundary information.\n"); printf(" -N Suppresses output of .node file.\n"); printf(" -E Suppresses output of .ele file.\n"); - printf(" -F Suppresses output of .face file.\n"); + printf(" -F Suppresses output of .face and .edge file.\n"); printf(" -I Suppresses mesh iteration numbers.\n"); printf(" -C Checks the consistency of the final mesh.\n"); printf(" -Q Quiet: No terminal output except errors.\n"); @@ -2687,25 +2974,17 @@ 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"); + printf("Version 1.5\n"); + printf("November 4, 2013\n"); printf("\n"); printf("What Can TetGen Do?\n"); printf("\n"); - printf(" TetGen generates exact Delaunay tetrahedralizations, exact\n"); - printf(" constrained Delaunay tetrahedralizations, and quality "); - printf("tetrahedral\n meshes. The latter are nicely graded and whose "); - printf("tetrahedra have\n radius-edge ratio bounded, thus are suitable "); - printf("for finite element and\n finite volume analysis.\n"); + printf(" TetGen generates Delaunay tetrahedralizations, constrained\n"); + printf(" Delaunay tetrahedralizations, and quality tetrahedral meshes.\n"); printf("\n"); printf("Command Line Syntax:\n"); printf("\n"); - printf(" Below is the command line syntax of TetGen with a list of "); + printf(" Below is the basic command line syntax of TetGen with a list of "); printf("short\n"); printf(" descriptions. Underscores indicate that numbers may optionally\n"); printf(" follow certain switches. Do not leave any space between a "); @@ -2722,22 +3001,24 @@ void tetgenbehavior::usage() printf("Examples of How to Use TetGen:\n"); printf("\n"); printf(" \'tetgen object\' reads vertices from object.node, and writes "); - printf("their\n Delaunay tetrahedralization to object.1.node and "); - printf("object.1.ele.\n"); + printf("their\n Delaunay tetrahedralization to object.1.node, "); + printf("object.1.ele\n (tetrahedra), and object.1.face"); + printf(" (convex hull faces).\n"); printf("\n"); printf(" \'tetgen -p object\' reads a PLC from object.poly or object."); printf("smesh (and\n possibly object.node) and writes its constrained "); - printf("Delaunay\n tetrahedralization to object.1.node, object.1.ele and "); - printf("object.1.face.\n"); + printf("Delaunay\n tetrahedralization to object.1.node, object.1.ele, "); + printf("object.1.face,\n"); + printf(" (boundary faces) and object.1.edge (boundary edges).\n"); printf("\n"); printf(" \'tetgen -pq1.414a.1 object\' reads a PLC from object.poly or\n"); printf(" object.smesh (and possibly object.node), generates a mesh "); printf("whose\n tetrahedra have radius-edge ratio smaller than 1.414 and "); printf("have volume\n of 0.1 or less, and writes the mesh to "); - printf("object.1.node, object.1.ele\n and object.1.face.\n"); + printf("object.1.node, object.1.ele,\n object.1.face, and object.1.edge\n"); printf("\n"); printf("Please send bugs/comments to Hang Si \n"); - terminatetetgen(0); + terminatetetgen(NULL, 0); } /////////////////////////////////////////////////////////////////////////////// @@ -2749,19 +3030,6 @@ void tetgenbehavior::usage() // of a C/C++ program. They together represent the command line user invoked // // from an environment in which TetGen is running. // // // -// When TetGen is invoked from an environment. 'argc' is nonzero, switches // -// and input filename should be supplied as zero-terminated strings in // -// argv[0] through argv[argc - 1] and argv[0] shall be the name used to // -// invoke TetGen, i.e. "tetgen". Switches are previously started with a // -// dash '-' to identify them from the input filename. // -// // -// When TetGen is called from within another program. 'argc' is set to zero. // -// switches are given in one zero-terminated string (no previous dash is // -// required.), and 'argv' is a pointer points to this string. No input // -// filename is required (usually the input data has been directly created by // -// user in the 'tetgenio' structure). A default filename 'tetgen-tmpfile' // -// will be created for debugging output purpose. // -// // /////////////////////////////////////////////////////////////////////////////// bool tetgenbehavior::parse_commandline(int argc, char **argv) @@ -2769,7 +3037,6 @@ bool tetgenbehavior::parse_commandline(int argc, char **argv) int startindex; int increment; int meshnumber; - int scount; int i, j, k; char workstring[1024]; @@ -2783,12 +3050,9 @@ bool tetgenbehavior::parse_commandline(int argc, char **argv) strcpy(commandline, argv[0]); strcat(commandline, " "); } - - // Rcount used to count the number of '-R' be used. - scount = 0; for (i = startindex; i < argc; i++) { - // Remember the command line switches. + // Remember the command line for output. strcat(commandline, argv[i]); strcat(commandline, " "); if (startindex == 1) { @@ -2796,7 +3060,6 @@ bool tetgenbehavior::parse_commandline(int argc, char **argv) if (argv[i][0] != '-') { strncpy(infilename, argv[i], 1024 - 1); infilename[1024 - 1] = '\0'; - // Go to the next string directly. continue; } } @@ -2804,12 +3067,6 @@ bool tetgenbehavior::parse_commandline(int argc, char **argv) for (j = startindex; argv[i][j] != '\0'; j++) { if (argv[i][j] == 'p') { plc = 1; - } else if (argv[i][j] == 'r') { - refine++; - } else if (argv[i][j] == 'R') { - coarse = 1; - } else if (argv[i][j] == 'q') { - quality++; if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || (argv[i][j + 1] == '.')) { k = 0; @@ -2820,16 +3077,27 @@ bool tetgenbehavior::parse_commandline(int argc, char **argv) k++; } workstring[k] = '\0'; - if (quality == 1) { - minratio = (REAL) strtod(workstring, (char **) NULL); - } else if (quality == 2) { - mindihedral = (REAL) strtod(workstring, (char **) NULL); - } else if (quality == 3) { - maxdihedral = (REAL) strtod(workstring, (char **) NULL); + facet_ang_tol = (REAL) strtod(workstring, (char **) NULL); + } + } else if (argv[i][j] == 's') { + psc = 1; + } else if (argv[i][j] == 'Y') { + nobisect = 1; + if ((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) { + nobisect_param = (argv[i][j + 1] - '0'); + j++; + } + if ((argv[i][j + 1] == '/') || (argv[i][j + 1] == ',')) { + j++; + if ((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) { + addsteiner_algo = (argv[i][j + 1] - '0'); + j++; } } - } else if (argv[i][j] == 'm') { - metric++; + } else if (argv[i][j] == 'r') { + refine = 1; + } else if (argv[i][j] == 'q') { + quality = 1; if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || (argv[i][j + 1] == '.')) { k = 0; @@ -2840,33 +3108,68 @@ bool tetgenbehavior::parse_commandline(int argc, char **argv) k++; } workstring[k] = '\0'; - if (metric == 1) { - alpha1 = (REAL) strtod(workstring, (char **) NULL); - } else if (metric == 2) { - alpha2 = (REAL) strtod(workstring, (char **) NULL); + minratio = (REAL) strtod(workstring, (char **) NULL); + } + if ((argv[i][j + 1] == '/') || (argv[i][j + 1] == ',')) { + j++; + if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || + (argv[i][j + 1] == '.')) { + k = 0; + while (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || + (argv[i][j + 1] == '.')) { + j++; + workstring[k] = argv[i][j]; + k++; + } + workstring[k] = '\0'; + mindihedral = (REAL) strtod(workstring, (char **) NULL); } } - } else if (argv[i][j] == 'a') { - if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || - (argv[i][j + 1] == '.')) { - fixedvolume = 1; - k = 0; - while (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || - (argv[i][j + 1] == '.') || (argv[i][j + 1] == 'e') || - (argv[i][j + 1] == '-') || (argv[i][j + 1] == '+')) { - j++; - workstring[k] = argv[i][j]; - k++; + if ((argv[i][j + 1] == '/') || (argv[i][j + 1] == ',')) { + j++; + if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || + (argv[i][j + 1] == '.')) { + k = 0; + while (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || + (argv[i][j + 1] == '.')) { + j++; + workstring[k] = argv[i][j]; + k++; + } + workstring[k] = '\0'; + optmaxdihedral = (REAL) strtod(workstring, (char **) NULL); } - workstring[k] = '\0'; - maxvolume = (REAL) strtod(workstring, (char **) NULL); - } else { - varvolume = 1; } - } 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. + } else if (argv[i][j] == 'R') { + coarsen = 1; + if ((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) { + coarsen_param = (argv[i][j + 1] - '0'); + j++; + } + if ((argv[i][j + 1] == '/') || (argv[i][j + 1] == ',')) { + j++; + if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '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'; + coarsen_percent = (REAL) strtod(workstring, (char **) NULL); + } + } + } else if (argv[i][j] == 'w') { + weighted = 1; + if ((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) { + weighted_param = (argv[i][j + 1] - '0'); + j++; + } + } else if (argv[i][j] == 'b') { + // -b(brio_threshold/brio_ratio/hilbert_limit/hilbert_order) + brio_hilbert = 1; if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || (argv[i][j + 1] == '.')) { k = 0; @@ -2877,19 +3180,122 @@ bool tetgenbehavior::parse_commandline(int argc, char **argv) k++; } workstring[k] = '\0'; - max_btreenode_size = (int) strtol(workstring, (char **) NULL, 0); - } - if (max_btreenode_size == 0) { - btree = 0; + brio_threshold = (int) strtol(workstring, (char **) &workstring, 0); + } + if ((argv[i][j + 1] == '/') || (argv[i][j + 1] == ',')) { + j++; + if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || + (argv[i][j + 1] == '.')) { + k = 0; + while (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || + (argv[i][j + 1] == '.')) { + j++; + workstring[k] = argv[i][j]; + k++; + } + workstring[k] = '\0'; + brio_ratio = (REAL) strtod(workstring, (char **) NULL); + } + } + if ((argv[i][j + 1] == '/') || (argv[i][j + 1] == ',')) { + j++; + if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || + (argv[i][j + 1] == '.') || (argv[i][j + 1] == '-')) { + k = 0; + while (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || + (argv[i][j + 1] == '.') || (argv[i][j + 1] == '-')) { + j++; + workstring[k] = argv[i][j]; + k++; + } + workstring[k] = '\0'; + hilbert_limit = (int) strtol(workstring, (char **) &workstring, 0); + } + } + if ((argv[i][j + 1] == '/') || (argv[i][j + 1] == ',')) { + j++; + if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || + (argv[i][j + 1] == '.') || (argv[i][j + 1] == '-')) { + k = 0; + while (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || + (argv[i][j + 1] == '.') || (argv[i][j + 1] == '-')) { + j++; + workstring[k] = argv[i][j]; + k++; + } + workstring[k] = '\0'; + hilbert_order = (REAL) strtod(workstring, (char **) NULL); + } + } + if (brio_threshold == 0) { // -b0 + brio_hilbert = 0; // Turn off BRIO-Hilbert sorting. + } + if (brio_ratio >= 1.0) { // -b/1 + no_sort = 1; + brio_hilbert = 0; // Turn off BRIO-Hilbert sorting. + } + } else if (argv[i][j] == 'l') { + incrflip = 1; + } else if (argv[i][j] == 'L') { + flipinsert = 1; + } else if (argv[i][j] == 'm') { + metric = 1; + } else if (argv[i][j] == 'a') { + if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || + (argv[i][j + 1] == '.')) { + fixedvolume = 1; + k = 0; + while (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || + (argv[i][j + 1] == '.') || (argv[i][j + 1] == 'e') || + (argv[i][j + 1] == '-') || (argv[i][j + 1] == '+')) { + j++; + workstring[k] = argv[i][j]; + k++; + } + workstring[k] = '\0'; + maxvolume = (REAL) strtod(workstring, (char **) NULL); + } else { + varvolume = 1; + } + } else if (argv[i][j] == 'A') { + regionattrib = 1; + } else if (argv[i][j] == 'D') { + conforming = 1; + if ((argv[i][j + 1] >= '1') && (argv[i][j + 1] <= '3')) { + reflevel = (argv[i][j + 1] - '1') + 1; + j++; } } else if (argv[i][j] == 'i') { insertaddpoints = 1; } else if (argv[i][j] == 'd') { diagnose = 1; + } else if (argv[i][j] == 'c') { + convex = 1; + } else if (argv[i][j] == 'M') { + nomergefacet = 1; + nomergevertex = 1; + if ((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '1')) { + nomergefacet = (argv[i][j + 1] - '0'); + j++; + } + if ((argv[i][j + 1] == '/') || (argv[i][j + 1] == ',')) { + j++; + if ((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '1')) { + nomergevertex = (argv[i][j + 1] - '0'); + j++; + } + } + } else if (argv[i][j] == 'X') { + if (argv[i][j + 1] == '1') { + nostaticfilter = 1; + j++; + } else { + noexact = 1; + } } else if (argv[i][j] == 'z') { zeroindex = 1; } else if (argv[i][j] == 'f') { - facesout = 1; + facesout++; } else if (argv[i][j] == 'e') { edgesout++; } else if (argv[i][j] == 'n') { @@ -2898,16 +3304,8 @@ bool tetgenbehavior::parse_commandline(int argc, char **argv) voroout = 1; } else if (argv[i][j] == 'g') { meditview = 1; - } else if (argv[i][j] == 'G') { - gidview = 1; - } else if (argv[i][j] == 'O') { - geomview = 1; - } else if (argv[i][j] == 'K') { + } else if (argv[i][j] == 'k') { vtkview = 1; - } else if (argv[i][j] == 'M') { - nomerge = 1; - } else if (argv[i][j] == 'Y') { - nobisect++; } else if (argv[i][j] == 'J') { nojettison = 1; } else if (argv[i][j] == 'B') { @@ -2916,19 +3314,10 @@ bool tetgenbehavior::parse_commandline(int argc, char **argv) nonodewritten = 1; } else if (argv[i][j] == 'E') { noelewritten = 1; - if (argv[i][j + 1] == '2') { - j++; - noelewritten = 2; - } } else if (argv[i][j] == 'F') { nofacewritten = 1; } else if (argv[i][j] == 'I') { noiterationnum = 1; - } else if (argv[i][j] == 'o') { - if (argv[i][j + 1] == '2') { - j++; - order = 2; - } } else if (argv[i][j] == 'S') { if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || (argv[i][j + 1] == '.')) { @@ -2941,10 +3330,41 @@ bool tetgenbehavior::parse_commandline(int argc, char **argv) k++; } workstring[k] = '\0'; - steiner = (int) strtol(workstring, (char **) NULL, 0); - } - } else if (argv[i][j] == 's') { - scount++; + steinerleft = (int) strtol(workstring, (char **) NULL, 0); + } + } else if (argv[i][j] == 'o') { + if (argv[i][j + 1] == '2') { + order = 2; + j++; + } + if ((argv[i][j + 1] == '/') || (argv[i][j + 1] == ',')) { + j++; + if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || + (argv[i][j + 1] == '.')) { + k = 0; + while (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || + (argv[i][j + 1] == '.')) { + j++; + workstring[k] = argv[i][j]; + k++; + } + workstring[k] = '\0'; + optmaxdihedral = (REAL) strtod(workstring, (char **) NULL); + } + } + } else if (argv[i][j] == 'O') { + if ((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) { + optlevel = (argv[i][j + 1] - '0'); + j++; + } + if ((argv[i][j + 1] == '/') || (argv[i][j + 1] == ',')) { + j++; + if ((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '7')) { + optscheme = (argv[i][j + 1] - '0'); + j++; + } + } + } else if (argv[i][j] == 'T') { if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || (argv[i][j + 1] == '.')) { k = 0; @@ -2956,15 +3376,17 @@ bool tetgenbehavior::parse_commandline(int argc, char **argv) k++; } workstring[k] = '\0'; - if (scount == 1) { - optlevel = (int) strtol(workstring, (char **) NULL, 0); - } else if (scount == 2) { - optpasses = (int) strtol(workstring, (char **) NULL, 0); - } + epsilon = (REAL) strtod(workstring, (char **) NULL); } - } else if (argv[i][j] == 'D') { - conformdel++; - } else if (argv[i][j] == 'T') { + } else if (argv[i][j] == 'R') { + reversetetori = 1; + } else if (argv[i][j] == 'C') { + docheck++; + } else if (argv[i][j] == 'Q') { + quiet = 1; + } else if (argv[i][j] == 'V') { + verbose++; + } else if (argv[i][j] == 'x') { if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || (argv[i][j + 1] == '.')) { k = 0; @@ -2976,16 +3398,14 @@ bool tetgenbehavior::parse_commandline(int argc, char **argv) k++; } workstring[k] = '\0'; - epsilon = (REAL) strtod(workstring, (char **) NULL); - } - } else if (argv[i][j] == 'C') { - docheck++; - } else if (argv[i][j] == 'X') { - fliprepair = 0; - } else if (argv[i][j] == 'Q') { - quiet = 1; - } else if (argv[i][j] == 'V') { - verbose++; + tetrahedraperblock = (int) strtol(workstring, (char **) NULL, 0); + if (tetrahedraperblock > 8188) { + vertexperblock = tetrahedraperblock / 2; + shellfaceperblock = vertexperblock / 2; + } else { + tetrahedraperblock = 8188; + } + } } else if ((argv[i][j] == 'h') || (argv[i][j] == 'H') || (argv[i][j] == '?')) { usage(); @@ -3002,7 +3422,7 @@ bool tetgenbehavior::parse_commandline(int argc, char **argv) if (infilename[0] == '\0') { // No input file name. Print the syntax and exit. syntax(); - terminatetetgen(0); + terminatetetgen(NULL, 0); } // Recognize the object from file extension if it is available. if (!strcmp(&infilename[strlen(infilename) - 5], ".node")) { @@ -3031,7 +3451,7 @@ bool tetgenbehavior::parse_commandline(int argc, char **argv) } else if (!strcmp(&infilename[strlen(infilename) - 5], ".mesh")) { infilename[strlen(infilename) - 5] = '\0'; object = MEDIT; - plc = 1; + if (!refine) plc = 1; } else if (!strcmp(&infilename[strlen(infilename) - 4], ".vtk")) { infilename[strlen(infilename) - 4] = '\0'; object = VTK; @@ -3042,47 +3462,76 @@ bool tetgenbehavior::parse_commandline(int argc, char **argv) refine = 1; } } - plc = plc || diagnose; - useshelles = plc || refine || coarse || quality; - goodratio = minratio; - goodratio *= goodratio; - // Detect improper combinations of switches. - if (plc && refine) { - printf("Error: Switch -r cannot use together with -p.\n"); - return false; + if (nobisect && (!plc && !refine)) { // -Y + plc = 1; // Default -p option. } - if (refine && (plc || noiterationnum)) { - printf("Error: Switches %s cannot use together with -r.\n", - "-p, -d, and -I"); - return false; + if (quality && (!plc && !refine)) { // -q + plc = 1; // Default -p option. + } + if (diagnose && !plc) { // -d + plc = 1; + } + if (refine && !quality) { // -r only + // Reconstruct a mesh, no mesh optimization. + optlevel = 0; } - if (diagnose && (quality || insertaddpoints || (order == 2) || neighout - || docheck)) { - printf("Error: Switches %s cannot use together with -d.\n", - "-q, -i, -o2, -n, and -C"); + if (insertaddpoints && (optlevel == 0)) { // with -i option + optlevel = 2; + } + if (coarsen && (optlevel == 0)) { // with -R option + optlevel = 2; + } + + // Detect improper combinations of switches. + if ((refine || plc) && weighted) { + printf("Error: Switches -w cannot use together with -p or -r.\n"); return false; } - // Be careful not to allocate space for element area constraints that - // will never be assigned any value (other than the default -1.0). - if (!refine && !plc) { - varvolume = 0; + if (convex) { // -c + if (plc && !regionattrib) { + // -A (region attribute) is needed for marking exterior tets (-1). + regionattrib = 1; + } } + + // Note: -A must not used together with -r option. // Be careful not to add an extra attribute to each element unless the // input supports it (PLC in, but not refining a preexisting mesh). if (refine || !plc) { regionattrib = 0; } + // Be careful not to allocate space for element area constraints that + // will never be assigned any value (other than the default -1.0). + if (!refine && !plc) { + varvolume = 0; + } // If '-a' or '-aa' is in use, enable '-q' option too. if (fixedvolume || varvolume) { if (quality == 0) { quality = 1; + if (!plc && !refine) { + plc = 1; // enable -p. + } + } + } + // No user-specified dihedral angle bound. Use default ones. + if (!quality) { + if (optmaxdihedral < 179.0) { + if (nobisect) { // with -Y option + optmaxdihedral = 179.0; + } else { // -p only + optmaxdihedral = 179.999; + } + } + if (optminsmtdihed < 179.999) { + optminsmtdihed = 179.999; + } + if (optminslidihed < 179.999) { + optminslidihed = 179.999; } } - // Calculate the goodangle for testing bad subfaces. - goodangle = cos(minangle * tetgenmesh::PI / 180.0); - goodangle *= goodangle; increment = 0; strcpy(workstring, infilename); @@ -3130,884 +3579,229 @@ bool tetgenbehavior::parse_commandline(int argc, char **argv) //// //// //// behavior_cxx ///////////////////////////////////////////////////////////// -//// prim_cxx ///////////////////////////////////////////////////////////////// +//// mempool_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. +// Initialize fast lookup tables for mesh maniplulation primitives. -int tetgenmesh::ve[6] = { 2, 5, 4, 1, 0, 3 }; +int tetgenmesh::bondtbl[12][12] = {{0,},}; +int tetgenmesh::enexttbl[12] = {0,}; +int tetgenmesh::eprevtbl[12] = {0,}; +int tetgenmesh::enextesymtbl[12] = {0,}; +int tetgenmesh::eprevesymtbl[12] = {0,}; +int tetgenmesh::eorgoppotbl[12] = {0,}; +int tetgenmesh::edestoppotbl[12] = {0,}; +int tetgenmesh::fsymtbl[12][12] = {{0,},}; +int tetgenmesh::facepivot1[12] = {0,}; +int tetgenmesh::facepivot2[12][12] = {{0,},}; +int tetgenmesh::tsbondtbl[12][6] = {{0,},}; +int tetgenmesh::stbondtbl[12][6] = {{0,},}; +int tetgenmesh::tspivottbl[12][6] = {{0,},}; +int tetgenmesh::stpivottbl[12][6] = {{0,},}; -// Tables 'vo', 'vd' and 'va' take an edge version, return the positions of -// the origin, destination and apex in the triangle. +// Table 'esymtbl' takes an directed edge (version) as input, returns the +// inversed edge (version) of it. -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 }; +int tetgenmesh::esymtbl[12] = {9, 6, 11, 4, 3, 7, 1, 5, 10, 0, 8, 2}; -// The following tables are for tetrahedron primitives (operate on trifaces). +// The following four tables give the 12 permutations of the set {0,1,2,3}. +// An offset 4 is added to each element for a direct access of the points +// in the tetrahedron data structure. -// For 'org()', 'dest()' and 'apex()'. Use 'loc' as the first index and -// 'ver' as the second index. +int tetgenmesh:: orgpivot[12] = {7, 7, 5, 5, 6, 4, 4, 6, 5, 6, 7, 4}; +int tetgenmesh::destpivot[12] = {6, 4, 4, 6, 5, 6, 7, 4, 7, 7, 5, 5}; +int tetgenmesh::apexpivot[12] = {5, 6, 7, 4, 7, 7, 5, 5, 6, 4, 4, 6}; +int tetgenmesh::oppopivot[12] = {4, 5, 6, 7, 4, 5, 6, 7, 4, 5, 6, 7}; -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} -}; +// The twelve versions correspond to six undirected edges. The following two +// tables map a version to an undirected edge and vice versa. -// For oppo() primitives, use 'loc' as the index. +int tetgenmesh::ver2edge[12] = {0, 1, 2, 3, 3, 5, 1, 5, 4, 0, 4, 2}; +int tetgenmesh::edge2ver[ 6] = {0, 1, 2, 3, 8, 5}; -int tetgenmesh::loc2oppo[4] = { 3, 2, 0, 1 }; +// Edge versions whose apex or opposite may be dummypoint. -// 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}.) +int tetgenmesh::epivot[12] = {4, 5, 2, 11, 4, 5, 2, 11, 4, 5, 2, 11}; -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} } -}; -// 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). +// Table 'snextpivot' takes an edge version as input, returns the next edge +// version in the same edge ring. -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} -}; +int tetgenmesh::snextpivot[6] = {2, 5, 4, 1, 0, 3}; -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) -}; +// The following three tables give the 6 permutations of the set {0,1,2}. +// An offset 3 is added to each element for a direct access of the points +// in the triangle data structure. -int tetgenmesh::locpivot[4][3] = { - {1, 2, 3}, - {0, 2, 3}, - {0, 1, 3}, - {0, 1, 2} -}; - -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}} -}; +int tetgenmesh::sorgpivot [6] = {3, 4, 4, 5, 5, 3}; +int tetgenmesh::sdestpivot[6] = {4, 3, 5, 4, 3, 5}; +int tetgenmesh::sapexpivot[6] = {5, 5, 3, 3, 4, 4}; /////////////////////////////////////////////////////////////////////////////// // // -// getnextsface() Finds the next subface in the face ring. // -// // -// 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. // +// inittable() Initialize the look-up tables. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::getnextsface(face* s1, face* s2) +void tetgenmesh::inittables() { - 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); + int i, j; + + + // i = t1.ver; j = t2.ver; + for (i = 0; i < 12; i++) { + for (j = 0; j < 12; j++) { + bondtbl[i][j] = (j & 3) + (((i & 12) + (j & 12)) % 12); } - } else { - spivot(*s1, neighsh); } - if (sorg(neighsh) != sorg(*s1)) { - sesymself(neighsh); + + + // i = t1.ver; j = t2.ver + for (i = 0; i < 12; i++) { + for (j = 0; j < 12; j++) { + fsymtbl[i][j] = (j + 12 - (i & 12)) % 12; + } } - if (s2 != (face *) NULL) { - *s2 = neighsh; - } else { - *s1 = neighsh; + + + for (i = 0; i < 12; i++) { + facepivot1[i] = (esymtbl[i] & 3); } -} -/////////////////////////////////////////////////////////////////////////////// -// // -// 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. // -// // -// 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. // -// // -/////////////////////////////////////////////////////////////////////////////// + for (i = 0; i < 12; i++) { + for (j = 0; j < 12; j++) { + facepivot2[i][j] = fsymtbl[esymtbl[i]][j]; + } + } -void tetgenmesh::tsspivot(triface* checkedge, face* checkseg) -{ - triface spintet; - face parentsh; - point tapex; - int hitbdry; + for (i = 0; i < 12; i++) { + enexttbl[i] = (i + 4) % 12; + eprevtbl[i] = (i + 8) % 12; + } - 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); - } + for (i = 0; i < 12; i++) { + enextesymtbl[i] = esymtbl[enexttbl[i]]; + eprevesymtbl[i] = esymtbl[eprevtbl[i]]; + } + + for (i = 0; i < 12; i++) { + eorgoppotbl [i] = eprevtbl[esymtbl[enexttbl[i]]]; + edestoppotbl[i] = enexttbl[esymtbl[eprevtbl[i]]]; + } + + int soffset, toffset; + + // i = t.ver, j = s.shver + for (i = 0; i < 12; i++) { + for (j = 0; j < 6; j++) { + if ((j & 1) == 0) { + soffset = (6 - ((i & 12) >> 1)) % 6; + toffset = (12 - ((j & 6) << 1)) % 12; + } else { + soffset = (i & 12) >> 1; + toffset = (j & 6) << 1; } - return; + tsbondtbl[i][j] = (j & 1) + (((j & 6) + soffset) % 6); + stbondtbl[i][j] = (i & 3) + (((i & 12) + toffset) % 12); } - if (!fnextself(spintet)) { - hitbdry++; - if (hitbdry < 2) { - esym(*checkedge, spintet); - if (!fnextself(spintet)) { - hitbdry++; - } + } + + + // i = t.ver, j = s.shver + for (i = 0; i < 12; i++) { + for (j = 0; j < 6; j++) { + if ((j & 1) == 0) { + soffset = (i & 12) >> 1; + toffset = (j & 6) << 1; + } else { + soffset = (6 - ((i & 12) >> 1)) % 6; + toffset = (12 - ((j & 6) << 1)) % 12; } + tspivottbl[i][j] = (j & 1) + (((j & 6) + soffset) % 6); + stpivottbl[i][j] = (i & 3) + (((i & 12) + toffset) % 12); } - } while ((apex(spintet) != tapex) && (hitbdry < 2)); - // Not find. - checkseg->sh = dummysh; + } } /////////////////////////////////////////////////////////////////////////////// // // -// sstpivot() Finds a tetrahedron abutting a subsegment. // +// restart() Deallocate all objects in this pool. // // // -// 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. // +// 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. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::sstpivot(face* checkseg, triface* retedge) +void tetgenmesh::arraypool::restart() { - 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 - } - // Correct the edge direction before return. - findedge(retedge, sorg(*checkseg), sdest(*checkseg)); + objects = 0l; } /////////////////////////////////////////////////////////////////////////////// // // -// point2tetorg(), point2shorg(), point2segorg() // +// poolinit() Initialize an arraypool for allocation of objects. // // // -// 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. // +// Before the pool may be used, it must be initialized by this procedure. // +// After initialization, memory can be allocated and freed in this pool. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::point2tetorg(point pa, triface& searchtet) +void tetgenmesh::arraypool::poolinit(int sizeofobject, int log2objperblk) { - int i; + // Each object must be at least one byte long. + objectbytes = sizeofobject > 1 ? sizeofobject : 1; - // 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 (i == 8) { - printf("Internal error: %d contains bad tet pointer.\n", pointmark(pa)); - terminatetetgen(2); - } -} + log2objectsperblock = log2objperblk; + // Compute the number of objects in each block. + objectsperblock = ((int) 1) << log2objectsperblock; + objectsperblockmark = objectsperblock - 1; -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); - } -} + // No memory has been allocated. + totalmemory = 0l; + // The top array has not been allocated yet. + toparray = (char **) NULL; + toparraylen = 0; -void tetgenmesh::point2segorg(point pa, face& searchsh) -{ - sdecode(point2seg(pa), searchsh); - if (searchsh.sh == NULL) { - printf("Internal error: %d contains bad seg pointer.\n", pointmark(pa)); - terminatetetgen(2); - } - 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); - } + // Ready all indices to be allocated. + restart(); } /////////////////////////////////////////////////////////////////////////////// // // -// findorg() Find a point in the given tet or subface. // -// // -// 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. // +// arraypool() The constructor and destructor. // // // /////////////////////////////////////////////////////////////////////////////// -bool tetgenmesh::findorg(triface* tface, point dorg) +tetgenmesh::arraypool::arraypool(int sizeofobject, int log2objperblk) { - 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; + poolinit(sizeofobject, log2objperblk); } -bool tetgenmesh::findorg(face* sface, point dorg) +tetgenmesh::arraypool::~arraypool() { - if (sorg(*sface) == dorg) { - return true; - } else { - if (sdest(*sface) == dorg) { - senextself(*sface); - return true; - } else { - if (sapex(*sface) == dorg) { - senext2self(*sface); - return true; - } + 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); } - return false; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// 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. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::findedge(triface* tface, point eorg, point edest) -{ - 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); -} - -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; - } - } - } - senextself(*sface); - } - printf("Internalerror in findedge(): Unable to find an edge in subface.\n"); - terminatetetgen(2); -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// 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. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::getonextseg(face* s, face* lseg) -{ - 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; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// getseghasorg() Get the segment containing the given point. // -// // -// '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. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::getseghasorg(face* sseg, point dorg) -{ - face nextseg; - point checkpt; - - 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"); - terminatetetgen(2); -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// getsubsegfarorg() Get the origin of the parent segment of a subseg. // -// // -/////////////////////////////////////////////////////////////////////////////// - -tetgenmesh::point tetgenmesh::getsubsegfarorg(face* sseg) -{ - face prevseg; - point checkpt; - - 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; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// getsubsegfardest() Get the dest. of the parent segment of a subseg. // -// // -/////////////////////////////////////////////////////////////////////////////// - -tetgenmesh::point tetgenmesh::getsubsegfardest(face* sseg) -{ - face nextseg; - point checkpt; - - 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; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// printtet() Print out the details of a tetrahedron on screen. // -// // -// It's also used when the highest level of verbosity (`-VVV') is specified. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::printtet(triface* tface) -{ - 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"); - - 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]); - } 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. - } - printf("\n"); - } - } - 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"); - } - } - } -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// printsh() Print out the details of a subface or subsegment on screen. // -// // -// It's also used when the highest level of verbosity (`-VVV') is specified. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::printsh(face* sface) -{ - 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); - } - } -} - -//// //// -//// //// -//// prim_cxx ///////////////////////////////////////////////////////////////// - -//// mempool_cxx ////////////////////////////////////////////////////////////// -//// //// -//// //// - -/////////////////////////////////////////////////////////////////////////////// -// // -// restart() Deallocate all objects in this pool. // -// // -// 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. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::arraypool::restart() -{ - objects = 0l; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// poolinit() Initialize an arraypool for allocation of objects. // -// // -// Before the pool may be used, it must be initialized by this procedure. // -// After initialization, memory can be allocated and freed in this pool. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::arraypool::poolinit(int sizeofobject, int log2objperblk) -{ - // 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; - - // Ready all indices to be allocated. - restart(); -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// arraypool() The constructor and destructor. // -// // -/////////////////////////////////////////////////////////////////////////////// - -tetgenmesh::arraypool::arraypool(int sizeofobject, int log2objperblk) -{ - poolinit(sizeofobject, log2objperblk); -} - -tetgenmesh::arraypool::~arraypool() -{ - 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); - } - - // The top array is no longer allocated. - toparray = (char **) NULL; - toparraylen = 0; - objects = 0; - totalmemory = 0; + + // The top array is no longer allocated. + toparray = (char **) NULL; + toparraylen = 0; + objects = 0; + totalmemory = 0; } /////////////////////////////////////////////////////////////////////////////// @@ -4043,7 +3837,7 @@ char* tetgenmesh::arraypool::getblock(int objectindex) toparray[i] = (char *) NULL; } // Account for the memory. - totalmemory = newsize * (unsigned long) sizeof(char *); + totalmemory = newsize * (uintptr_t) sizeof(char *); } else if (topindex >= toparraylen) { // Resize the top array, making sure it holds 'topindex'. newsize = 3 * toparraylen; @@ -4120,177 +3914,25 @@ void* tetgenmesh::arraypool::lookup(int objectindex) // // // newindex() Allocate space for a fresh object from the pool. // // // +// 'newptr' returns a pointer to the new object (it must not be a NULL). // +// // /////////////////////////////////////////////////////////////////////////////// int tetgenmesh::arraypool::newindex(void **newptr) { - void *newobject; - int newindex; - // Allocate an object at index 'firstvirgin'. - newindex = objects; - newobject = (void *) (getblock(objects) + + int newindex = objects; + *newptr = (void *) (getblock(objects) + (objects & (objectsperblock - 1)) * objectbytes); objects++; - // If 'newptr' is not NULL, use it to return a pointer to the object. - if (newptr != (void **) NULL) { - *newptr = newobject; - } return newindex; } -/////////////////////////////////////////////////////////////////////////////// -// // -// listinit() Initialize a list for storing a data type. // -// // -// 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::list::listinit(int itbytes, compfunc pcomp, int mitems, - int exsize) -{ - itembytes = itbytes; - comp = pcomp; - maxitems = mitems; - expandsize = exsize; - base = (char *) malloc(maxitems * itembytes); - if (base == (char *) NULL) { - terminatetetgen(1); - } - items = 0; -} /////////////////////////////////////////////////////////////////////////////// // // -// append() Add a new item at the end of the list. // -// // -// 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::list::append(void *appitem) -{ - // 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); - } - 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* 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. - if (insitem != (void *) NULL) { - memcpy(base + pos * itembytes, insitem, itembytes); - } - items++; - return (void *) (base + pos * itembytes); -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// 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::list::del(int pos, int order) -{ - // 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--; - } -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// hasitem() Search in this list to find if 'checkitem' exists. // -// // -// 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. // -// // -/////////////////////////////////////////////////////////////////////////////// - -int tetgenmesh::list::hasitem(void* checkitem) -{ - int i; - - for (i = 0; i < items; i++) { - if (comp != (compfunc) NULL) { - if ((* comp)((void *)(base + i * itembytes), checkitem) == 0) { - return i; - } - } - } - return -1; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// memorypool() The constructors of memorypool. // +// memorypool() The constructors of memorypool. // // // /////////////////////////////////////////////////////////////////////////////// @@ -4301,7 +3943,6 @@ tetgenmesh::memorypool::memorypool() deaditemstack = (void *) NULL; pathblock = (void **) NULL; pathitem = (void *) NULL; - itemwordtype = POINTER; alignbytes = 0; itembytes = itemwords = 0; itemsperblock = 0; @@ -4310,10 +3951,10 @@ tetgenmesh::memorypool::memorypool() pathitemsleft = 0; } -tetgenmesh::memorypool:: -memorypool(int bytecount, int itemcount, enum wordtype wtype, int alignment) +tetgenmesh::memorypool::memorypool(int bytecount, int itemcount, int wsize, + int alignment) { - poolinit(bytecount, itemcount, wtype, alignment); + poolinit(bytecount, itemcount, wsize, alignment); } /////////////////////////////////////////////////////////////////////////////// @@ -4347,14 +3988,9 @@ tetgenmesh::memorypool::~memorypool() // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::memorypool:: -poolinit(int bytecount, int itemcount, enum wordtype wtype, int alignment) +void tetgenmesh::memorypool::poolinit(int bytecount,int itemcount,int wordsize, + int alignment) { - int wordsize; - - // Initialize values in the pool. - itemwordtype = wtype; - wordsize = (itemwordtype == POINTER) ? sizeof(void *) : sizeof(REAL); // Find the proper alignment, which must be at least as large as: // - The parameter `alignment'. // - The primary word type, to avoid unaligned accesses. @@ -4379,7 +4015,7 @@ poolinit(int bytecount, int itemcount, enum wordtype wtype, int alignment) firstblock = (void **) malloc(itemsperblock * itembytes + sizeof(void *) + alignbytes); if (firstblock == (void **) NULL) { - terminatetetgen(1); + terminatetetgen(NULL, 1); } // Set the next block pointer to NULL. *(firstblock) = (void *) NULL; @@ -4398,7 +4034,6 @@ poolinit(int bytecount, int itemcount, enum wordtype wtype, int alignment) void tetgenmesh::memorypool::restart() { - // unsigned long alignptr; uintptr_t alignptr; items = 0; @@ -4407,12 +4042,8 @@ void tetgenmesh::memorypool::restart() // 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)); @@ -4432,7 +4063,6 @@ void* tetgenmesh::memorypool::alloc() { void *newitem; void **newblock; - // unsigned long alignptr; uintptr_t alignptr; // First check the linked list of dead items. If the list is not @@ -4449,7 +4079,7 @@ void* tetgenmesh::memorypool::alloc() newblock = (void **) malloc(itemsperblock * itembytes + sizeof(void *) + alignbytes); if (newblock == (void **) NULL) { - terminatetetgen(1); + terminatetetgen(NULL, 1); } *nowblock = (void *) newblock; // The next block pointer is NULL. @@ -4459,12 +4089,8 @@ void* tetgenmesh::memorypool::alloc() 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)); @@ -4474,11 +4100,7 @@ void* tetgenmesh::memorypool::alloc() // Allocate a new item. newitem = nextitem; // Advance `nextitem' pointer to next free item in block. - if (itemwordtype == POINTER) { - nextitem = (void *) ((void **) nextitem + itemwords); - } else { - nextitem = (void *) ((REAL *) nextitem + itemwords); - } + nextitem = (void *) ((uintptr_t) nextitem + itembytes); unallocateditems--; maxitems++; } @@ -4512,18 +4134,13 @@ void tetgenmesh::memorypool::dealloc(void *dyingitem) void tetgenmesh::memorypool::traversalinit() { - // unsigned long alignptr; uintptr_t 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); 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)); @@ -4546,7 +4163,6 @@ void tetgenmesh::memorypool::traversalinit() void* tetgenmesh::memorypool::traverse() { void *newitem; - // unsigned long alignptr; uintptr_t alignptr; // Stop upon exhausting the list of items. @@ -4558,12 +4174,8 @@ void* tetgenmesh::memorypool::traverse() // 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)); @@ -4572,89 +4184,18 @@ void* tetgenmesh::memorypool::traverse() } newitem = pathitem; // Find the next item in the block. - if (itemwordtype == POINTER) { - pathitem = (void *) ((void **) pathitem + itemwords); - } else { - pathitem = (void *) ((REAL *) pathitem + itemwords); - } + pathitem = (void *) ((uintptr_t) pathitem + itembytes); pathitemsleft--; return newitem; } -/////////////////////////////////////////////////////////////////////////////// -// // -// 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. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::makepoint2tetmap() -{ - triface tetloop; - point pointptr; - - if (b->verbose > 2) { - printf(" Constructing mapping from points to tetrahedra.\n"); - } - - // Initialize the point2tet field of each point. - points->traversalinit(); - pointptr = pointtraverse(); - while (pointptr != (point) NULL) { - setpoint2tet(pointptr, (tetrahedron) NULL); - pointptr = pointtraverse(); - } - - 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(); - } -} - -void tetgenmesh::makepoint2segmap() -{ - face segloop; - point *ppt; - - if (b->verbose > 2) { - printf(" Constructing mapping from points to segments.\n"); - } - - 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); - } -} - /////////////////////////////////////////////////////////////////////////////// // // // makeindex2pointmap() Create a map from index to vertices. // // // // 'idx2verlist' returns the created map. Traverse all vertices, a pointer // // to each vertex is set into the array. The pointer to the first vertex is // -// saved in 'idx2verlist[0]'. Don't forget to minus 'in->firstnumber' when // -// to get the vertex form its index. // +// saved in 'idx2verlist[in->firstnumber]'. // // // /////////////////////////////////////////////////////////////////////////////// @@ -4667,430 +4208,567 @@ void tetgenmesh::makeindex2pointmap(point*& idx2verlist) printf(" Constructing mapping from indices to points.\n"); } - idx2verlist = new point[points->items]; + idx2verlist = new point[points->items + 1]; points->traversalinit(); pointloop = pointtraverse(); - idx = 0; + idx = in->firstnumber; while (pointloop != (point) NULL) { - idx2verlist[idx] = pointloop; - idx++; + idx2verlist[idx++] = pointloop; pointloop = pointtraverse(); } } /////////////////////////////////////////////////////////////////////////////// // // -// makesegmentmap(), makesubfacemap(), maketetrahedronmap() // -// // -// Create a map from vertex indices to segments, subfaces, and tetrahedra // -// sharing at the same vertices. // +// makesubfacemap() Create a map from vertex to subfaces incident at it. // // // -// 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'. // +// The map is returned in two arrays 'idx2faclist' and 'facperverlist'. All // +// subfaces incident at i-th vertex (i is counted from 0) are found in the // +// array facperverlist[j], where idx2faclist[i] <= j < idx2faclist[i + 1]. // +// Each entry in facperverlist[j] is a subface whose origin is the vertex. // // // // NOTE: These two arrays will be created inside this routine, don't forget // // to free them after using. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::makesegmentmap(int*& idx2seglist, shellface**& segsperverlist) +void tetgenmesh::makepoint2submap(memorypool* pool, int*& idx2faclist, + face*& facperverlist) { - shellface *shloop; + face shloop; int i, j, k; if (b->verbose > 1) { - printf(" Constructing mapping from points to segments.\n"); + printf(" Making a map from points to subfaces.\n"); } - // Create and initialize 'idx2seglist'. - idx2seglist = new int[points->items + 1]; - for (i = 0; i < points->items + 1; i++) idx2seglist[i] = 0; - - // 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); - } + // Initialize 'idx2faclist'. + idx2faclist = new int[points->items + 1]; + for (i = 0; i < points->items + 1; i++) idx2faclist[i] = 0; - // Calculate the total length of array 'facesperverlist'. - j = idx2seglist[0]; - idx2seglist[0] = 0; // Array starts from 0 element. + // Loop all subfaces, counter the number of subfaces incident at a vertex. + pool->traversalinit(); + shloop.sh = shellfacetraverse(pool); + while (shloop.sh != (shellface *) NULL) { + // Increment the number of incident subfaces for each vertex. + j = pointmark((point) shloop.sh[3]) - in->firstnumber; + idx2faclist[j]++; + j = pointmark((point) shloop.sh[4]) - in->firstnumber; + idx2faclist[j]++; + // Skip the third corner if it is a segment. + if (shloop.sh[5] != NULL) { + j = pointmark((point) shloop.sh[5]) - in->firstnumber; + idx2faclist[j]++; + } + shloop.sh = shellfacetraverse(pool); + } + + // Calculate the total length of array 'facperverlist'. + j = idx2faclist[0]; + idx2faclist[0] = 0; // Array starts from 0 element. for (i = 0; i < points->items; i++) { - k = idx2seglist[i + 1]; - idx2seglist[i + 1] = idx2seglist[i] + j; + k = idx2faclist[i + 1]; + idx2faclist[i + 1] = idx2faclist[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]++; + + // The total length is in the last unit of idx2faclist. + facperverlist = new face[idx2faclist[i]]; + + // Loop all subfaces again, remember the subfaces at each vertex. + pool->traversalinit(); + shloop.sh = shellfacetraverse(pool); + while (shloop.sh != (shellface *) NULL) { + j = pointmark((point) shloop.sh[3]) - in->firstnumber; + shloop.shver = 0; // save the origin. + facperverlist[idx2faclist[j]] = shloop; + idx2faclist[j]++; + // Is it a subface or a subsegment? + if (shloop.sh[5] != NULL) { + j = pointmark((point) shloop.sh[4]) - in->firstnumber; + shloop.shver = 2; // save the origin. + facperverlist[idx2faclist[j]] = shloop; + idx2faclist[j]++; + j = pointmark((point) shloop.sh[5]) - in->firstnumber; + shloop.shver = 4; // save the origin. + facperverlist[idx2faclist[j]] = shloop; + idx2faclist[j]++; + } else { + j = pointmark((point) shloop.sh[4]) - in->firstnumber; + shloop.shver = 1; // save the origin. + facperverlist[idx2faclist[j]] = shloop; + idx2faclist[j]++; } - shloop = shellfacetraverse(subsegs); + shloop.sh = shellfacetraverse(pool); } - // Contents in 'idx2seglist' are shifted, now shift them back. + + // Contents in 'idx2faclist' are shifted, now shift them back. for (i = points->items - 1; i >= 0; i--) { - idx2seglist[i + 1] = idx2seglist[i]; + idx2faclist[i + 1] = idx2faclist[i]; } - idx2seglist[0] = 0; + idx2faclist[0] = 0; } -void tetgenmesh::makesubfacemap(int*& idx2facelist, - shellface**& facesperverlist) +/////////////////////////////////////////////////////////////////////////////// +// // +// tetrahedrondealloc() Deallocate space for a tet., marking it dead. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::tetrahedrondealloc(tetrahedron *dyingtetrahedron) { - shellface *shloop; - int i, j, k; + // 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; - if (b->verbose > 1) { - printf(" Constructing mapping from points to subfaces.\n"); + // Dealloc the space to subfaces/subsegments. + if (dyingtetrahedron[8] != NULL) { + tet2segpool->dealloc((shellface *) dyingtetrahedron[8]); } - - // 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); + if (dyingtetrahedron[9] != NULL) { + tet2subpool->dealloc((shellface *) dyingtetrahedron[9]); } - // 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; + tetrahedrons->dealloc((void *) dyingtetrahedron); } -void tetgenmesh::maketetrahedronmap(int*& idx2tetlist, - tetrahedron**& tetsperverlist) +/////////////////////////////////////////////////////////////////////////////// +// // +// tetrahedrontraverse() Traverse the tetrahedra, skipping dead ones. // +// // +/////////////////////////////////////////////////////////////////////////////// + +tetgenmesh::tetrahedron* tetgenmesh::tetrahedrontraverse() { - tetrahedron *tetloop; - int i, j, k; + tetrahedron *newtetrahedron; - if (b->verbose > 1) { - printf(" Constructing mapping from points to tetrahedra.\n"); - } + do { + newtetrahedron = (tetrahedron *) tetrahedrons->traverse(); + if (newtetrahedron == (tetrahedron *) NULL) { + return (tetrahedron *) NULL; + } + } while ((newtetrahedron[4] == (tetrahedron) NULL) || + ((point) newtetrahedron[7] == dummypoint)); + return newtetrahedron; +} - // Create and initialize 'idx2tetlist'. - idx2tetlist = new int[points->items + 1]; - for (i = 0; i < points->items + 1; i++) idx2tetlist[i] = 0; +tetgenmesh::tetrahedron* tetgenmesh::alltetrahedrontraverse() +{ + tetrahedron *newtetrahedron; - // 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]++; + do { + newtetrahedron = (tetrahedron *) tetrahedrons->traverse(); + if (newtetrahedron == (tetrahedron *) NULL) { + return (tetrahedron *) NULL; } - tetloop = tetrahedrontraverse(); - } + } while (newtetrahedron[4] == (tetrahedron) NULL); // Skip dead ones. + return newtetrahedron; +} - // 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; +/////////////////////////////////////////////////////////////////////////////// +// // +// 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; + pool->dealloc((void *) dyingsh); } /////////////////////////////////////////////////////////////////////////////// // // -// dummyinit() Initialize the tetrahedron that fills "outer space" and // -// the omnipresent subface. // +// shellfacetraverse() Traverse the subfaces, skipping dead ones. Used // +// for both subfaces and subsegments pool traverse. // // // -// 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. // +/////////////////////////////////////////////////////////////////////////////// + +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; +} + + +/////////////////////////////////////////////////////////////////////////////// // // -// 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. // +// pointdealloc() Deallocate space for a point, marking it dead. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::dummyinit(int tetwords, int shwords) +void tetgenmesh::pointdealloc(point dyingpoint) { - 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 ] = NULL; - dummytet[9 ] = NULL; - } + // 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); } /////////////////////////////////////////////////////////////////////////////// // // -// initializepools() Calculate the sizes of the point, tetrahedron, and // -// subface. Initialize their memory pools. // +// pointtraverse() Traverse the points, skipping dead ones. // // // -// 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. // +/////////////////////////////////////////////////////////////////////////////// + +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; +} + +/////////////////////////////////////////////////////////////////////////////// // // -// 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. // +// maketetrahedron() Create a new tetrahedron. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::initializepools() +void tetgenmesh::maketetrahedron(triface *newtet) { - enum wordtype wtype; - int pointsize, elesize, shsize; + newtet->tet = (tetrahedron *) tetrahedrons->alloc(); - // Default checkpbc = 0; - if ((b->plc || b->refine) && (in->pbcgrouplist != NULL)) { - checkpbcs = 1; + // Initialize the four adjoining tetrahedra to be "outer space". + newtet->tet[0] = NULL; + newtet->tet[1] = NULL; + newtet->tet[2] = NULL; + newtet->tet[3] = NULL; + // Four NULL vertices. + newtet->tet[4] = NULL; + newtet->tet[5] = NULL; + newtet->tet[6] = NULL; + newtet->tet[7] = NULL; + // No attached segments and subfaces yet. + newtet->tet[8] = NULL; + newtet->tet[9] = NULL; + // Initialize the marker (clear all flags). + setelemmarker(newtet->tet, 0); + for (int i = 0; i < numelemattrib; i++) { + setelemattribute(newtet->tet, i, 0.0); } - // Default varconstraint = 0; - if (in->segmentconstraintlist || in->facetconstraintlist) { - varconstraint = 1; + if (b->varvolume) { + setvolumebound(newtet->tet, -1.0); } - // 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 { + // Initialize the version to be Zero. + newtet->ver = 11; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// makeshellface() Create a new shellface with version zero. Used for // +// both subfaces and subsegments. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::makeshellface(memorypool *pool, face *newface) +{ + newface->sh = (shellface *) pool->alloc(); + + // No adjointing subfaces. + newface->sh[0] = NULL; + newface->sh[1] = NULL; + newface->sh[2] = NULL; + // Three NULL vertices. + newface->sh[3] = NULL; + newface->sh[4] = NULL; + newface->sh[5] = NULL; + // No adjoining subsegments. + newface->sh[6] = NULL; + newface->sh[7] = NULL; + newface->sh[8] = NULL; + // No adjoining tetrahedra. + newface->sh[9] = NULL; + newface->sh[10] = NULL; + if (checkconstraints) { + // Initialize the maximum area bound. + setareabound(*newface, 0.0); + } + // Clear the infection and marktest bits. + ((int *) (newface->sh))[shmarkindex + 1] = 0; + if (useinsertradius) { + setfacetindex(*newface, 0); + } + // Set the boundary marker to zero. + setshellmark(*newface, 0); + + newface->shver = 0; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// makepoint() Create a new point. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::makepoint(point* pnewpoint, enum verttype vtype) +{ + int i; + + *pnewpoint = (point) points->alloc(); + + // Initialize the point attributes. + for (i = 0; i < numpointattrib; i++) { + (*pnewpoint)[3 + i] = 0.0; + } + // Initialize the metric tensor. + for (i = 0; i < sizeoftensor; i++) { + (*pnewpoint)[pointmtrindex + i] = 0.0; + } + setpoint2tet(*pnewpoint, NULL); + setpoint2ppt(*pnewpoint, NULL); + if (b->plc || b->refine) { + // Initialize the point-to-simplex field. + setpoint2sh(*pnewpoint, NULL); + if (b->metric && (bgm != NULL)) { + setpoint2bgmtet(*pnewpoint, NULL); + } + } + // Initialize the point marker (starting from in->firstnumber). + setpointmark(*pnewpoint, (int) (points->items) - (!in->firstnumber)); + // Clear all flags. + ((int *) (*pnewpoint))[pointmarkindex + 1] = 0; + // Initialize (set) the point type. + setpointtype(*pnewpoint, vtype); +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// initializepools() Calculate the sizes of the point, tetrahedron, and // +// subface. Initialize their memory pools. // +// // +// This routine also computes the indices 'pointmarkindex', 'point2simindex',// +// 'point2pbcptindex', 'elemattribindex', and 'volumeboundindex'. They are // +// used to find values within each point and tetrahedron, respectively. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::initializepools() +{ + int pointsize = 0, elesize = 0, shsize = 0; + int i; + + if (b->verbose) { + printf(" Initializing memorypools.\n"); + printf(" tetrahedron per block: %d.\n", b->tetrahedraperblock); + } + + inittables(); + + // There are three input point lists available, which are in, addin, + // and bgm->in. These point lists may have different number of + // attributes. Decide the maximum number. + numpointattrib = in->numberofpointattributes; + if (bgm != NULL) { + if (bgm->in->numberofpointattributes > numpointattrib) { + numpointattrib = bgm->in->numberofpointattributes; + } + } + if (addin != NULL) { + if (addin->numberofpointattributes > numpointattrib) { + numpointattrib = addin->numberofpointattributes; + } + } + if (b->weighted || b->flipinsert) { // -w or -L. + // The internal number of point attribute needs to be at least 1 + // (for storing point weights). + if (numpointattrib == 0) { + numpointattrib = 1; + } + } + + // Default varconstraint = 0; + if (in->segmentconstraintlist || in->facetconstraintlist) { + checkconstraints = 1; + } + if (b->plc || b->refine) { + // Save the insertion radius for Steiner points if boundaries + // are allowed be split. + if (!b->nobisect || checkconstraints) { + useinsertradius = 1; + } + } + + // The index within each point at which its metric tensor is found. + // Each vertex has three coordinates. + if (b->psc) { + // '-s' option (PSC), the u,v coordinates are provided. + pointmtrindex = 5 + numpointattrib; + // The index within each point at which its u, v coordinates are found. + // Comment: They are saved after the list of point attributes. + pointparamindex = pointmtrindex - 2; + } else { + pointmtrindex = 3 + numpointattrib; + } + // For '-m' option. A tensor field is provided (*.mtr or *.b.mtr file). + if (b->metric) { + // Decide the size (1, 3, or 6) of the metric tensor. + 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; } + if (useinsertradius) { + // Increase a space (REAL) for saving point insertion radius, it is + // saved directly after the metric. + sizeoftensor++; + } // 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: + // Increase the point size by three 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 { + // - a pointer to a subface or segment, read by point2sh(); + if (b->metric && (bgm != (tetgenmesh *) NULL)) { + // Increase one pointer into the background mesh, point2bgmtet(). 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 { + pointsize = (point2simindex + 3) * sizeof(tetrahedron); } } else { - // Increase the point size by one pointer, which is: + // Increase the point size by two pointer, which are: // - a pointer to a tet, read by point2tet(); - pointsize = (point2simindex + 1) * sizeof(tetrahedron); + // - a pointer to a parent point, read by point2ppt()). -- Used by btree. + pointsize = (point2simindex + 2) * 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: + // Now point size is the ints (indicated 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; + // - an integer for geometry tag (optional, -s option). + pointsize = (pointmarkindex + 2 + (b->psc ? 1 : 0)) * sizeof(tetrahedron); + // Initialize the pool of vertices. - points = new memorypool(pointsize, VERPERBLOCK, wtype, 0); + points = new memorypool(pointsize, b->vertexperblock, sizeof(REAL), 0); - if (b->useshelles) { - dummypoint = (point) new char[pointsize]; // For abovepoint. + if (b->verbose) { + printf(" Size of a point: %d bytes.\n", points->itembytes); } - // 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); + // Initialize the infinite vertex. + dummypoint = (point) new char[pointsize]; + // Initialize all fields of this point. + dummypoint[0] = 0.0; + dummypoint[1] = 0.0; + dummypoint[2] = 0.0; + for (i = 0; i < numpointattrib; i++) { + dummypoint[3 + i] = 0.0; + } + // Initialize the metric tensor. + for (i = 0; i < sizeoftensor; i++) { + dummypoint[pointmtrindex + i] = 0.0; } + setpoint2tet(dummypoint, NULL); + setpoint2ppt(dummypoint, NULL); + if (b->plc || b->psc || b->refine) { + // Initialize the point-to-simplex field. + setpoint2sh(dummypoint, NULL); + if (b->metric && (bgm != NULL)) { + setpoint2bgmtet(dummypoint, NULL); + } + } + // Initialize the point marker (starting from in->firstnumber). + setpointmark(dummypoint, -1); // The unique marker for dummypoint. + // Clear all flags. + ((int *) (dummypoint))[pointmarkindex + 1] = 0; + // Initialize (set) the point type. + setpointtype(dummypoint, UNUSEDVERTEX); // Does not matter. + + // The number of bytes occupied by a tetrahedron is varying by the user- + // specified options. The contents of the first 12 pointers are listed + // in the following table: + // [0] |__ neighbor at f0 __| + // [1] |__ neighbor at f1 __| + // [2] |__ neighbor at f2 __| + // [3] |__ neighbor at f3 __| + // [4] |_____ vertex p0 ____| + // [5] |_____ vertex p1 ____| + // [6] |_____ vertex p2 ____| + // [7] |_____ vertex p3 ____| + // [8] |__ segments array __| (used by -p) + // [9] |__ subfaces array __| (used by -p) + // [10] |_____ reserved _____| + // [11] |___ elem marker ____| (used as an integer) + + elesize = 12 * sizeof(tetrahedron); + + // The index to find the element markers. An integer containing varies + // flags and element counter. + assert(sizeof(int) <= sizeof(tetrahedron)); + assert((sizeof(tetrahedron) % sizeof(int)) == 0); + elemmarkerindex = (elesize - sizeof(tetrahedron)) / sizeof(int); + + // The actual number of element attributes. Note that if the + // `b->regionattrib' flag is set, an additional attribute will be added. + numelemattrib = in->numberoftetrahedronattributes + (b->regionattrib > 0); + // The index within each element at which its attributes are found, where // the index is measured in REALs. elemattribindex = (elesize + sizeof(REAL) - 1) / sizeof(REAL); - // The index within each element at which the maximum voulme bound is - // found, where the index is measured in REALs. Note that if the - // `b->regionattrib' flag is set, an additional attribute will be added. - volumeboundindex = elemattribindex + in->numberoftetrahedronattributes - + (b->regionattrib > 0); + // The index within each element at which the maximum volume bound is + // found, where the index is measured in REALs. + volumeboundindex = elemattribindex + numelemattrib; // If element attributes or an constraint are needed, increase the number // of bytes occupied by an element. if (b->varvolume) { elesize = (volumeboundindex + 1) * sizeof(REAL); - } else if (in->numberoftetrahedronattributes + b->regionattrib > 0) { + } else if (numelemattrib > 0) { elesize = volumeboundindex * sizeof(REAL); } - // 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); + tetrahedrons = new memorypool(elesize, b->tetrahedraperblock, sizeof(void *), + 16); + + if (b->verbose) { + printf(" Size of a tetrahedron: %d (%d) bytes.\n", elesize, + tetrahedrons->itembytes); + } - if (b->useshelles) { + if (b->plc || b->refine) { // 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); + // three to subsegments, two to tetrahedra. + shsize = 11 * 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) { + if (checkconstraints) { shsize = (areaboundindex + 1) * sizeof(REAL); } else { shsize = areaboundindex * sizeof(REAL); @@ -5100,1138 +4778,347 @@ void tetgenmesh::initializepools() shmarkindex = (shsize + sizeof(int) - 1) / sizeof(int); // Increase the number of bytes by two or three integers, one for facet // marker, one for shellface type, and optionally one for pbc group. - shsize = (shmarkindex + 2 + checkpbcs) * sizeof(int); + shsize = (shmarkindex + 2) * sizeof(shellface); + if (useinsertradius) { + // Increase the number of byte by one integer for storing facet index. + // set/read by setfacetindex() and getfacetindex. + shsize = (shmarkindex + 3) * sizeof(shellface); + } + // Initialize the pool of subfaces. Each subface record is eight-byte // aligned so it has room to store an edge version (from 0 to 5) in // the least three bits. - subfaces = new memorypool(shsize, SUBPERBLOCK, POINTER, 8); + subfaces = new memorypool(shsize, b->shellfaceperblock, sizeof(void *), 8); + + if (b->verbose) { + printf(" Size of a shellface: %d (%d) bytes.\n", shsize, + subfaces->itembytes); + } + // Initialize the pool of subsegments. The subsegment's record is same // with subface. - subsegs = new memorypool(shsize, SUBPERBLOCK, POINTER, 8); + subsegs = new memorypool(shsize, b->shellfaceperblock, sizeof(void *), 8); + // Initialize the pool for tet-subseg connections. - tet2segpool = new memorypool(6*sizeof(shellface), SUBPERBLOCK, POINTER, 0); + tet2segpool = new memorypool(6 * sizeof(shellface), b->shellfaceperblock, + sizeof(void *), 0); // Initialize the pool for tet-subface connections. - tet2subpool = new memorypool(4*sizeof(shellface), SUBPERBLOCK, POINTER, 0); + tet2subpool = new memorypool(4 * sizeof(shellface), b->shellfaceperblock, + sizeof(void *), 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); - } -} + subvertstack = new arraypool(sizeof(point), 8); -/////////////////////////////////////////////////////////////////////////////// -// // -// 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; + // Initialize arraypools for surface point insertion/deletion. + caveshlist = new arraypool(sizeof(face), 8); + caveshbdlist = new arraypool(sizeof(face), 8); + cavesegshlist = new arraypool(sizeof(face), 4); - 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]); - } + cavetetshlist = new arraypool(sizeof(face), 8); + cavetetseglist = new arraypool(sizeof(face), 8); + caveencshlist = new arraypool(sizeof(face), 8); + caveencseglist = new arraypool(sizeof(face), 8); } - tetrahedrons->dealloc((void *) dyingtetrahedron); + // Initialize the pools for flips. + flippool = new memorypool(sizeof(badface), 1024, sizeof(void *), 0); + unflipqueue = new arraypool(sizeof(badface), 10); + + // Initialize the arraypools for point insertion. + cavetetlist = new arraypool(sizeof(triface), 10); + cavebdrylist = new arraypool(sizeof(triface), 10); + caveoldtetlist = new arraypool(sizeof(triface), 10); + cavetetvertlist = new arraypool(sizeof(point), 10); } -/////////////////////////////////////////////////////////////////////////////// -// // -// tetrahedrontraverse() Traverse the tetrahedra, skipping dead ones. // -// // -/////////////////////////////////////////////////////////////////////////////// +//// //// +//// //// +//// mempool_cxx ////////////////////////////////////////////////////////////// -tetgenmesh::tetrahedron* tetgenmesh::tetrahedrontraverse() -{ - tetrahedron *newtetrahedron; +//// geom_cxx ///////////////////////////////////////////////////////////////// +//// //// +//// //// - do { - newtetrahedron = (tetrahedron *) tetrahedrons->traverse(); - if (newtetrahedron == (tetrahedron *) NULL) { - return (tetrahedron *) NULL; - } - } while (newtetrahedron[7] == (tetrahedron) NULL); // Skip dead ones. - return newtetrahedron; -} +// PI is the ratio of a circle's circumference to its diameter. +REAL tetgenmesh::PI = 3.14159265358979323846264338327950288419716939937510582; /////////////////////////////////////////////////////////////////////////////// // // -// shellfacedealloc() Deallocate space for a shellface, marking it dead. // -// Used both for dealloc a subface and subsegment. // +// insphere_s() Insphere test with symbolic perturbation. // // // -/////////////////////////////////////////////////////////////////////////////// - -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); -} - -/////////////////////////////////////////////////////////////////////////////// +// Given four points pa, pb, pc, and pd, test if the point pe lies inside or // +// outside the circumscribed sphere of the four points. // // // -// shellfacetraverse() Traverse the subfaces, skipping dead ones. Used // -// for both subfaces and subsegments pool traverse. // +// Here we assume that the 3d orientation of the point sequence {pa, pb, pc, // +// pd} is positive (NOT zero), i.e., pd lies above the plane passing through // +// points pa, pb, and pc. Otherwise, the returned sign is flipped. // +// // +// Return a positive value (> 0) if pe lies inside, a negative value (< 0) // +// if pe lies outside the sphere, the returned value will not be zero. // // // /////////////////////////////////////////////////////////////////////////////// -tetgenmesh::shellface* tetgenmesh::shellfacetraverse(memorypool *pool) +REAL tetgenmesh::insphere_s(REAL* pa, REAL* pb, REAL* pc, REAL* pd, REAL* pe) { - shellface *newshellface; + REAL sign; + + sign = insphere(pa, pb, pc, pd, pe); + if (sign != 0.0) { + return sign; + } + + // Symbolic perturbation. + point pt[5], swappt; + REAL oriA, oriB; + int swaps, count; + int n, i; + pt[0] = pa; + pt[1] = pb; + pt[2] = pc; + pt[3] = pd; + pt[4] = pe; + + // Sort the five points such that their indices are in the increasing + // order. An optimized bubble sort algorithm is used, i.e., it has + // the worst case O(n^2) runtime, but it is usually much faster. + swaps = 0; // Record the total number of swaps. + n = 5; do { - newshellface = (shellface *) pool->traverse(); - if (newshellface == (shellface *) NULL) { - return (shellface *) NULL; + 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++; + } } - } while (newshellface[3] == (shellface) NULL); // Skip dead ones. - return newshellface; + swaps += count; + } while (count > 0); // Continue if some points are swapped. + + oriA = orient3d(pt[1], pt[2], pt[3], pt[4]); + if (oriA != 0.0) { + // Flip the sign if there are odd number of swaps. + if ((swaps % 2) != 0) oriA = -oriA; + return oriA; + } + + oriB = -orient3d(pt[0], pt[2], pt[3], pt[4]); + assert(oriB != 0.0); // SELF_CHECK + // Flip the sign if there are odd number of swaps. + if ((swaps % 2) != 0) oriB = -oriB; + return oriB; } /////////////////////////////////////////////////////////////////////////////// // // -// badfacedealloc() Deallocate space for a badface, marking it dead. // +// orient4d_s() 4d orientation test with symbolic perturbation. // // // -/////////////////////////////////////////////////////////////////////////////// - -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); -} - -/////////////////////////////////////////////////////////////////////////////// +// Given four lifted points pa', pb', pc', and pd' in R^4,test if the lifted // +// point pe' in R^4 lies below or above the hyperplane passing through the // +// four points pa', pb', pc', and pd'. // +// // +// Here we assume that the 3d orientation of the point sequence {pa, pb, pc, // +// pd} is positive (NOT zero), i.e., pd lies above the plane passing through // +// the points pa, pb, and pc. Otherwise, the returned sign is flipped. // // // -// badfacetraverse() Traverse the pools, skipping dead ones. // +// Return a positive value (> 0) if pe' lies below, a negative value (< 0) // +// if pe' lies above the hyperplane, the returned value should not be zero. // // // /////////////////////////////////////////////////////////////////////////////// -tetgenmesh::badface* tetgenmesh::badfacetraverse(memorypool *pool) +REAL tetgenmesh::orient4d_s(REAL* pa, REAL* pb, REAL* pc, REAL* pd, REAL* pe, + REAL aheight, REAL bheight, REAL cheight, + REAL dheight, REAL eheight) { - badface *newsh; + REAL sign; - do { - newsh = (badface *) pool->traverse(); - if (newsh == (badface *) NULL) { - return (badface *) NULL; - } - } while (newsh->forg == (point) NULL); // Skip dead ones. - return newsh; -} + sign = orient4d(pa, pb, pc, pd, pe, + aheight, bheight, cheight, dheight, eheight); + if (sign != 0.0) { + return sign; + } -/////////////////////////////////////////////////////////////////////////////// -// // -// 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; + // 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 { - newpoint = (point) points->traverse(); - if (newpoint == (point) NULL) { - return (point) NULL; + 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++; + } } - } while (pointtype(newpoint) == DEADVERTEX); // Skip dead ones. - return newpoint; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// maketetrahedron() Create a new tetrahedron. // -// // -/////////////////////////////////////////////////////////////////////////////// + swaps += count; + } while (count > 0); // Continue if some points are swapped. -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); + 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; } - // Initialize the marker (for flags). - setelemmarker(newtet->tet, 0); - // Initialize the location and version to be Zero. - newtet->loc = 0; - newtet->ver = 0; + + 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; } /////////////////////////////////////////////////////////////////////////////// // // -// makeshellface() Create a new shellface with version zero. Used for // -// both subfaces and seusegments. // +// tri_edge_test() Triangle-edge intersection test. // // // -/////////////////////////////////////////////////////////////////////////////// - -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); - } - // 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; -} - -/////////////////////////////////////////////////////////////////////////////// +// 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. // // // -// makepoint() Create a new point. // +// 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. // +// // +// If T and E intersect each other, they may intersect in different ways. If // +// 'level' > 0, their intersection type will be reported 'types' and 'pos'. // +// // +// The return value indicates one of the following cases: // +// - 0, T and E are disjoint. // +// - 1, T and E intersect each other. // +// - 2, T and E are not coplanar. They intersect at a single point. // +// - 4, T and E are coplanar. They intersect at a single point or a line // +// segment (if types[1] != DISJOINT). // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::makepoint(point* pnewpoint) +#define SETVECTOR3(V, a0, a1, a2) (V)[0] = (a0); (V)[1] = (a1); (V)[2] = (a2) + +#define SWAP2(a0, a1, tmp) (tmp) = (a0); (a0) = (a1); (a1) = (tmp) + +int tetgenmesh::tri_edge_2d(point A, point B, point C, point P, point Q, + point R, int level, int *types, int *pos) { - int ptmark, i; + point U[3], V[3]; // The permuted vectors of points. + int pu[3], pv[3]; // The original positions of points. + REAL abovept[3]; + REAL sA, sB, sC; + REAL s1, s2, s3, s4; + int z1; - *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); - 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); + if (R == NULL) { + // Calculate a lift point. + if (1) { + REAL n[3], len; + // Calculate a lift point, saved in dummypoint. + facenormal(A, B, C, n, 1, NULL); + len = sqrt(dot(n, n)); + if (len != 0) { + n[0] /= len; + n[1] /= len; + n[2] /= len; + len = distance(A, B); + len += distance(B, C); + len += distance(C, A); + len /= 3.0; + R = abovept; //dummypoint; + R[0] = A[0] + len * n[0]; + R[1] = A[1] + len * n[1]; + R[2] = A[2] + len * n[2]; + } else { + // The triangle [A,B,C] is (nearly) degenerate, i.e., it is (close) + // to a line. We need a line-line intersection test. + //assert(0); + // !!! A non-save return value.!!! + return 0; // DISJOINT + } } } - // 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 ////////////////////////////////////////////////////////////// - -//// geom_cxx ///////////////////////////////////////////////////////////////// -//// //// -//// //// - -// PI is the ratio of a circle's circumference to its diameter. - -REAL tetgenmesh::PI = 3.14159265358979323846264338327950288419716939937510582; - -/////////////////////////////////////////////////////////////////////////////// -// // -// 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. // -// // -/////////////////////////////////////////////////////////////////////////////// - -// 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. + // 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); -/////////////////////////////////////////////////////////////////////////////// -// // -// edge_vert_col_inter() Test whether an edge (ab) and a collinear vertex // -// (p) are intersecting or not. // -// // -// 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 return value indicates one of the three cases: DISJOINT, SHAREVERTEX // -// (p = a or p = b), and INTERSECT (a < p < b). // -// // -/////////////////////////////////////////////////////////////////////////////// -enum tetgenmesh::interresult tetgenmesh::edge_vert_col_inter(REAL* A, REAL* B, - REAL* 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; + 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 { - // assert(P[i] == B[i]); - return SHAREVERTEX; + 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 { - // 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; + 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 { - // 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; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// 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. // -// // -// The return value indicates one of the four cases: DISJOINT, SHAREVERTEX, // -// SHAREEDGE, and INTERSECT. // -// // -/////////////////////////////////////////////////////////////////////////////// - -enum tetgenmesh::interresult tetgenmesh:: edge_edge_cop_inter(REAL* A, REAL* B, - REAL* P, REAL* Q, REAL* R) -{ - 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; - } - - // 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; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// 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. // -// // -/////////////////////////////////////////////////////////////////////////////// - -/////////////////////////////////////////////////////////////////////////////// -// // -// 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. // -// // -// The return value indicates one of the four cases: DISJOINT, SHAREVERTEX, // -// and INTERSECT. // -// // -/////////////////////////////////////////////////////////////////////////////// - -enum tetgenmesh::interresult tetgenmesh::tri_vert_cop_inter(REAL* A, REAL* B, - REAL* C, REAL* P, REAL* R) -{ - 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; - - // 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; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// 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 return value indicates one of the four cases: DISJOINT, SHAREVERTEX, // -// SHAREEDGE, and INTERSECT. // -// // -/////////////////////////////////////////////////////////////////////////////// - -enum tetgenmesh::interresult tetgenmesh::tri_edge_cop_inter(REAL* A, REAL* B, - REAL* C, REAL* P, REAL* Q, REAL* R) -{ - enum interresult abpq, bcpq, capq; - enum interresult abcp, abcq; - - // 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; - } - - // They are disjointed. - return DISJOINT; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// tri_edge_inter_tail() Test whether a triangle (abc) and an edge (pq) // -// are intersecting or not. // -// // -// 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. // -// // -/////////////////////////////////////////////////////////////////////////////// - -enum tetgenmesh::interresult tetgenmesh::tri_edge_inter_tail(REAL* A, REAL* B, - REAL* C, REAL* P, REAL* Q, REAL s1, REAL s2) -{ - 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); - } - } - - // 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); -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// tri_edge_inter() Test whether a triangle (abc) and an edge (pq) are // -// intersecting or not. // -// // -// The return value indicates one of the four cases: DISJOINT, SHAREVERTEX, // -// SHAREEDGE, and INTERSECT. // -// // -/////////////////////////////////////////////////////////////////////////////// - -enum tetgenmesh::interresult tetgenmesh::tri_edge_inter(REAL* A, REAL* B, - REAL* C, REAL* P, REAL* Q) -{ - 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); - - return tri_edge_inter_tail(A, B, C, P, Q, s1, s2); -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// 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. // -// // -/////////////////////////////////////////////////////////////////////////////// - -enum tetgenmesh::interresult tetgenmesh::tri_tri_inter(REAL* A, REAL* B, - REAL* C, REAL* O, REAL* P, REAL* Q) -{ - REAL s_o, s_p, s_q; - REAL s_a, s_b, s_c; - - 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; - } - - 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; - } - - enum interresult abcop, abcpq, abcqo; - int shareedge = 0; - - 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; - } - - // They are disjoint. - return DISJOINT; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// 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]. // -// // -// 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'. // -// // -/////////////////////////////////////////////////////////////////////////////// - -int tetgenmesh::tri_edge_2d(point A, point B, point C, point P, point Q, - point R, int level, int *types, int *pos) -{ - 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; - - 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')); - } - // triedgcopcount++; - - 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; - } + 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; + } } } } @@ -6364,6 +5251,11 @@ int tetgenmesh::tri_edge_2d(point A, point B, point C, point P, point Q, z1 = 3; } else { // (000) // Not possible unless ABC is degenerate. + // Avoiding compiler warnings. + SETVECTOR3(U, A, B, C); // I3 + SETVECTOR3(V, P, Q, R); // I2 + SETVECTOR3(pu, 0, 1, 2); + SETVECTOR3(pv, 0, 1, 2); z1 = 4; } } @@ -6374,15 +5266,6 @@ int tetgenmesh::tri_edge_2d(point A, point B, point C, point P, point Q, 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; @@ -6395,34 +5278,35 @@ int tetgenmesh::tri_edge_2d(point A, point B, point C, point P, point Q, return 1; // They are intersected. } + assert(z1 != 4); // SELF_CHECK + if (z1 == 1) { if (s1 == 0) { // (0###) // C = Q. - types[0] = (int) SHAREVERTEX; + types[0] = (int) SHAREVERT; 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; + types[0] = (int) SHAREVERT; pos[0] = pu[2]; // C pos[1] = pv[0]; // P types[1] = (int) DISJOINT; } else { // (-+##) // C in [P, Q]. - types[0] = (int) INTERVERT; + types[0] = (int) ACROSSVERT; pos[0] = pu[2]; // C pos[1] = pv[0]; // [P, Q] types[1] = (int) DISJOINT; } } - return 1; + return 4; } 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) { @@ -6430,7 +5314,7 @@ int tetgenmesh::tri_edge_2d(point A, point B, point C, point P, point Q, assert(s2 > 0); // SELF_CHECK if (s4 > 0) { // [P, Q] overlaps [k, l] (-+++). - types[0] = (int) INTEREDGE; + types[0] = (int) ACROSSEDGE; pos[0] = pu[2]; // [C, A] pos[1] = pv[0]; // [P, Q] types[1] = (int) TOUCHFACE; @@ -6439,7 +5323,7 @@ int tetgenmesh::tri_edge_2d(point A, point B, point C, point P, point Q, } else { if (s4 == 0) { // Q = l, [P, Q] contains [k, l] (-++0). - types[0] = (int) INTEREDGE; + types[0] = (int) ACROSSEDGE; pos[0] = pu[2]; // [C, A] pos[1] = pv[0]; // [P, Q] types[1] = (int) TOUCHEDGE; @@ -6447,10 +5331,10 @@ int tetgenmesh::tri_edge_2d(point A, point B, point C, point P, point Q, pos[3] = pv[1]; // Q } else { // s4 < 0 // [P, Q] contains [k, l] (-++-). - types[0] = (int) INTEREDGE; + types[0] = (int) ACROSSEDGE; pos[0] = pu[2]; // [C, A] pos[1] = pv[0]; // [P, Q] - types[1] = (int) INTEREDGE; + types[1] = (int) ACROSSEDGE; pos[2] = pu[1]; // [B, C] pos[3] = pv[0]; // [P, Q] } @@ -6480,7 +5364,7 @@ int tetgenmesh::tri_edge_2d(point A, point B, point C, point P, point Q, types[0] = (int) TOUCHEDGE; pos[0] = pu[2]; // [C, A] pos[1] = pv[0]; // P - types[1] = (int) INTEREDGE; + types[1] = (int) ACROSSEDGE; pos[2] = pu[1]; // [B, C] pos[3] = pv[0]; // [P, Q] } @@ -6509,7 +5393,7 @@ int tetgenmesh::tri_edge_2d(point A, point B, point C, point P, point Q, types[0] = (int) TOUCHFACE; pos[0] = 3; // [A, B, C] pos[1] = pv[0]; // P - types[1] = (int) INTEREDGE; + types[1] = (int) ACROSSEDGE; pos[2] = pu[1]; // [B, C] pos[3] = pv[0]; // [P, Q] } @@ -6536,7 +5420,7 @@ int tetgenmesh::tri_edge_2d(point A, point B, point C, point P, point Q, assert(s2 > 0); // SELF_CHECK if (s4 > 0) { // [P, Q] overlaps [A, l] (-+++). - types[0] = (int) INTERVERT; + types[0] = (int) ACROSSVERT; pos[0] = pu[0]; // A pos[1] = pv[0]; // [P, Q] types[1] = (int) TOUCHFACE; @@ -6545,7 +5429,7 @@ int tetgenmesh::tri_edge_2d(point A, point B, point C, point P, point Q, } else { if (s4 == 0) { // Q = l, [P, Q] contains [A, l] (-++0). - types[0] = (int) INTERVERT; + types[0] = (int) ACROSSVERT; pos[0] = pu[0]; // A pos[1] = pv[0]; // [P, Q] types[1] = (int) TOUCHEDGE; @@ -6553,10 +5437,10 @@ int tetgenmesh::tri_edge_2d(point A, point B, point C, point P, point Q, pos[3] = pv[1]; // Q } else { // s4 < 0 // [P, Q] contains [A, l] (-++-). - types[0] = (int) INTERVERT; + types[0] = (int) ACROSSVERT; pos[0] = pu[0]; // A pos[1] = pv[0]; // [P, Q] - types[1] = (int) INTEREDGE; + types[1] = (int) ACROSSEDGE; pos[2] = pu[1]; // [B, C] pos[3] = pv[0]; // [P, Q] } @@ -6566,7 +5450,7 @@ int tetgenmesh::tri_edge_2d(point A, point B, point C, point P, point Q, assert(s2 > 0); // SELF_CHECK if (s4 > 0) { // P = A, [P, Q] in [A, l] (-+0+). - types[0] = (int) SHAREVERTEX; + types[0] = (int) SHAREVERT; pos[0] = pu[0]; // A pos[1] = pv[0]; // P types[1] = (int) TOUCHFACE; @@ -6575,7 +5459,7 @@ int tetgenmesh::tri_edge_2d(point A, point B, point C, point P, point Q, } else { if (s4 == 0) { // [P, Q] = [A, l] (-+00). - types[0] = (int) SHAREVERTEX; + types[0] = (int) SHAREVERT; pos[0] = pu[0]; // A pos[1] = pv[0]; // P types[1] = (int) TOUCHEDGE; @@ -6583,10 +5467,10 @@ int tetgenmesh::tri_edge_2d(point A, point B, point C, point P, point Q, pos[3] = pv[1]; // Q } else { // s4 < 0 // Q = l, [P, Q] in [A, l] (-+0-). - types[0] = (int) SHAREVERTEX; + types[0] = (int) SHAREVERT; pos[0] = pu[0]; // A pos[1] = pv[0]; // P - types[1] = (int) INTEREDGE; + types[1] = (int) ACROSSEDGE; pos[2] = pu[1]; // [B, C] pos[3] = pv[0]; // [P, Q] } @@ -6615,7 +5499,7 @@ int tetgenmesh::tri_edge_2d(point A, point B, point C, point P, point Q, types[0] = (int) TOUCHFACE; pos[0] = 3; // [A, B, C] pos[1] = pv[0]; // P - types[0] = (int) INTEREDGE; + types[0] = (int) ACROSSEDGE; pos[0] = pu[1]; // [B, C] pos[1] = pv[0]; // [P, Q] } @@ -6631,7 +5515,7 @@ int tetgenmesh::tri_edge_2d(point A, point B, point C, point P, point Q, } } else { // s1 == 0 // Q = A (0###). - types[0] = (int) SHAREVERTEX; + types[0] = (int) SHAREVERT; pos[0] = pu[0]; // A pos[1] = pv[1]; // Q types[1] = (int) DISJOINT; @@ -6642,7 +5526,7 @@ int tetgenmesh::tri_edge_2d(point A, point B, point C, point P, point Q, assert(s2 > 0); // SELF_CHECK if (s4 > 0) { // [P, Q] overlaps [A, B] (-+++). - types[0] = (int) INTERVERT; + types[0] = (int) ACROSSVERT; pos[0] = pu[0]; // A pos[1] = pv[0]; // [P, Q] types[1] = (int) TOUCHEDGE; @@ -6651,18 +5535,18 @@ int tetgenmesh::tri_edge_2d(point A, point B, point C, point P, point Q, } else { if (s4 == 0) { // Q = B, [P, Q] contains [A, B] (-++0). - types[0] = (int) INTERVERT; + types[0] = (int) ACROSSVERT; pos[0] = pu[0]; // A pos[1] = pv[0]; // [P, Q] - types[1] = (int) SHAREVERTEX; + types[1] = (int) SHAREVERT; pos[2] = pu[1]; // B pos[3] = pv[1]; // Q } else { // s4 < 0 // [P, Q] contains [A, B] (-++-). - types[0] = (int) INTERVERT; + types[0] = (int) ACROSSVERT; pos[0] = pu[0]; // A pos[1] = pv[0]; // [P, Q] - types[1] = (int) INTERVERT; + types[1] = (int) ACROSSVERT; pos[2] = pu[1]; // B pos[3] = pv[0]; // [P, Q] } @@ -6672,7 +5556,7 @@ int tetgenmesh::tri_edge_2d(point A, point B, point C, point P, point Q, assert(s2 > 0); // SELF_CHECK if (s4 > 0) { // P = A, [P, Q] in [A, B] (-+0+). - types[0] = (int) SHAREVERTEX; + types[0] = (int) SHAREVERT; pos[0] = pu[0]; // A pos[1] = pv[0]; // P types[1] = (int) TOUCHEDGE; @@ -6687,10 +5571,10 @@ int tetgenmesh::tri_edge_2d(point A, point B, point C, point P, point Q, types[1] = (int) DISJOINT; } else { // s4 < 0 // P= A, [P, Q] in [A, B] (-+0-). - types[0] = (int) SHAREVERTEX; + types[0] = (int) SHAREVERT; pos[0] = pu[0]; // A pos[1] = pv[0]; // P - types[1] = (int) INTERVERT; + types[1] = (int) ACROSSVERT; pos[2] = pu[1]; // B pos[3] = pv[0]; // [P, Q] } @@ -6711,7 +5595,7 @@ int tetgenmesh::tri_edge_2d(point A, point B, point C, point P, point Q, types[0] = (int) TOUCHEDGE; pos[0] = pu[0]; // [A, B] pos[1] = pv[0]; // P - types[1] = (int) SHAREVERTEX; + types[1] = (int) SHAREVERT; pos[2] = pu[1]; // B pos[3] = pv[1]; // Q } else { // s4 < 0 @@ -6719,14 +5603,14 @@ int tetgenmesh::tri_edge_2d(point A, point B, point C, point P, point Q, types[0] = (int) TOUCHEDGE; pos[0] = pu[0]; // [A, B] pos[1] = pv[0]; // P - types[1] = (int) INTERVERT; + types[1] = (int) ACROSSVERT; pos[2] = pu[1]; // B pos[3] = pv[0]; // [P, Q] } } } else { // s2 == 0 // P = B (#0##). - types[0] = (int) SHAREVERTEX; + types[0] = (int) SHAREVERT; pos[0] = pu[1]; // B pos[1] = pv[0]; // P types[1] = (int) DISJOINT; @@ -6735,52 +5619,24 @@ int tetgenmesh::tri_edge_2d(point A, point B, point C, point P, point Q, } } else { // s1 == 0 // Q = A (0###). - types[0] = (int) SHAREVERTEX; + types[0] = (int) SHAREVERT; pos[0] = pu[0]; // A pos[1] = pv[1]; // Q types[1] = (int) DISJOINT; } } - return 1; + return 4; } -/////////////////////////////////////////////////////////////////////////////// -// // -// 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. // -// // -// 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'. // -// // -/////////////////////////////////////////////////////////////////////////////// - -int tetgenmesh::tri_edge_test(point A, point B, point C, point P, point Q, - point R, int level, int *types, int *pos) +int tetgenmesh::tri_edge_tail(point A,point B,point C,point P,point Q,point R, + REAL sP,REAL sQ,int level,int *types,int *pos) { point U[3], V[3]; //, Ptmp; int pu[3], pv[3]; //, itmp; - REAL sP, sQ, s1, s2, s3; + REAL 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++; if (sP < 0) { if (sQ < 0) { // (--) disjoint @@ -6846,28 +5702,21 @@ int tetgenmesh::tri_edge_test(point A, point B, point C, point P, point Q, return tri_edge_2d(A, B, C, P, Q, R, level, types, pos); } - s1 = orient3d(U[0], U[1], V[0], V[1]); orient3dcount++; + s1 = orient3d(U[0], U[1], V[0], V[1]); if (s1 < 0) { return 0; } - s2 = orient3d(U[1], U[2], V[0], V[1]); orient3dcount++; + s2 = orient3d(U[1], U[2], V[0], V[1]); if (s2 < 0) { return 0; } - s3 = orient3d(U[2], U[0], V[0], V[1]); orient3dcount++; + s3 = orient3d(U[2], U[0], V[0], V[1]); if (s3 < 0) { return 0; } - 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. } @@ -6879,24 +5728,24 @@ int tetgenmesh::tri_edge_test(point A, point B, point C, point P, point Q, if (s2 > 0) { if (s3 > 0) { // (+++) // [P, Q] passes interior of [A, B, C]. - types[0] = (int) INTERFACE; + types[0] = (int) ACROSSFACE; 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; + types[0] = (int) ACROSSEDGE; 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; + types[0] = (int) ACROSSEDGE; pos[0] = pu[1]; // [B, C] pos[1] = 0; // [P, Q] } else { // s3 == 0 (+00) // [P, Q] passes C. - types[0] = (int) INTERVERT; + types[0] = (int) ACROSSVERT; pos[0] = pu[2]; // C pos[1] = 0; // [P, Q] } @@ -6905,19 +5754,19 @@ int tetgenmesh::tri_edge_test(point A, point B, point C, point P, point Q, if (s2 > 0) { if (s3 > 0) { // (0++) // [P, Q] intersects [A, B]. - types[0] = (int) INTEREDGE; + types[0] = (int) ACROSSEDGE; pos[0] = pu[0]; // [A, B] pos[1] = 0; // [P, Q] } else { // s3 == 0 (0+0) // [P, Q] passes A. - types[0] = (int) INTERVERT; + types[0] = (int) ACROSSVERT; 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; + types[0] = (int) ACROSSVERT; pos[0] = pu[1]; // B pos[1] = 0; // [P, Q] } else { // s3 == 0 (000) @@ -6948,7 +5797,7 @@ int tetgenmesh::tri_edge_test(point A, point B, point C, point P, point Q, pos[1] = pv[1]; // Q } else { // s3 == 0 (+00) // Q = C. - types[0] = (int) SHAREVERTEX; + types[0] = (int) SHAREVERT; pos[0] = pu[2]; // C pos[1] = pv[1]; // Q } @@ -6962,14 +5811,14 @@ int tetgenmesh::tri_edge_test(point A, point B, point C, point P, point Q, pos[1] = pv[1]; // Q } else { // s3 == 0 (0+0) // Q = A. - types[0] = (int) SHAREVERTEX; + types[0] = (int) SHAREVERT; pos[0] = pu[0]; // A pos[1] = pv[1]; // Q } } else { // s2 == 0 if (s3 > 0) { // (00+) // Q = B. - types[0] = (int) SHAREVERTEX; + types[0] = (int) SHAREVERT; pos[0] = pu[1]; // B pos[1] = pv[1]; // Q } else { // s3 == 0 (000) @@ -6980,250 +5829,176 @@ int tetgenmesh::tri_edge_test(point A, point B, point C, point P, point Q, } } - return 1; + // T and E intersect in a single point. + return 2; } -/////////////////////////////////////////////////////////////////////////////// -// // -// 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) +int tetgenmesh::tri_edge_test(point A, point B, point C, point P, point Q, + point R, int level, int *types, int *pos) { - 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); - } else { - // The four points are collinear. This case only happens on the boundary. - return 0; // Return "not inside". - } - } + REAL sP, sQ; - sign = d - r; - if (fabs(sign) / r < b->epsilon) { - sign = 0; - } + // Test the locations of P and Q with respect to ABC. + sP = orient3d(A, B, C, P); + sQ = orient3d(A, B, C, Q); - return sign; + return tri_edge_tail(A, B, C, P, Q, R, sP, sQ, level, types, pos); } /////////////////////////////////////////////////////////////////////////////// // // -// 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.// +// tri_tri_inter() Test whether two triangle (abc) and (opq) are // +// intersecting or not. // // // -// 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. // +// Return 0 if they are disjoint. Otherwise, return 1. 'type' returns one of // +// the four cases: SHAREVERTEX, SHAREEDGE, SHAREFACE, and INTERSECT. // // // /////////////////////////////////////////////////////////////////////////////// -REAL tetgenmesh::insphere_s(REAL* pa, REAL* pb, REAL* pc, REAL* pd, REAL* pe) +int tetgenmesh::tri_edge_inter_tail(REAL* A, REAL* B, REAL* C, REAL* P, + REAL* Q, REAL s_p, REAL s_q) { - REAL sign; + int types[2], pos[4]; + int ni; // =0, 2, 4 - inspherecount++; + ni = tri_edge_tail(A, B, C, P, Q, NULL, s_p, s_q, 1, types, pos); - sign = insphere(pa, pb, pc, pd, pe); - if (sign != 0.0) { - return sign; + if (ni > 0) { + if (ni == 2) { + // Get the intersection type. + if (types[0] == (int) SHAREVERT) { + return (int) SHAREVERT; + } else { + return (int) INTERSECT; + } + } else if (ni == 4) { + // There may be two intersections. + if (types[0] == (int) SHAREVERT) { + if (types[1] == (int) DISJOINT) { + return (int) SHAREVERT; + } else { + assert(types[1] != (int) SHAREVERT); + return (int) INTERSECT; + } + } else { + if (types[0] == (int) SHAREEDGE) { + return (int) SHAREEDGE; + } else { + return (int) INTERSECT; + } + } + } else { + assert(0); + } } - insphere_sos_count++; + return (int) DISJOINT; +} - // Symbolic perturbation. - point pt[5], swappt; - REAL oriA, oriB; - int swaps, count; - int n, i; +int tetgenmesh::tri_tri_inter(REAL* A,REAL* B,REAL* C,REAL* O,REAL* P,REAL* Q) +{ + REAL s_o, s_p, s_q; + REAL s_a, s_b, s_c; - 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. + 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 0; // DISJOINT; + } - 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; + 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 0; // DISJOINT; } - - 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. // -// // -// '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. // -// // -/////////////////////////////////////////////////////////////////////////////// + int abcop, abcpq, abcqo; + int shareedge = 0; -bool tetgenmesh::iscollinear(REAL* A, REAL* B, REAL* C, REAL eps) -{ - REAL abx, aby, abz; - REAL acx, acy, acz; - REAL Lv, Lw, dd; - REAL d, q; - - // Limit of two closed points. - q = longest * eps; - q *= q; - - 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; -} + abcop = tri_edge_inter_tail(A, B, C, O, P, s_o, s_p); + if (abcop == (int) INTERSECT) { + return (int) INTERSECT; + } else if (abcop == (int) SHAREEDGE) { + shareedge++; + } + abcpq = tri_edge_inter_tail(A, B, C, P, Q, s_p, s_q); + if (abcpq == (int) INTERSECT) { + return (int) INTERSECT; + } else if (abcpq == (int) SHAREEDGE) { + shareedge++; + } + abcqo = tri_edge_inter_tail(A, B, C, Q, O, s_q, s_o); + if (abcqo == (int) INTERSECT) { + return (int) INTERSECT; + } else if (abcqo == (int) SHAREEDGE) { + shareedge++; + } + if (shareedge == 3) { + // opq are coincident with abc. + return (int) SHAREFACE; + } -/////////////////////////////////////////////////////////////////////////////// -// // -// iscoplanar() Check if four points are approximately coplanar. // -// // -// '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. // -// // -/////////////////////////////////////////////////////////////////////////////// + // It is only possible either no share edge or one. + assert(shareedge == 0 || shareedge == 1); -bool tetgenmesh:: -iscoplanar(REAL* k, REAL* l, REAL* m, REAL* n, REAL vol6, REAL eps) -{ - REAL L, q; - REAL x, y, z; - - 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; -} + // Continue to detect whether opq and abc are intersecting or not. + int opqab, opqbc, opqca; -/////////////////////////////////////////////////////////////////////////////// -// // -// 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. // -// // -/////////////////////////////////////////////////////////////////////////////// + opqab = tri_edge_inter_tail(O, P, Q, A, B, s_a, s_b); + if (opqab == (int) INTERSECT) { + return (int) INTERSECT; + } + opqbc = tri_edge_inter_tail(O, P, Q, B, C, s_b, s_c); + if (opqbc == (int) INTERSECT) { + return (int) INTERSECT; + } + opqca = tri_edge_inter_tail(O, P, Q, C, A, s_c, s_a); + if (opqca == (int) INTERSECT) { + return (int) INTERSECT; + } -bool tetgenmesh:: -iscospheric(REAL* k, REAL* l, REAL* m, REAL* n, REAL* o, REAL vol24, REAL eps) -{ - REAL L, q; - - // 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); - - return q < eps; + // 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 == (int) SHAREEDGE) { + assert((abcpq == (int) SHAREVERT) && (abcqo == (int) SHAREVERT)); + // op is coincident with an edge of abc. + return (int) SHAREEDGE; + } + if (abcpq == (int) SHAREEDGE) { + assert((abcop == (int) SHAREVERT) && (abcqo == (int) SHAREVERT)); + // pq is coincident with an edge of abc. + return (int) SHAREEDGE; + } + if (abcqo == (int) SHAREEDGE) { + assert((abcop == (int) SHAREVERT) && (abcpq == (int) SHAREVERT)); + // qo is coincident with an edge of abc. + return (int) SHAREEDGE; + } + + // They may share a vertex or disjoint. + if (abcop == (int) SHAREVERT) { + // o or p is coincident with a vertex of abc. + if (abcpq == (int) SHAREVERT) { + // p is the coincident vertex. + assert(abcqo != (int) SHAREVERT); + } else { + // o is the coincident vertex. + assert(abcqo == (int) SHAREVERT); + } + return (int) SHAREVERT; + } + if (abcpq == (int) SHAREVERT) { + // q is the coincident vertex. + assert(abcqo == (int) SHAREVERT); + return (int) SHAREVERT; + } + + // They are disjoint. + return (int) DISJOINT; } /////////////////////////////////////////////////////////////////////////////// @@ -7349,100 +6124,222 @@ 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; -} - -// 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]; -} - /////////////////////////////////////////////////////////////////////////////// // // -// shortdistance() Returns the shortest distance from point p to a line // -// defined by two points e1 and e2. // +// incircle3d() 3D in-circle test. // // // -// 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. // +// Return a negative value if pd is inside the circumcircle of the triangle // +// pa, pb, and pc. // // // -// 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. // +// IMPORTANT: It assumes that [a,b] is the common edge, i.e., the two input // +// triangles are [a,b,c] and [b,a,d]. // // // /////////////////////////////////////////////////////////////////////////////// -REAL tetgenmesh::shortdistance(REAL* p, REAL* e1, REAL* e2) +REAL tetgenmesh::incircle3d(point pa, point pb, point pc, point pd) { - 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); + REAL area2[2], n1[3], n2[3], c[3]; + REAL sign, r, d; - return sqrt(dot(v2, v2) - l_p * l_p); -} + // Calculate the areas of the two triangles [a, b, c] and [b, a, d]. + facenormal(pa, pb, pc, n1, 1, NULL); + area2[0] = dot(n1, n1); + facenormal(pb, pa, pd, n2, 1, NULL); + area2[1] = dot(n2, n2); -/////////////////////////////////////////////////////////////////////////////// -// // -// shortdistance() Returns the shortest distance from point p to a face. // -// // -/////////////////////////////////////////////////////////////////////////////// + if (area2[0] > area2[1]) { + // Choose [a, b, c] as the base triangle. + circumsphere(pa, pb, pc, NULL, c, &r); + d = distance(c, pd); + } else { + // Choose [b, a, d] as the base triangle. + if (area2[1] > 0) { + circumsphere(pb, pa, pd, NULL, c, &r); + d = distance(c, pc); + } else { + // The four points are collinear. This case only happens on the boundary. + return 0; // Return "not inside". + } + } -REAL tetgenmesh::shortdistance(REAL* p, REAL* e1, REAL* e2, REAL* e3) -{ - REAL prj[3]; + sign = d - r; + if (fabs(sign) / r < b->epsilon) { + sign = 0; + } - projpt2face(p, e1, e2, e3, prj); - return distance(p, prj); + return sign; } /////////////////////////////////////////////////////////////////////////////// // // -// interiorangle() Return the interior angle (0 - 2 * PI) between vectors // -// o->p1 and o->p2. // +// facenormal() Calculate the normal of the face. // // // -// '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 normal of the face abc can be calculated by the cross product of 2 of // +// its 3 edge vectors. A better choice of two edge vectors will reduce the // +// numerical error during the calculation. Burdakov proved that the optimal // +// basis problem is equivalent to the minimum spanning tree problem with the // +// edge length be the functional, see Burdakov, "A greedy algorithm for the // +// optimal basis problem", BIT 37:3 (1997), 591-599. If 'pivot' > 0, the two // +// short edges in abc are chosen for the calculation. // +// // +// If 'lav' is not NULL and if 'pivot' is set, the average edge length of // +// the edges of the face [a,b,c] is returned. // // // /////////////////////////////////////////////////////////////////////////////// -REAL tetgenmesh::interiorangle(REAL* o, REAL* p1, REAL* p2, REAL* n) +void tetgenmesh::facenormal(point pa, point pb, point pc, REAL *n, int pivot, + REAL* lav) { - REAL v1[3], v2[3], np[3]; - REAL theta, costheta, lenlen; - REAL ori, len1, len2; + REAL v1[3], v2[3], v3[3], *pv1, *pv2; + REAL L1, L2, L3; - // Get the interior angle (0 - PI) between o->p1, and o->p2. + v1[0] = pb[0] - pa[0]; // edge vector v1: a->b + v1[1] = pb[1] - pa[1]; + v1[2] = pb[2] - pa[2]; + v2[0] = pa[0] - pc[0]; // edge vector v2: c->a + v2[1] = pa[1] - pc[1]; + v2[2] = pa[2] - pc[2]; + + // Default, normal is calculated by: v1 x (-v2) (see Fig. fnormal). + if (pivot > 0) { + // Choose edge vectors by Burdakov's algorithm. + v3[0] = pc[0] - pb[0]; // edge vector v3: b->c + v3[1] = pc[1] - pb[1]; + v3[2] = pc[2] - pb[2]; + L1 = dot(v1, v1); + L2 = dot(v2, v2); + L3 = dot(v3, v3); + // Sort the three edge lengths. + if (L1 < L2) { + if (L2 < L3) { + pv1 = v1; pv2 = v2; // n = v1 x (-v2). + } else { + pv1 = v3; pv2 = v1; // n = v3 x (-v1). + } + } else { + if (L1 < L3) { + pv1 = v1; pv2 = v2; // n = v1 x (-v2). + } else { + pv1 = v2; pv2 = v3; // n = v2 x (-v3). + } + } + if (lav) { + // return the average edge length. + *lav = (sqrt(L1) + sqrt(L2) + sqrt(L3)) / 3.0; + } + } else { + pv1 = v1; pv2 = v2; // n = v1 x (-v2). + } + + // Calculate the face normal. + cross(pv1, pv2, n); + // Inverse the direction; + n[0] = -n[0]; + n[1] = -n[1]; + n[2] = -n[2]; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// shortdistance() Returns the shortest distance from point p to a line // +// defined by two points e1 and e2. // +// // +// First compute the projection length l_p of the vector v1 = p - e1 along // +// the vector v2 = e2 - e1. Then Pythagoras' Theorem is used to compute the // +// shortest distance. // +// // +// This routine allows that p is collinear with the line. In this case, the // +// return value is zero. The two points e1 and e2 should not be identical. // +// // +/////////////////////////////////////////////////////////////////////////////// + +REAL tetgenmesh::shortdistance(REAL* p, REAL* e1, REAL* e2) +{ + REAL v1[3], v2[3]; + REAL len, l_p; + + v1[0] = e2[0] - e1[0]; + 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)); + assert(len != 0.0); + + v1[0] /= len; + v1[1] /= len; + v1[2] /= len; + l_p = dot(v1, v2); + + return sqrt(dot(v2, v2) - l_p * l_p); +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// triarea() Return the area of a triangle. // +// // +/////////////////////////////////////////////////////////////////////////////// + +REAL tetgenmesh::triarea(REAL* pa, REAL* pb, REAL* pc) +{ + REAL A[4][4]; + + // 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]; // vector V1 (pa->pb) + A[1][0] = pc[0] - pa[0]; + A[1][1] = pc[1] - pa[1]; + A[1][2] = pc[2] - pa[2]; // vector V2 (pa->pc) + + cross(A[0], A[1], A[2]); // vector V3 (V1 X V2) + + return 0.5 * sqrt(dot(A[2], A[2])); // The area of [a,b,c]. +} + +REAL tetgenmesh::orient3dfast(REAL *pa, REAL *pb, REAL *pc, REAL *pd) +{ + REAL adx, bdx, cdx; + REAL ady, bdy, cdy; + REAL adz, bdz, cdz; + + adx = pa[0] - pd[0]; + bdx = pb[0] - pd[0]; + cdx = pc[0] - pd[0]; + ady = pa[1] - pd[1]; + bdy = pb[1] - pd[1]; + cdy = pc[1] - pd[1]; + adz = pa[2] - pd[2]; + bdz = pb[2] - pd[2]; + cdz = pc[2] - pd[2]; + + return adx * (bdy * cdz - bdz * cdy) + + bdx * (cdy * adz - cdz * ady) + + cdx * (ady * bdz - adz * bdy); +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// interiorangle() Return the interior angle (0 - 2 * PI) between vectors // +// o->p1 and o->p2. // +// // +// '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. // +// // +/////////////////////////////////////////////////////////////////////////////// + +REAL tetgenmesh::interiorangle(REAL* o, REAL* p1, REAL* p2, REAL* n) +{ + REAL v1[3], v2[3], np[3]; + REAL theta, costheta, lenlen; + REAL ori, len1, len2; + + // 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]; @@ -7452,9 +6349,8 @@ REAL tetgenmesh::interiorangle(REAL* o, REAL* p1, REAL* p2, REAL* n) len1 = sqrt(dot(v1, v1)); len2 = sqrt(dot(v2, v2)); lenlen = len1 * len2; -#ifdef SELF_CHECK assert(lenlen != 0.0); -#endif + costheta = dot(v1, v2) / lenlen; if (costheta > 1.0) { costheta = 1.0; // Roundoff. @@ -7496,9 +6392,7 @@ void tetgenmesh::projpt2edge(REAL* p, REAL* e1, REAL* e2, REAL* prj) v2[2] = p[2] - e1[2]; len = sqrt(dot(v1, v1)); -#ifdef SELF_CHECK assert(len != 0.0); -#endif v1[0] /= len; v1[1] /= len; v1[2] /= len; @@ -7521,10 +6415,9 @@ void tetgenmesh::projpt2face(REAL* p, REAL* f1, REAL* f2, REAL* f3, REAL* prj) REAL len, dist; // Get the unit face normal. - // facenormal(f1, f2, f3, fnormal, &len); - facenormal2(f1, f2, f3, fnormal, 1); + facenormal(f1, f2, f3, fnormal, 1, NULL); len = sqrt(fnormal[0]*fnormal[0] + fnormal[1]*fnormal[1] + - fnormal[2]*fnormal[2]); + fnormal[2]*fnormal[2]); fnormal[0] /= len; fnormal[1] /= len; fnormal[2] /= len; @@ -7541,135 +6434,6 @@ void tetgenmesh::projpt2face(REAL* p, REAL* f1, REAL* f2, REAL* f3, REAL* prj) prj[2] = p[2] - dist * fnormal[2]; } -/////////////////////////////////////////////////////////////////////////////// -// // -// 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. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::facenormal(REAL* pa, REAL* pb, REAL* pc, REAL* n, REAL* nlen) -{ - REAL v1[3], v2[3]; - - 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]; - - cross(v1, v2, n); - if (nlen != (REAL *) NULL) { - *nlen = sqrt(dot(n, n)); - } -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// facenormal() Calculate the normal of the face. // -// // -// The normal of the face abc can be calculated by the cross product of 2 of // -// its 3 edge vectors. A better choice of two edge vectors will reduce the // -// numerical error during the calculation. Burdakov proved that the optimal // -// basis problem is equivalent to the minimum spanning tree problem with the // -// edge length be the functional, see Burdakov, "A greedy algorithm for the // -// optimal basis problem", BIT 37:3 (1997), 591-599. If 'pivot' > 0, the two // -// short edges in abc are chosen for the calculation. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::facenormal2(point pa, point pb, point pc, REAL *n, int pivot) -{ - REAL v1[3], v2[3], v3[3], *pv1, *pv2; - REAL L1, L2, L3; - - v1[0] = pb[0] - pa[0]; // edge vector v1: a->b - v1[1] = pb[1] - pa[1]; - v1[2] = pb[2] - pa[2]; - v2[0] = pa[0] - pc[0]; // edge vector v2: c->a - v2[1] = pa[1] - pc[1]; - v2[2] = pa[2] - pc[2]; - - // Default, normal is calculated by: v1 x (-v2) (see Fig. fnormal). - if (pivot > 0) { - // Choose edge vectors by Burdakov's algorithm. - v3[0] = pc[0] - pb[0]; // edge vector v3: b->c - v3[1] = pc[1] - pb[1]; - v3[2] = pc[2] - pb[2]; - L1 = DOT(v1, v1); - L2 = DOT(v2, v2); - L3 = DOT(v3, v3); - // Sort the three edge lengths. - if (L1 < L2) { - if (L2 < L3) { - pv1 = v1; pv2 = v2; // n = v1 x (-v2). - } else { - pv1 = v3; pv2 = v1; // n = v3 x (-v1). - } - } else { - if (L1 < L3) { - pv1 = v1; pv2 = v2; // n = v1 x (-v2). - } else { - pv1 = v2; pv2 = v3; // n = v2 x (-v3). - } - } - } else { - pv1 = v1; pv2 = v2; // n = v1 x (-v2). - } - - // Calculate the face normal. - CROSS(pv1, pv2, n); - // Inverse the direction; - n[0] = -n[0]; - n[1] = -n[1]; - n[2] = -n[2]; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// 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. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::edgeorthonormal(REAL* e1, REAL* e2, REAL* op, REAL* n) -{ - 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; -} - /////////////////////////////////////////////////////////////////////////////// // // // facedihedral() Return the dihedral angle (in radian) between two // @@ -7688,8 +6452,10 @@ REAL tetgenmesh::facedihedral(REAL* pa, REAL* pb, REAL* pc1, REAL* pc2) REAL costheta, ori; REAL theta; - facenormal(pa, pb, pc1, n1, &n1len); - facenormal(pa, pb, pc2, n2, &n2len); + facenormal(pa, pb, pc1, n1, 1, NULL); + facenormal(pa, pb, pc2, n2, 1, NULL); + n1len = sqrt(dot(n1, n1)); + n2len = sqrt(dot(n2, n2)); costheta = dot(n1, n2) / (n1len * n2len); // Be careful rounding error! if (costheta > 1.0) { @@ -7710,81 +6476,113 @@ REAL tetgenmesh::facedihedral(REAL* pa, REAL* pb, REAL* pc1, REAL* pc2) // // // 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. // +// If 'cosdd' is not NULL, it returns the cosines of the 6 dihedral angles, // +// the edge indices are given in the global array 'edge2ver'. 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) +bool 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; + int f1 = 0, f2 = 0, 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 (vol > 0) { + // 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; + } else { + // There are degeneracies, such as duplicated vertices. + vol = 0; //assert(0); } } - // This tet has zero volume. - if (cosmaxd != NULL) { - *cosmaxd = -1.0; // 180 degree. - } - if (cosmind != NULL) { - *cosmind = 1.0; // 0 degree. - } - return; } - // 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 (vol <= 0) { // if (vol == 0.0) { + // A degenerated tet or an inverted tet. + facenormal(pc, pb, pd, N[0], 1, NULL); + facenormal(pa, pc, pd, N[1], 1, NULL); + facenormal(pb, pa, pd, N[2], 1, NULL); + facenormal(pa, pb, pc, N[3], 1, 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; + } else { + // There are degeneracies, such as duplicated vertices. + break; // Not a valid normal. + } + } + if (i < 4) { + // Do not calculate dihedral angles. + // Set all angles be 0 degree. There will be no quality optimization for + // this tet! Use volume optimization to correct it. + if (cosdd != NULL) { + for (i = 0; i < 6; i++) { + cosdd[i] = -1.0; // 180 degree. + } + } + // This tet has zero volume. + if (cosmaxd != NULL) { + *cosmaxd = -1.0; // 180 degree. + } + if (cosmind != NULL) { + *cosmind = -1.0; // 180 degree. + } + return false; } } + // Calculate the cosine of the dihedral angles of the edges. 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. + case 0: f1 = 0; f2 = 1; break; // [c,d]. + case 1: f1 = 1; f2 = 2; break; // [a,d]. + case 2: f1 = 2; f2 = 3; break; // [a,b]. + case 3: f1 = 0; f2 = 3; break; // [b,c]. + case 4: f1 = 2; f2 = 0; break; // [b,d]. + case 5: f1 = 1; f2 = 3; break; // [a,c]. } cosd = -dot(N[f1], N[f2]); + if (cosd < -1.0) cosd = -1.0; // Rounding. + if (cosd > 1.0) cosd = 1.0; // Rounding. if (cosdd) cosdd[i] = cosd; - if (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; + if (cosmaxd || cosmind) { + 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; + } } } + + return true; } /////////////////////////////////////////////////////////////////////////////// // // -// tetallnormal() Get the in-noramls of the four faces of a given tet. // +// tetallnormal() Get the in-normals 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. // +// N[1] acd, N[2] bad, N[3] abc (exactly corresponding to the face indices // +// of the mesh data structure). These normals are unnormalized. // // // /////////////////////////////////////////////////////////////////////////////// void tetgenmesh::tetallnormal(point pa, point pb, point pc, point pd, - REAL N[4][3], REAL* volume) + REAL N[4][3], REAL* volume) { REAL A[4][4], rhs[4], D; int indx[4]; @@ -7794,20 +6592,27 @@ void tetgenmesh::tetallnormal(point pa, point pb, point pc, point pd, 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]; + if (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]; + } else { + // The tet is degenerated. + if (volume != NULL) { + *volume = 0; + } } - // 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]; } /////////////////////////////////////////////////////////////////////////////// @@ -7887,12 +6692,12 @@ REAL tetgenmesh::tetaspectratio(point pa, point pb, point pc, point pd) // // // 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.// +// are not NULLs. Otherwise, return FALSE, the four points are co-planar. // // // /////////////////////////////////////////////////////////////////////////////// -bool tetgenmesh:: -circumsphere(REAL* pa, REAL* pb, REAL* pc, REAL* pd, REAL* cent, REAL* radius) +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]; @@ -7941,165 +6746,56 @@ circumsphere(REAL* pa, REAL* pb, REAL* pc, REAL* pd, REAL* cent, REAL* radius) /////////////////////////////////////////////////////////////////////////////// // // -// inscribedsphere() Compute the radius and center of the biggest // -// inscribed sphere of a given tetrahedron. // -// // -// 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. // +// orthosphere() Calulcate the orthosphere of four weighted points. // // // -// 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. // +// A weighted point (p, P^2) can be interpreted as a sphere centered at the // +// point 'p' with a radius 'P'. The 'height' of 'p' is pheight = p[0]^2 + // +// p[1]^2 + p[2]^2 - P^2. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::inscribedsphere(REAL* pa, REAL* pb, REAL* pc, REAL* pd, - REAL* cent, REAL* radius) +bool tetgenmesh::orthosphere(REAL* pa, REAL* pb, REAL* pc, REAL* pd, + REAL aheight, REAL bheight, REAL cheight, + REAL dheight, REAL* orthocent, REAL* radius) { - REAL N[4][3], H[4]; // Normals (colume vectors) and heights of each face. - REAL rd; - int i; + REAL A[4][4], rhs[4], D; + int indx[4]; - // 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]); - } -} + // Set the coefficient matrix A (4 x 4). + A[0][0] = 1.0; A[0][1] = pa[0]; A[0][2] = pa[1]; A[0][3] = pa[2]; + A[1][0] = 1.0; A[1][1] = pb[0]; A[1][2] = pb[1]; A[1][3] = pb[2]; + A[2][0] = 1.0; A[2][1] = pc[0]; A[2][2] = pc[1]; A[2][3] = pc[2]; + A[3][0] = 1.0; A[3][1] = pd[0]; A[3][2] = pd[1]; A[3][3] = pd[2]; -/////////////////////////////////////////////////////////////////////////////// -// // -// 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. // -// // -// 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 // -// // -/////////////////////////////////////////////////////////////////////////////// + // Set the right hand side vector (4 x 1). + rhs[0] = 0.5 * aheight; + rhs[1] = 0.5 * bheight; + rhs[2] = 0.5 * cheight; + rhs[3] = 0.5 * dheight; -void tetgenmesh::rotatepoint(REAL* p, REAL rotangle, REAL* p1, REAL* p2) -{ - REAL T[4][4], pp0[4], p0t[4], p2t[4]; - REAL roty, rotx, alphaR, projlen; - REAL dx, dy, dz; - - 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; - } + // Solve the 4 by 4 equations use LU decomposition with partial pivoting + // and backward and forward substitute.. + if (!lu_decmp(A, 4, indx, &D, 0)) { + if (radius != (REAL *) NULL) *radius = 0.0; + return false; } - - 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 - - p[0] = p0t[0]; - p[1] = p0t[1]; - p[2] = p0t[2]; + lu_solve(A, 4, indx, rhs, 0); + + if (orthocent != (REAL *) NULL) { + orthocent[0] = rhs[1]; + orthocent[1] = rhs[2]; + orthocent[2] = rhs[3]; + } + if (radius != (REAL *) NULL) { + // rhs[0] = - rheight / 2; + // rheight = - 2 * rhs[0]; + // = r[0]^2 + r[1]^2 + r[2]^2 - radius^2 + // radius^2 = r[0]^2 + r[1]^2 + r[2]^2 -rheight + // = r[0]^2 + r[1]^2 + r[2]^2 + 2 * rhs[0] + *radius = sqrt(rhs[1] * rhs[1] + rhs[2] * rhs[2] + rhs[3] * rhs[3] + + 2.0 * rhs[0]); + } + return true; } /////////////////////////////////////////////////////////////////////////////// @@ -8126,12 +6822,12 @@ void tetgenmesh::rotatepoint(REAL* p, REAL rotangle, REAL* p1, REAL* p2) /////////////////////////////////////////////////////////////////////////////// void tetgenmesh::planelineint(REAL* pa, REAL* pb, REAL* pc, REAL* e1, REAL* e2, - REAL* ip, REAL* u) + REAL* ip, REAL* u) { REAL n[3], det, det1; // Calculate N. - facenormal2(pa, pb, pc, n, 1); + facenormal(pa, pb, pc, n, 1, 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]); @@ -8150,9202 +6846,7299 @@ void tetgenmesh::planelineint(REAL* pa, REAL* pb, REAL* pc, REAL* e1, REAL* e2, /////////////////////////////////////////////////////////////////////////////// // // -// randomnation() Generate a random number between 0 and 'choices' - 1. // +// linelineint() Calculate the intersection(s) of two line segments. // +// // +// Calculate the line segment [P, Q] that is the shortest route between two // +// lines from A to B and C to D. Calculate also the values of tp and tq // +// where: P = A + tp (B - A), and Q = C + tq (D - C). // +// // +// Return 1 if the line segment exists. Otherwise, return 0. // // // /////////////////////////////////////////////////////////////////////////////// -unsigned long tetgenmesh::randomnation(unsigned int choices) +int tetgenmesh::linelineint(REAL* A, REAL* B, REAL* C, REAL* D, REAL* P, + REAL* Q, REAL* tp, REAL* tq) { - unsigned long newrandom; + REAL vab[3], vcd[3], vca[3]; + REAL vab_vab, vcd_vcd, vab_vcd; + REAL vca_vab, vca_vcd; + REAL det, eps; + int i; - 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; + for (i = 0; i < 3; i++) { + vab[i] = B[i] - A[i]; + vcd[i] = D[i] - C[i]; + vca[i] = A[i] - C[i]; } -} -/////////////////////////////////////////////////////////////////////////////// -// // -// distance2() Returns the square "distance" of a tetrahedron to point p. // -// // -/////////////////////////////////////////////////////////////////////////////// + vab_vab = dot(vab, vab); + vcd_vcd = dot(vcd, vcd); + vab_vcd = dot(vab, vcd); -REAL tetgenmesh::distance2(tetrahedron* tetptr, point p) -{ - point p1, p2, p3, p4; - REAL dx, dy, dz; + det = vab_vab * vcd_vcd - vab_vcd * vab_vcd; + // Round the result. + eps = det / (fabs(vab_vab * vcd_vcd) + fabs(vab_vcd * vab_vcd)); + if (eps < b->epsilon) { + return 0; + } - p1 = (point) tetptr[4]; - p2 = (point) tetptr[5]; - p3 = (point) tetptr[6]; - p4 = (point) tetptr[7]; + vca_vab = dot(vca, vab); + vca_vcd = dot(vca, vcd); - 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]); + *tp = (vcd_vcd * (- vca_vab) + vab_vcd * vca_vcd) / det; + *tq = (vab_vcd * (- vca_vab) + vab_vab * vca_vcd) / det; - return dx * dx + dy * dy + dz * dz; + for (i = 0; i < 3; i++) P[i] = A[i] + (*tp) * vab[i]; + for (i = 0; i < 3; i++) Q[i] = C[i] + (*tq) * vcd[i]; + + return 1; } /////////////////////////////////////////////////////////////////////////////// // // -// preciselocate() Find a simplex containing a given point. // +// tetprismvol() Calculate the volume of a tetrahedral prism in 4D. // // // -// 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. // +// A tetrahedral prism is a convex uniform polychoron (four dimensional poly-// +// tope). It has 6 polyhedral cells: 2 tetrahedra connected by 4 triangular // +// prisms. It has 14 faces: 8 triangular and 6 square. It has 16 edges and 8 // +// vertices. (Wikipedia). // // // -// 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. // +// Let 'p0', ..., 'p3' be four affinely independent points in R^3. They form // +// the lower tetrahedral facet of the prism. The top tetrahedral facet is // +// formed by four vertices, 'p4', ..., 'p7' in R^4, which is obtained by // +// lifting each vertex of the lower facet into R^4 by a weight (height). A // +// canonical choice of the weights is the square of Euclidean norm of of the // +// points (vectors). // // // -// 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. // +// The return value is (4!) 24 times of the volume of the tetrahedral prism. // // // /////////////////////////////////////////////////////////////////////////////// -enum tetgenmesh::locateresult tetgenmesh::preciselocate(point searchpt, - triface* searchtet, long maxtetnumber) +REAL tetgenmesh::tetprismvol(REAL* p0, REAL* p1, REAL* p2, REAL* p3) { - 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(searchtet->tet != dummytet); -#endif - - 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 - - backtracetet = *searchtet; // Initialize backtracetet. - - // 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++; - } + REAL *p4, *p5, *p6, *p7; + REAL w4, w5, w6, w7; + REAL vol[4]; - if (!b->quiet && b->verbose) { - printf("Warning: Point location stopped after searching %ld tets.\n", - maxtetnumber); - } - // terminatetetgen(2); - return OUTSIDE; + p4 = p0; + p5 = p1; + p6 = p2; + p7 = p3; + + // TO DO: these weights can be pre-calculated! + w4 = dot(p0, p0); + w5 = dot(p1, p1); + w6 = dot(p2, p2); + w7 = dot(p3, p3); + + // Calculate the volume of the tet-prism. + vol[0] = orient4d(p5, p6, p4, p3, p7, w5, w6, w4, 0, w7); + vol[1] = orient4d(p3, p6, p2, p0, p1, 0, w6, 0, 0, 0); + vol[2] = orient4d(p4, p6, p3, p0, p1, w4, w6, 0, 0, 0); + vol[3] = orient4d(p6, p5, p4, p3, p1, w6, w5, w4, 0, 0); + + return fabs(vol[0]) + fabs(vol[1]) + fabs(vol[2]) + fabs(vol[3]); } /////////////////////////////////////////////////////////////////////////////// // // -// randomsample() Randomly sample the tetrahedra for point loation. // -// // -// This routine implements Muecke's Jump-and-walk point location algorithm. // -// It improves the simple walk-through by "jumping" to a good starting point // -// via random sampling. Searching begins from one of handles: the input // -// 'searchtet', a recently encountered tetrahedron 'recenttet', or from one // -// chosen from a random sample. The choice is made by determining which one // -// 's origin is closest to the point we are searcing for. Having chosen the // -// starting tetrahedron, the simple Walk-through algorithm is executed. // +// calculateabovepoint() Calculate a point above a facet in 'dummypoint'. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::randomsample(point searchpt, triface *searchtet) +bool tetgenmesh::calculateabovepoint(arraypool *facpoints, point *ppa, + point *ppb, point *ppc) { - tetrahedron *firsttet, *tetptr; - void **sampleblock; - long sampleblocks, samplesperblock, samplenum; - long tetblocks, i, j; - uintptr_t alignptr; - REAL searchdist, dist; + point *ppt, pa, pb, pc; + REAL v1[3], v2[3], n[3]; + REAL lab, len, A, area; + REAL x, y, z; + int i; - // '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); - } + ppt = (point *) fastlookup(facpoints, 0); + pa = *ppt; // a is the first point. + pb = pc = NULL; // Avoid compiler warnings. - // 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 a point b s.t. the length of [a, b] is maximal. + lab = 0; + for (i = 1; i < facpoints->objects; i++) { + ppt = (point *) fastlookup(facpoints, i); + x = (*ppt)[0] - pa[0]; + y = (*ppt)[1] - pa[1]; + z = (*ppt)[2] - pa[2]; + len = x * x + y * y + z * z; + if (len > lab) { + lab = len; + pb = *ppt; + } } - - // 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; + lab = sqrt(lab); + if (lab == 0) { + if (!b->quiet) { + printf("Warning: All points of a facet are coincident with %d.\n", + pointmark(pa)); } + return false; } - // 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; - } - } + // Get a point c s.t. the area of [a, b, c] is maximal. + v1[0] = pb[0] - pa[0]; + v1[1] = pb[1] - pa[1]; + v1[2] = pb[2] - pa[2]; + A = 0; + for (i = 1; i < facpoints->objects; i++) { + ppt = (point *) fastlookup(facpoints, i); + v2[0] = (*ppt)[0] - pa[0]; + v2[1] = (*ppt)[1] - pa[1]; + v2[2] = (*ppt)[2] - pa[2]; + cross(v1, v2, n); + area = dot(n, n); + if (area > A) { + A = area; + pc = *ppt; + } + } + if (A == 0) { + // All points are collinear. No above point. + if (!b->quiet) { + printf("Warning: All points of a facet are collinaer with [%d, %d].\n", + pointmark(pa), pointmark(pb)); } - sampleblock = (void **) *sampleblock; + return false; + } + + // Calculate an above point of this facet. + facenormal(pa, pb, pc, n, 1, NULL); + len = sqrt(dot(n, n)); + n[0] /= len; + n[1] /= len; + n[2] /= len; + lab /= 2.0; // Half the maximal length. + dummypoint[0] = pa[0] + lab * n[0]; + dummypoint[1] = pa[1] + lab * n[1]; + dummypoint[2] = pa[2] + lab * n[2]; + + if (ppa != NULL) { + // Return the three points. + *ppa = pa; + *ppb = pb; + *ppc = pc; } + + return true; } /////////////////////////////////////////////////////////////////////////////// // // -// locate() Find a simplex containing a given point. // +// Calculate an above point. It lies above the plane containing the subface // +// [a,b,c], and save it in dummypoint. Moreover, the vector pa->dummypoint // +// is the normal of the plane. // // // /////////////////////////////////////////////////////////////////////////////// -enum tetgenmesh::locateresult tetgenmesh::locate(point searchpt, - triface *searchtet) +void tetgenmesh::calculateabovepoint4(point pa, point pb, point pc, point pd) { - // 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 n1[3], n2[3], *norm; + REAL len, len1, len2; + + // Select a base. + facenormal(pa, pb, pc, n1, 1, NULL); + len1 = sqrt(dot(n1, n1)); + facenormal(pa, pb, pd, n2, 1, NULL); + len2 = sqrt(dot(n2, n2)); + if (len1 > len2) { + norm = n1; + len = len1; + } else { + norm = n2; + len = len2; + } + assert(len > 0); + norm[0] /= len; + norm[1] /= len; + norm[2] /= len; + len = distance(pa, pb); + dummypoint[0] = pa[0] + len * norm[0]; + dummypoint[1] = pa[1] + len * norm[1]; + dummypoint[2] = pa[2] + len * norm[2]; } +//// //// +//// //// +//// geom_cxx ///////////////////////////////////////////////////////////////// + +//// flip_cxx ///////////////////////////////////////////////////////////////// +//// //// +//// //// + /////////////////////////////////////////////////////////////////////////////// // // -// locate2() Find a simplex containing a given point. // +// flip23() Perform a 2-to-3 flip (face-to-edge flip). // // // -// Another implementation of the Walk-through point location algorithm. // -// See the comments of preciselocate(). // +// 'fliptets' is an array of three tets (handles), where the [0] and [1] are // +// [a,b,c,d] and [b,a,c,e]. The three new tets: [e,d,a,b], [e,d,b,c], and // +// [e,d,c,a] are returned in [0], [1], and [2] of 'fliptets'. As a result, // +// The face [a,b,c] is removed, and the edge [d,e] is created. // +// // +// If 'hullflag' > 0, hull tets may be involved in this flip, i.e., one of // +// the five vertices may be 'dummypoint'. There are two canonical cases: // +// (1) d is 'dummypoint', then all three new tets are hull tets. If e is // +// 'dummypoint', we reconfigure e to d, i.e., turn it up-side down. // +// (2) c is 'dummypoint', then two new tets: [e,d,b,c] and [e,d,c,a], are // +// hull tets. If a or b is 'dummypoint', we reconfigure it to c, i.e., // +// rotate the three input tets counterclockwisely (right-hand rule) // +// until a or b is in c's position. // +// // +// If 'fc->enqflag' is set, convex hull faces will be queued for flipping. // +// In particular, if 'fc->enqflag' is 1, it is called by incrementalflip() // +// after the insertion of a new point. It is assumed that 'd' is the new // +// point. IN this case, only link faces of 'd' are queued. // // // /////////////////////////////////////////////////////////////////////////////// -enum tetgenmesh::locateresult tetgenmesh::locate2(point searchpt, - triface* searchtet, arraypool *histtetarray) +void tetgenmesh::flip23(triface* fliptets, int hullflag, flipconstraints *fc) { - 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; + triface topcastets[3], botcastets[3]; + triface newface, casface; + point pa, pb, pc, pd, pe; + REAL attrib, volume; + int dummyflag = 0; // range = {-1, 0, 1, 2}. int i; - if (searchtet->tet == dummytet) { - // A hull tet. Choose the neighbor of its base face. - searchtet->loc = 0; - symself(*searchtet); + if (hullflag > 0) { + // Check if e is dummypoint. + if (oppo(fliptets[1]) == dummypoint) { + // Swap the two old tets. + newface = fliptets[0]; + fliptets[0] = fliptets[1]; + fliptets[1] = newface; + dummyflag = -1; // d is dummypoint. + } else { + // Check if either a or b is dummypoint. + if (org(fliptets[0]) == dummypoint) { + dummyflag = 1; // a is dummypoint. + enextself(fliptets[0]); + eprevself(fliptets[1]); + } else if (dest(fliptets[0]) == dummypoint) { + dummyflag = 2; // b is dummypoint. + eprevself(fliptets[0]); + enextself(fliptets[1]); + } else { + dummyflag = 0; // either c or d may be dummypoint. + } + } } - // Stay in the 0th edge ring. - searchtet->ver = 0; + pa = org(fliptets[0]); + pb = dest(fliptets[0]); + pc = apex(fliptets[0]); + pd = oppo(fliptets[0]); + pe = oppo(fliptets[1]); - // 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; + flip23count++; + + // Get the outer boundary faces. + for (i = 0; i < 3; i++) { + fnext(fliptets[0], topcastets[i]); + enextself(fliptets[0]); } - 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; + for (i = 0; i < 3; i++) { + fnext(fliptets[1], botcastets[i]); + eprevself(fliptets[1]); } - if (histtetarray != NULL) { - // Remember all the tets we've visited. - assert(histtetarray->objects == 0l); - infect(*searchtet); - histtetarray->newindex((void **) &parytet); - *parytet = *searchtet; + // Re-use fliptets[0] and fliptets[1]. + fliptets[0].ver = 11; + fliptets[1].ver = 11; + setelemmarker(fliptets[0].tet, 0); // Clear all flags. + setelemmarker(fliptets[1].tet, 0); + // NOTE: the element attributes and volume constraint remain unchanged. + if (checksubsegflag) { + // Dealloc the space to subsegments. + if (fliptets[0].tet[8] != NULL) { + tet2segpool->dealloc((shellface *) fliptets[0].tet[8]); + fliptets[0].tet[8] = NULL; + } + if (fliptets[1].tet[8] != NULL) { + tet2segpool->dealloc((shellface *) fliptets[1].tet[8]); + fliptets[1].tet[8] = NULL; + } + } + if (checksubfaceflag) { + // Dealloc the space to subfaces. + if (fliptets[0].tet[9] != NULL) { + tet2subpool->dealloc((shellface *) fliptets[0].tet[9]); + fliptets[0].tet[9] = NULL; + } + if (fliptets[1].tet[9] != NULL) { + tet2subpool->dealloc((shellface *) fliptets[1].tet[9]); + fliptets[1].tet[9] = NULL; + } + } + // Create a new tet. + maketetrahedron(&(fliptets[2])); + // The new tet have the same attributes from the old tet. + for (i = 0; i < numelemattrib; i++) { + attrib = elemattribute(fliptets[0].tet, i); + setelemattribute(fliptets[2].tet, i, attrib); + } + if (b->varvolume) { + volume = volumebound(fliptets[0].tet); + setvolumebound(fliptets[2].tet, volume); + } + + if (hullflag > 0) { + // Check if d is dummytet. + if (pd != dummypoint) { + setvertices(fliptets[0], pe, pd, pa, pb); // [e,d,a,b] * + setvertices(fliptets[1], pe, pd, pb, pc); // [e,d,b,c] * + // Check if c is dummypoint. + if (pc != dummypoint) { + setvertices(fliptets[2], pe, pd, pc, pa); // [e,d,c,a] * + } else { + setvertices(fliptets[2], pd, pe, pa, pc); // [d,e,a,c] + esymself(fliptets[2]); // [e,d,c,a] * + } + // The hullsize does not change. + } else { + // d is dummypoint. + setvertices(fliptets[0], pa, pb, pe, pd); // [a,b,e,d] + setvertices(fliptets[1], pb, pc, pe, pd); // [b,c,e,d] + setvertices(fliptets[2], pc, pa, pe, pd); // [c,a,e,d] + // Adjust the faces to [e,d,a,b], [e,d,b,c], [e,d,c,a] * + for (i = 0; i < 3; i++) { + eprevesymself(fliptets[i]); + enextself(fliptets[i]); + } + // We deleted one hull tet, and created three hull tets. + hullsize += 2; + } + } else { + setvertices(fliptets[0], pe, pd, pa, pb); // [e,d,a,b] * + setvertices(fliptets[1], pe, pd, pb, pc); // [e,d,b,c] * + setvertices(fliptets[2], pe, pd, pc, pa); // [e,d,c,a] * + } + + if (fc->remove_ndelaunay_edge) { // calc_tetprism_vol + REAL volneg[2], volpos[3], vol_diff; + if (pd != dummypoint) { + if (pc != dummypoint) { + volpos[0] = tetprismvol(pe, pd, pa, pb); + volpos[1] = tetprismvol(pe, pd, pb, pc); + volpos[2] = tetprismvol(pe, pd, pc, pa); + volneg[0] = tetprismvol(pa, pb, pc, pd); + volneg[1] = tetprismvol(pb, pa, pc, pe); + } else { // pc == dummypoint + volpos[0] = tetprismvol(pe, pd, pa, pb); + volpos[1] = 0.; + volpos[2] = 0.; + volneg[0] = 0.; + volneg[1] = 0.; + } + } else { // pd == dummypoint. + volpos[0] = 0.; + volpos[1] = 0.; + volpos[2] = 0.; + volneg[0] = 0.; + volneg[1] = tetprismvol(pb, pa, pc, pe); + } + vol_diff = volpos[0] + volpos[1] + volpos[2] - volneg[0] - volneg[1]; + fc->tetprism_vol_sum += vol_diff; // Update the total sum. + } + + // Bond three new tets together. + for (i = 0; i < 3; i++) { + esym(fliptets[i], newface); + bond(newface, fliptets[(i + 1) % 3]); + } + // Bond to top outer boundary faces (at [a,b,c,d]). + for (i = 0; i < 3; i++) { + eorgoppo(fliptets[i], newface); // At edges [b,a], [c,b], [a,c]. + bond(newface, topcastets[i]); + } + // Bond bottom outer boundary faces (at [b,a,c,e]). + for (i = 0; i < 3; i++) { + edestoppo(fliptets[i], newface); // At edges [a,b], [b,c], [c,a]. + bond(newface, botcastets[i]); } - loc = OUTSIDE; // Set a default return value. + if (checksubsegflag) { + // Bond subsegments if there are. + // Each new tet has 5 edges to be checked (except the edge [e,d]). + face checkseg; + // The middle three: [a,b], [b,c], [c,a]. + for (i = 0; i < 3; i++) { + if (issubseg(topcastets[i])) { + tsspivot1(topcastets[i], checkseg); + eorgoppo(fliptets[i], newface); + tssbond1(newface, checkseg); + sstbond1(checkseg, newface); + if (fc->chkencflag & 1) { + enqueuesubface(badsubsegs, &checkseg); + } + } + } + // The top three: [d,a], [d,b], [d,c]. Two tets per edge. + for (i = 0; i < 3; i++) { + eprev(topcastets[i], casface); + if (issubseg(casface)) { + tsspivot1(casface, checkseg); + enext(fliptets[i], newface); + tssbond1(newface, checkseg); + sstbond1(checkseg, newface); + esym(fliptets[(i + 2) % 3], newface); + eprevself(newface); + tssbond1(newface, checkseg); + sstbond1(checkseg, newface); + if (fc->chkencflag & 1) { + enqueuesubface(badsubsegs, &checkseg); + } + } + } + // The bot three: [a,e], [b,e], [c,e]. Two tets per edge. + for (i = 0; i < 3; i++) { + enext(botcastets[i], casface); + if (issubseg(casface)) { + tsspivot1(casface, checkseg); + eprev(fliptets[i], newface); + tssbond1(newface, checkseg); + sstbond1(checkseg, newface); + esym(fliptets[(i + 2) % 3], newface); + enextself(newface); + tssbond1(newface, checkseg); + sstbond1(checkseg, newface); + if (fc->chkencflag & 1) { + enqueuesubface(badsubsegs, &checkseg); + } + } + } + } // if (checksubsegflag) - // Walk through tetrahedra to locate the point. - while (true) { + if (checksubfaceflag) { + // Bond 6 subfaces if there are. + face checksh; + for (i = 0; i < 3; i++) { + if (issubface(topcastets[i])) { + tspivot(topcastets[i], checksh); + eorgoppo(fliptets[i], newface); + sesymself(checksh); + tsbond(newface, checksh); + if (fc->chkencflag & 2) { + enqueuesubface(badsubfacs, &checksh); + } + } + } + for (i = 0; i < 3; i++) { + if (issubface(botcastets[i])) { + tspivot(botcastets[i], checksh); + edestoppo(fliptets[i], newface); + sesymself(checksh); + tsbond(newface, checksh); + if (fc->chkencflag & 2) { + enqueuesubface(badsubfacs, &checksh); + } + } + } + } // if (checksubfaceflag) - ptloc_count++; // Algorithimic count. + if (fc->chkencflag & 4) { + // Put three new tets into check list. + for (i = 0; i < 3; i++) { + enqueuetetrahedron(&(fliptets[i])); + } + } - 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; + // Update the point-to-tet map. + setpoint2tet(pa, (tetrahedron) fliptets[0].tet); + setpoint2tet(pb, (tetrahedron) fliptets[0].tet); + setpoint2tet(pc, (tetrahedron) fliptets[1].tet); + setpoint2tet(pd, (tetrahedron) fliptets[0].tet); + setpoint2tet(pe, (tetrahedron) fliptets[0].tet); + + if (hullflag > 0) { + if (dummyflag != 0) { + // Restore the original position of the points (for flipnm()). + if (dummyflag == -1) { + // Reverse the edge. + for (i = 0; i < 3; i++) { + esymself(fliptets[i]); + } + // Swap the last two new tets. + newface = fliptets[1]; + fliptets[1] = fliptets[2]; + fliptets[2] = newface; + } else { + // either a or b were swapped. + if (dummyflag == 1) { + // a is dummypoint. + newface = fliptets[0]; + fliptets[0] = fliptets[2]; + fliptets[2] = fliptets[1]; + fliptets[1] = newface; + } else { // dummyflag == 2 + // b is dummypoint. + newface = fliptets[0]; + fliptets[0] = fliptets[1]; + fliptets[1] = fliptets[2]; + fliptets[2] = newface; + } + } } + } - // 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; + if (fc->enqflag > 0) { + // Queue faces which may be locally non-Delaunay. + for (i = 0; i < 3; i++) { + eprevesym(fliptets[i], newface); + flippush(flipstack, &newface); + } + if (fc->enqflag > 1) { + for (i = 0; i < 3; i++) { + enextesym(fliptets[i], newface); + flippush(flipstack, &newface); + } + } + } - // 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; - } + recenttet = fliptets[0]; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// flip32() Perform a 3-to-2 flip (edge-to-face flip). // +// // +// 'fliptets' is an array of three tets (handles), which are [e,d,a,b], // +// [e,d,b,c], and [e,d,c,a]. The two new tets: [a,b,c,d] and [b,a,c,e] are // +// returned in [0] and [1] of 'fliptets'. As a result, the edge [e,d] is // +// replaced by the face [a,b,c]. // +// // +// If 'hullflag' > 0, hull tets may be involved in this flip, i.e., one of // +// the five vertices may be 'dummypoint'. There are two canonical cases: // +// (1) d is 'dummypoint', then [a,b,c,d] is hull tet. If e is 'dummypoint',// +// we reconfigure e to d, i.e., turnover it. // +// (2) c is 'dummypoint' then both [a,b,c,d] and [b,a,c,e] are hull tets. // +// If a or b is 'dummypoint', we reconfigure it to c, i.e., rotate the // +// three old tets counterclockwisely (right-hand rule) until a or b // +// is in c's position. // +// // +// If 'fc->enqflag' is set, convex hull faces will be queued for flipping. // +// In particular, if 'fc->enqflag' is 1, it is called by incrementalflip() // +// after the insertion of a new point. It is assumed that 'a' is the new // +// point. In this case, only link faces of 'a' are queued. // +// // +// If 'checksubfaceflag' is on (global variable), and assume [e,d] is not a // +// segment. There may be two (interior) subfaces sharing at [e,d], which are // +// [e,d,p] and [e,d,q], where the pair (p,q) may be either (a,b), or (b,c), // +// or (c,a) In such case, a 2-to-2 flip is performed on these two subfaces // +// and two new subfaces [p,q,e] and [p,q,d] are created. They are inserted // +// back into the tetrahedralization. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::flip32(triface* fliptets, int hullflag, flipconstraints *fc) +{ + triface topcastets[3], botcastets[3]; + triface newface, casface; + face flipshs[3]; + face checkseg; + point pa, pb, pc, pd, pe; + REAL attrib, volume; + int dummyflag = 0; // Rangle = {-1, 0, 1, 2} + int spivot = -1, scount = 0; // for flip22() + int t1ver; + int i, j; + + if (hullflag > 0) { + // Check if e is 'dummypoint'. + if (org(fliptets[0]) == dummypoint) { + // Reverse the edge. + for (i = 0; i < 3; i++) { + esymself(fliptets[i]); } + // Swap the last two tets. + newface = fliptets[1]; + fliptets[1] = fliptets[2]; + fliptets[2] = newface; + dummyflag = -1; // e is dummypoint. } 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; - } + // Check if a or b is the 'dummypoint'. + if (apex(fliptets[0]) == dummypoint) { + dummyflag = 1; // a is dummypoint. + newface = fliptets[0]; + fliptets[0] = fliptets[1]; + fliptets[1] = fliptets[2]; + fliptets[2] = newface; + } else if (apex(fliptets[1]) == dummypoint) { + dummyflag = 2; // b is dummypoint. + newface = fliptets[0]; + fliptets[0] = fliptets[2]; + fliptets[2] = fliptets[1]; + fliptets[1] = newface; } 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; - } + dummyflag = 0; // either c or d may be dummypoint. } } - - // Move to the selected face. - if (nextmove == ORGMOVE) { - enextfnextself(*searchtet); - } else if (nextmove == DESTMOVE) { - enext2fnextself(*searchtet); - } else { - fnextself(*searchtet); + } + + pa = apex(fliptets[0]); + pb = apex(fliptets[1]); + pc = apex(fliptets[2]); + pd = dest(fliptets[0]); + pe = org(fliptets[0]); + + flip32count++; + + // Get the outer boundary faces. + for (i = 0; i < 3; i++) { + eorgoppo(fliptets[i], casface); + fsym(casface, topcastets[i]); + } + for (i = 0; i < 3; i++) { + edestoppo(fliptets[i], casface); + fsym(casface, botcastets[i]); + } + + if (checksubfaceflag) { + // Check if there are interior subfaces at the edge [e,d]. + for (i = 0; i < 3; i++) { + tspivot(fliptets[i], flipshs[i]); + if (flipshs[i].sh != NULL) { + // Found an interior subface. + stdissolve(flipshs[i]); // Disconnect the sub-tet bond. + scount++; + } else { + spivot = i; + } } - // Move to the adjacent tetrahedron (maybe a hull tetrahedron). - backtracetet = *searchtet; - symself(*searchtet); - if (searchtet->tet == dummytet) { - *searchtet = backtracetet; - loc = OUTSIDE; // return OUTSIDE; - break; + } + + // Re-use fliptets[0] and fliptets[1]. + fliptets[0].ver = 11; + fliptets[1].ver = 11; + setelemmarker(fliptets[0].tet, 0); // Clear all flags. + setelemmarker(fliptets[1].tet, 0); + if (checksubsegflag) { + // Dealloc the space to subsegments. + if (fliptets[0].tet[8] != NULL) { + tet2segpool->dealloc((shellface *) fliptets[0].tet[8]); + fliptets[0].tet[8] = NULL; + } + if (fliptets[1].tet[8] != NULL) { + tet2segpool->dealloc((shellface *) fliptets[1].tet[8]); + fliptets[1].tet[8] = NULL; } + } + if (checksubfaceflag) { + // Dealloc the space to subfaces. + if (fliptets[0].tet[9] != NULL) { + tet2subpool->dealloc((shellface *) fliptets[0].tet[9]); + fliptets[0].tet[9] = NULL; + } + if (fliptets[1].tet[9] != NULL) { + tet2subpool->dealloc((shellface *) fliptets[1].tet[9]); + fliptets[1].tet[9] = NULL; + } + } + if (checksubfaceflag) { + if (scount > 0) { + // The element attributes and volume constraint must be set correctly. + // There are two subfaces involved in this flip. The three tets are + // separated into two different regions, one may be exterior. The + // first region has two tets, and the second region has only one. + // The two created tets must be in the same region as the first region. + // The element attributes and volume constraint must be set correctly. + //assert(spivot != -1); + // The tet fliptets[spivot] is in the first region. + for (j = 0; j < 2; j++) { + for (i = 0; i < numelemattrib; i++) { + attrib = elemattribute(fliptets[spivot].tet, i); + setelemattribute(fliptets[j].tet, i, attrib); + } + if (b->varvolume) { + volume = volumebound(fliptets[spivot].tet); + setvolumebound(fliptets[j].tet, volume); + } + } + } + } + // Delete an old tet. + tetrahedrondealloc(fliptets[2].tet); - 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; + if (hullflag > 0) { + // Check if c is dummypointc. + if (pc != dummypoint) { + // Check if d is dummypoint. + if (pd != dummypoint) { + // No hull tet is involved. } else { - // Remember this tet. - infect(*searchtet); - histtetarray->newindex((void **) &parytet); - *parytet = *searchtet; + // We deleted three hull tets, and created one hull tet. + hullsize -= 2; } + setvertices(fliptets[0], pa, pb, pc, pd); + setvertices(fliptets[1], pb, pa, pc, pe); + } else { + // c is dummypoint. The two new tets are hull tets. + setvertices(fliptets[0], pb, pa, pd, pc); + setvertices(fliptets[1], pa, pb, pe, pc); + // Adjust badc -> abcd. + esymself(fliptets[0]); + // Adjust abec -> bace. + esymself(fliptets[1]); + // The hullsize does not change. } + } else { + setvertices(fliptets[0], pa, pb, pc, pd); + setvertices(fliptets[1], pb, pa, pc, pe); + } + + if (fc->remove_ndelaunay_edge) { // calc_tetprism_vol + REAL volneg[3], volpos[2], vol_diff; + if (pc != dummypoint) { + if (pd != dummypoint) { + volneg[0] = tetprismvol(pe, pd, pa, pb); + volneg[1] = tetprismvol(pe, pd, pb, pc); + volneg[2] = tetprismvol(pe, pd, pc, pa); + volpos[0] = tetprismvol(pa, pb, pc, pd); + volpos[1] = tetprismvol(pb, pa, pc, pe); + } else { // pd == dummypoint + volneg[0] = 0.; + volneg[1] = 0.; + volneg[2] = 0.; + volpos[0] = 0.; + volpos[1] = tetprismvol(pb, pa, pc, pe); + } + } else { // pc == dummypoint. + volneg[0] = tetprismvol(pe, pd, pa, pb); + volneg[1] = 0.; + volneg[2] = 0.; + volpos[0] = 0.; + volpos[1] = 0.; + } + vol_diff = volpos[0] + volpos[1] - volneg[0] - volneg[1] - volneg[2]; + fc->tetprism_vol_sum += vol_diff; // Update the total sum. + } + + // Bond abcd <==> bace. + bond(fliptets[0], fliptets[1]); + // Bond new faces to top outer boundary faces (at abcd). + for (i = 0; i < 3; i++) { + esym(fliptets[0], newface); + bond(newface, topcastets[i]); + enextself(fliptets[0]); + } + // Bond new faces to bottom outer boundary faces (at bace). + for (i = 0; i < 3; i++) { + esym(fliptets[1], newface); + bond(newface, botcastets[i]); + eprevself(fliptets[1]); + } + + if (checksubsegflag) { + // Bond 9 segments to new (flipped) tets. + for (i = 0; i < 3; i++) { // edges a->b, b->c, c->a. + if (issubseg(topcastets[i])) { + tsspivot1(topcastets[i], checkseg); + tssbond1(fliptets[0], checkseg); + sstbond1(checkseg, fliptets[0]); + tssbond1(fliptets[1], checkseg); + sstbond1(checkseg, fliptets[1]); + if (fc->chkencflag & 1) { + enqueuesubface(badsubsegs, &checkseg); + } + } + enextself(fliptets[0]); + eprevself(fliptets[1]); + } + // The three top edges. + for (i = 0; i < 3; i++) { // edges b->d, c->d, a->d. + esym(fliptets[0], newface); + eprevself(newface); + enext(topcastets[i], casface); + if (issubseg(casface)) { + tsspivot1(casface, checkseg); + tssbond1(newface, checkseg); + sstbond1(checkseg, newface); + if (fc->chkencflag & 1) { + enqueuesubface(badsubsegs, &checkseg); + } + } + enextself(fliptets[0]); + } + // The three bot edges. + for (i = 0; i < 3; i++) { // edges b<-e, c<-e, a<-e. + esym(fliptets[1], newface); + enextself(newface); + eprev(botcastets[i], casface); + if (issubseg(casface)) { + tsspivot1(casface, checkseg); + tssbond1(newface, checkseg); + sstbond1(checkseg, newface); + if (fc->chkencflag & 1) { + enqueuesubface(badsubsegs, &checkseg); + } + } + eprevself(fliptets[1]); + } + } // if (checksubsegflag) + + if (checksubfaceflag) { + face checksh; + // Bond the top three casing subfaces. + for (i = 0; i < 3; i++) { // At edges [b,a], [c,b], [a,c] + if (issubface(topcastets[i])) { + tspivot(topcastets[i], checksh); + esym(fliptets[0], newface); + sesymself(checksh); + tsbond(newface, checksh); + if (fc->chkencflag & 2) { + enqueuesubface(badsubfacs, &checksh); + } + } + enextself(fliptets[0]); + } + // Bond the bottom three casing subfaces. + for (i = 0; i < 3; i++) { // At edges [a,b], [b,c], [c,a] + if (issubface(botcastets[i])) { + tspivot(botcastets[i], checksh); + esym(fliptets[1], newface); + sesymself(checksh); + tsbond(newface, checksh); + if (fc->chkencflag & 2) { + enqueuesubface(badsubfacs, &checksh); + } + } + eprevself(fliptets[1]); + } + + if (scount > 0) { + face flipfaces[2]; + // Perform a 2-to-2 flip in subfaces. + flipfaces[0] = flipshs[(spivot + 1) % 3]; + flipfaces[1] = flipshs[(spivot + 2) % 3]; + sesymself(flipfaces[1]); + flip22(flipfaces, 0, fc->chkencflag); + // Connect the flipped subfaces to flipped tets. + // First go to the corresponding flipping edge. + // Re-use top- and botcastets[0]. + topcastets[0] = fliptets[0]; + botcastets[0] = fliptets[1]; + for (i = 0; i < ((spivot + 1) % 3); i++) { + enextself(topcastets[0]); + eprevself(botcastets[0]); + } + // Connect the top subface to the top tets. + esymself(topcastets[0]); + sesymself(flipfaces[0]); + // Check if there already exists a subface. + tspivot(topcastets[0], checksh); + if (checksh.sh == NULL) { + tsbond(topcastets[0], flipfaces[0]); + fsymself(topcastets[0]); + sesymself(flipfaces[0]); + tsbond(topcastets[0], flipfaces[0]); + } else { + // An invalid 2-to-2 flip. Report a bug. + terminatetetgen(this, 2); + } + // Connect the bot subface to the bottom tets. + esymself(botcastets[0]); + sesymself(flipfaces[1]); + // Check if there already exists a subface. + tspivot(botcastets[0], checksh); + if (checksh.sh == NULL) { + tsbond(botcastets[0], flipfaces[1]); + fsymself(botcastets[0]); + sesymself(flipfaces[1]); + tsbond(botcastets[0], flipfaces[1]); + } else { + // An invalid 2-to-2 flip. Report a bug. + terminatetetgen(this, 2); + } + } // if (scount > 0) + } // if (checksubfaceflag) - // Retreat the three vertices of the base face. - searchtet->ver = 0; - torg = org(*searchtet); - tdest = dest(*searchtet); - tapex = apex(*searchtet); + if (fc->chkencflag & 4) { + // Put two new tets into check list. + for (i = 0; i < 2; i++) { + enqueuetetrahedron(&(fliptets[i])); + } + } - } // while (true) + setpoint2tet(pa, (tetrahedron) fliptets[0].tet); + setpoint2tet(pb, (tetrahedron) fliptets[0].tet); + setpoint2tet(pc, (tetrahedron) fliptets[0].tet); + setpoint2tet(pd, (tetrahedron) fliptets[0].tet); + setpoint2tet(pe, (tetrahedron) fliptets[1].tet); - if (histtetarray != NULL) { - // Unmark the visited tets. - for (i = 0; i < (int) histtetarray->objects; i++) { - parytet = (triface *) fastlookup(histtetarray, i); - uninfect(*parytet); + if (hullflag > 0) { + if (dummyflag != 0) { + // Restore the original position of the points (for flipnm()). + if (dummyflag == -1) { + // e were dummypoint. Swap the two new tets. + newface = fliptets[0]; + fliptets[0] = fliptets[1]; + fliptets[1] = newface; + } else { + // a or b was dummypoint. + if (dummyflag == 1) { + eprevself(fliptets[0]); + enextself(fliptets[1]); + } else { // dummyflag == 2 + enextself(fliptets[0]); + eprevself(fliptets[1]); + } + } + } + } + + if (fc->enqflag > 0) { + // Queue faces which may be locally non-Delaunay. + // pa = org(fliptets[0]); // 'a' may be a new vertex. + enextesym(fliptets[0], newface); + flippush(flipstack, &newface); + eprevesym(fliptets[1], newface); + flippush(flipstack, &newface); + if (fc->enqflag > 1) { + //pb = dest(fliptets[0]); + eprevesym(fliptets[0], newface); + flippush(flipstack, &newface); + enextesym(fliptets[1], newface); + flippush(flipstack, &newface); + //pc = apex(fliptets[0]); + esym(fliptets[0], newface); + flippush(flipstack, &newface); + esym(fliptets[1], newface); + flippush(flipstack, &newface); } - histtetarray->restart(); } - return loc; + recenttet = fliptets[0]; } /////////////////////////////////////////////////////////////////////////////// // // -// adjustlocate() Adjust the precise location of a vertex. // +// flip41() Perform a 4-to-1 flip (Remove 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. // +// 'fliptets' is an array of four tetrahedra in the star of the removing // +// vertex 'p'. Let the four vertices in the star of p be a, b, c, and d. The // +// four tets in 'fliptets' are: [p,d,a,b], [p,d,b,c], [p,d,c,a], and [a,b,c, // +// p]. On return, 'fliptets[0]' is the new tet [a,b,c,d]. // // // -// 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. // +// If 'hullflag' is set (> 0), one of the five vertices may be 'dummypoint'. // +// The 'hullsize' may be changed. Note that p may be dummypoint. In this // +// case, four hull tets are replaced by one real tet. // // // -// 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(). // +// If 'checksubface' flag is set (>0), it is possible that there are three // +// interior subfaces connecting at p. If so, a 3-to-1 flip is performed to // +// to remove p from the surface triangulation. // +// // +// If it is called by the routine incrementalflip(), we assume that d is the // +// newly inserted vertex. // // // /////////////////////////////////////////////////////////////////////////////// -enum tetgenmesh::locateresult tetgenmesh::adjustlocate(point searchpt, - triface* searchtet, enum locateresult precise, REAL epspp) +void tetgenmesh::flip41(triface* fliptets, int hullflag, flipconstraints *fc) { - point torg, tdest, tapex, toppo; - REAL s1, s2, s3, s4; + triface topcastets[3], botcastet; + triface newface, neightet; + face flipshs[4]; + point pa, pb, pc, pd, pp; + int dummyflag = 0; // in {0, 1, 2, 3, 4} + int spivot = -1, scount = 0; + int t1ver; + int i; - // 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); + pa = org(fliptets[3]); + pb = dest(fliptets[3]); + pc = apex(fliptets[3]); + pd = dest(fliptets[0]); + pp = org(fliptets[0]); // The removing vertex. + + flip41count++; + + // Get the outer boundary faces. + for (i = 0; i < 3; i++) { + enext(fliptets[i], topcastets[i]); + fnextself(topcastets[i]); // [d,a,b,#], [d,b,c,#], [d,c,a,#] + enextself(topcastets[i]); // [a,b,d,#], [b,c,d,#], [c,a,d,#] } - - if (s1 != 0.0) { - if (iscoplanar(tdest, torg, tapex, searchpt, s1, epspp)) { - s1 = 0.0; + fsym(fliptets[3], botcastet); // [b,a,c,#] + + if (checksubfaceflag) { + // Check if there are three subfaces at 'p'. + // Re-use 'newface'. + for (i = 0; i < 3; i++) { + fnext(fliptets[3], newface); // [a,b,p,d],[b,c,p,d],[c,a,p,d]. + tspivot(newface, flipshs[i]); + if (flipshs[i].sh != NULL) { + spivot = i; // Remember this subface. + scount++; + } + enextself(fliptets[3]); } - } - if (s1 < 0.0) { - return OUTSIDE; - } + if (scount > 0) { + // There are three subfaces connecting at p. + if (scount < 3) { + // The new subface is one of {[a,b,d], [b,c,d], [c,a,d]}. + assert(scount == 1); // spivot >= 0 + // Go to the tet containing the three subfaces. + fsym(topcastets[spivot], neightet); + // Get the three subfaces connecting at p. + for (i = 0; i < 3; i++) { + esym(neightet, newface); + tspivot(newface, flipshs[i]); + assert(flipshs[i].sh != NULL); + eprevself(neightet); + } + } else { + spivot = 3; // The new subface is [a,b,c]. + } + } + } // if (checksubfaceflag) - if (s2 != 0.0) { - if (iscoplanar(torg, tdest, toppo, searchpt, s2, epspp)) { - s2 = 0.0; + + // Re-use fliptets[0] for [a,b,c,d]. + fliptets[0].ver = 11; + setelemmarker(fliptets[0].tet, 0); // Clean all flags. + // NOTE: the element attributes and volume constraint remain unchanged. + if (checksubsegflag) { + // Dealloc the space to subsegments. + if (fliptets[0].tet[8] != NULL) { + tet2segpool->dealloc((shellface *) fliptets[0].tet[8]); + fliptets[0].tet[8] = NULL; } } - 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 (checksubfaceflag) { + // Dealloc the space to subfaces. + if (fliptets[0].tet[9] != NULL) { + tet2subpool->dealloc((shellface *) fliptets[0].tet[9]); + fliptets[0].tet[9] = NULL; } } - if (s3 < 0.0) { - enextfnextself(*searchtet); - return OUTSIDE; + // Delete the other three tets. + for (i = 1; i < 4; i++) { + tetrahedrondealloc(fliptets[i].tet); } - 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; + if (pp != dummypoint) { + // Mark the point pp as unused. + setpointtype(pp, UNUSEDVERTEX); + unuverts++; } - // 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; + // Create the new tet [a,b,c,d]. + if (hullflag > 0) { + // One of the five vertices may be 'dummypoint'. + if (pa == dummypoint) { + // pa is dummypoint. + setvertices(fliptets[0], pc, pb, pd, pa); + esymself(fliptets[0]); // [b,c,a,d] + eprevself(fliptets[0]); // [a,b,c,d] + dummyflag = 1; + } else if (pb == dummypoint) { + setvertices(fliptets[0], pa, pc, pd, pb); + esymself(fliptets[0]); // [c,a,b,d] + enextself(fliptets[0]); // [a,b,c,d] + dummyflag = 2; + } else if (pc == dummypoint) { + setvertices(fliptets[0], pb, pa, pd, pc); + esymself(fliptets[0]); // [a,b,c,d] + dummyflag = 3; + } else if (pd == dummypoint) { + setvertices(fliptets[0], pa, pb, pc, pd); + dummyflag = 4; + } else { + setvertices(fliptets[0], pa, pb, pc, pd); + if (pp == dummypoint) { + dummyflag = -1; + } else { + dummyflag = 0; } - // 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 (dummyflag > 0) { + // We deleted 3 hull tets, and create 1 hull tet. + hullsize -= 2; + } else if (dummyflag < 0) { + // We deleted 4 hull tets. + hullsize -= 4; + // meshedges does not change. } - if (s4 == 0.0) { - // On edge (tapex, torg). - enext2self(*searchtet); - return ONEDGE; + } else { + setvertices(fliptets[0], pa, pb, pc, pd); + } + + if (fc->remove_ndelaunay_edge) { // calc_tetprism_vol + REAL volneg[4], volpos[1], vol_diff; + if (dummyflag > 0) { + if (pa == dummypoint) { + volneg[0] = 0.; + volneg[1] = tetprismvol(pp, pd, pb, pc); + volneg[2] = 0.; + volneg[3] = 0.; + } else if (pb == dummypoint) { + volneg[0] = 0.; + volneg[1] = 0.; + volneg[2] = tetprismvol(pp, pd, pc, pa); + volneg[3] = 0.; + } else if (pc == dummypoint) { + volneg[0] = tetprismvol(pp, pd, pa, pb); + volneg[1] = 0.; + volneg[2] = 0.; + volneg[3] = 0.; + } else { // pd == dummypoint + volneg[0] = 0.; + volneg[1] = 0.; + volneg[2] = 0.; + volneg[3] = tetprismvol(pa, pb, pc, pp); + } + volpos[0] = 0.; + } else if (dummyflag < 0) { + volneg[0] = 0.; + volneg[1] = 0.; + volneg[2] = 0.; + volneg[3] = 0.; + volpos[0] = tetprismvol(pa, pb, pc, pd); + } else { + volneg[0] = tetprismvol(pp, pd, pa, pb); + volneg[1] = tetprismvol(pp, pd, pb, pc); + volneg[2] = tetprismvol(pp, pd, pc, pa); + volneg[3] = tetprismvol(pa, pb, pc, pp); + volpos[0] = tetprismvol(pa, pb, pc, pd); } - // On face (torg, tdest, tapex). - return ONFACE; + vol_diff = volpos[0] - volneg[0] - volneg[1] - volneg[2] - volneg[3]; + fc->tetprism_vol_sum += vol_diff; // Update the total sum. + } + + // Bond the new tet to adjacent tets. + for (i = 0; i < 3; i++) { + esym(fliptets[0], newface); // At faces [b,a,d], [c,b,d], [a,c,d]. + bond(newface, topcastets[i]); + enextself(fliptets[0]); } - if (s2 == 0.0) { - fnextself(*searchtet); - if (s3 == 0.0) { - if (s4 == 0.0) { - // On toppo. - enext2self(*searchtet); - return ONVERTEX; + bond(fliptets[0], botcastet); + + if (checksubsegflag) { + face checkseg; + // Bond 6 segments (at edges of [a,b,c,d]) if there there are. + for (i = 0; i < 3; i++) { + eprev(topcastets[i], newface); // At edges [d,a],[d,b],[d,c]. + if (issubseg(newface)) { + tsspivot1(newface, checkseg); + esym(fliptets[0], newface); + enextself(newface); // At edges [a,d], [b,d], [c,d]. + tssbond1(newface, checkseg); + sstbond1(checkseg, newface); + if (fc->chkencflag & 1) { + enqueuesubface(badsubsegs, &checkseg); + } } - // On edge (tdest, toppo). - enextself(*searchtet); - return ONEDGE; + enextself(fliptets[0]); } - 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; + for (i = 0; i < 3; i++) { + if (issubseg(topcastets[i])) { + tsspivot1(topcastets[i], checkseg); // At edges [a,b],[b,c],[c,a]. + tssbond1(fliptets[0], checkseg); + sstbond1(checkseg, fliptets[0]); + if (fc->chkencflag & 1) { + enqueuesubface(badsubsegs, &checkseg); + } + } + enextself(fliptets[0]); } - // On face (tdest, tapex, toppo). - return ONFACE; - } - if (s4 == 0.0) { - enext2fnextself(*searchtet); - // On face (tapex, torg, toppo). - return ONFACE; } - // Inside tetrahedron. - return INTETRAHEDRON; -} + if (checksubfaceflag) { + face checksh; + // Bond 4 subfaces (at faces of [a,b,c,d]) if there are. + for (i = 0; i < 3; i++) { + if (issubface(topcastets[i])) { + tspivot(topcastets[i], checksh); // At faces [a,b,d],[b,c,d],[c,a,d] + esym(fliptets[0], newface); // At faces [b,a,d],[c,b,d],[a,c,d] + sesymself(checksh); + tsbond(newface, checksh); + if (fc->chkencflag & 2) { + enqueuesubface(badsubfacs, &checksh); + } + } + enextself(fliptets[0]); + } + if (issubface(botcastet)) { + tspivot(botcastet, checksh); // At face [b,a,c] + sesymself(checksh); + tsbond(fliptets[0], checksh); + if (fc->chkencflag & 2) { + enqueuesubface(badsubfacs, &checksh); + } + } -/////////////////////////////////////////////////////////////////////////////// -// // -// hullwalk() Find a tetrahedron on the hull to continue search. // -// // -/////////////////////////////////////////////////////////////////////////////// + if (spivot >= 0) { + // Perform a 3-to-1 flip in surface triangulation. + // Depending on the value of 'spivot', the three subfaces are: + // - 0: [a,b,p], [b,d,p], [d,a,p] + // - 1: [b,c,p], [c,d,p], [d,b,p] + // - 2: [c,a,p], [a,d,p], [d,c,p] + // - 3: [a,b,p], [b,c,p], [c,a,p] + // Adjust the three subfaces such that their origins are p, i.e., + // - 3: [p,a,b], [p,b,c], [p,c,a]. (Required by the flip31()). + for (i = 0; i < 3; i++) { + senext2self(flipshs[i]); + } + flip31(flipshs, 0); + // Delete the three old subfaces. + for (i = 0; i < 3; i++) { + shellfacedealloc(subfaces, flipshs[i].sh); + } + if (spivot < 3) { + // // Bond the new subface to the new tet [a,b,c,d]. + tsbond(topcastets[spivot], flipshs[3]); + fsym(topcastets[spivot], newface); + sesym(flipshs[3], checksh); + tsbond(newface, checksh); + } else { + // Bound the new subface [a,b,c] to the new tet [a,b,c,d]. + tsbond(fliptets[0], flipshs[3]); + fsym(fliptets[0], newface); + sesym(flipshs[3], checksh); + tsbond(newface, checksh); + } + } // if (spivot > 0) + } // if (checksubfaceflag) -enum tetgenmesh::locateresult tetgenmesh::hullwalk(point searchpt, - triface *hulltet) -{ - list* travtetlist; - triface travtet, neightet; - point pa, pb, pc, pp[3]; - enum locateresult loc; - REAL prjpt[3]; - REAL ori; - int i, j; + if (fc->chkencflag & 4) { + enqueuetetrahedron(&(fliptets[0])); + } - 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); + // Update the point-to-tet map. + setpoint2tet(pa, (tetrahedron) fliptets[0].tet); + setpoint2tet(pb, (tetrahedron) fliptets[0].tet); + setpoint2tet(pc, (tetrahedron) fliptets[0].tet); + setpoint2tet(pd, (tetrahedron) fliptets[0].tet); + + if (fc->enqflag > 0) { + // Queue faces which may be locally non-Delaunay. + flippush(flipstack, &(fliptets[0])); // [a,b,c] (opposite to new point). + if (fc->enqflag > 1) { + for (i = 0; i < 3; i++) { + esym(fliptets[0], newface); + flippush(flipstack, &newface); + enextself(fliptets[0]); + } + } } - delete travtetlist; - return loc; + recenttet = fliptets[0]; } /////////////////////////////////////////////////////////////////////////////// // // -// locatesub() Find a point in the surface mesh of a facet. // +// flipnm() Flip an edge through a sequence of elementary flips. // // // -// Searching begins from the input 'searchsh', it should be a handle on the // -// convex hull of the facet triangulation. // +// 'abtets' is an array of 'n' tets in the star of edge [a,b].These tets are // +// ordered in a counterclockwise cycle with respect to the vector a->b, i.e.,// +// use the right-hand rule. // // // -// If 'stopatseg' is nonzero, the search will stop if it tries to walk // -// through a subsegment, and will return OUTSIDE. // +// 'level' (>= 0) indicates the current link level. If 'level > 0', we are // +// flipping a link edge of an edge [a',b'], and 'abedgepivot' indicates // +// which link edge, i.e., [c',b'] or [a',c'], is [a,b] These two parameters // +// allow us to determine the new tets after a 3-to-2 flip, i.e., tets that // +// do not inside the reduced star of edge [a',b']. // // // -// 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. // +// If the flag 'fc->unflip' is set, this routine un-does the flips performed // +// in flipnm([a,b]) so that the mesh is returned to its original state // +// before doing the flipnm([a,b]) operation. // // // -// WARNING: This routine is designed for convex triangulations, and will not // -// not generally work after the holes and concavities have been carved. // +// The return value is an integer nn, where nn <= n. If nn is 2, then the // +// edge is flipped. The first and the second tets in 'abtets' are new tets. // +// Otherwise, nn > 2, the edge is not flipped, and nn is the number of tets // +// in the current star of [a,b]. // +// // +// ASSUMPTIONS: // +// - Neither a nor b is 'dummypoint'. // +// - [a,b] must not be a segment. // // // /////////////////////////////////////////////////////////////////////////////// -enum tetgenmesh::locateresult tetgenmesh::locatesub(point searchpt, - face* searchsh, int stopatseg, REAL epspp) +int tetgenmesh::flipnm(triface* abtets, int n, int level, int abedgepivot, + flipconstraints* fc) { - 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; + triface fliptets[3], spintet, flipedge; + triface *tmpabtets, *parytet; + point pa, pb, pc, pd, pe, pf; + REAL ori; + int hullflag, hulledgeflag; + int reducflag, rejflag; + int reflexlinkedgecount; + int edgepivot; + int n1, nn; + int t1ver; + int i, j; + + pa = org(abtets[0]); + pb = dest(abtets[0]); + + if (n > 3) { + // Try to reduce the size of the Star(ab) by flipping a face in it. + reflexlinkedgecount = 0; + + for (i = 0; i < n; i++) { + // Let the face of 'abtets[i]' be [a,b,c]. + if (checksubfaceflag) { + if (issubface(abtets[i])) { + continue; // Skip a subface. + } } - } - // 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; + // Do not flip this face if it is involved in two Stars. + if ((elemcounter(abtets[i]) > 1) || + (elemcounter(abtets[(i - 1 + n) % n]) > 1)) { + continue; } - } - 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; + + pc = apex(abtets[i]); + pd = apex(abtets[(i + 1) % n]); + pe = apex(abtets[(i - 1 + n) % n]); + if ((pd == dummypoint) || (pe == dummypoint)) { + continue; // [a,b,c] is a hull face. } - } - // 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; + + + // Decide whether [a,b,c] is flippable or not. + reducflag = 0; + + hullflag = (pc == dummypoint); // pc may be dummypoint. + hulledgeflag = 0; + if (hullflag == 0) { + ori = orient3d(pb, pc, pd, pe); // Is [b,c] locally convex? + if (ori > 0) { + ori = orient3d(pc, pa, pd, pe); // Is [c,a] locally convex? + if (ori > 0) { + // Test if [a,b] is locally convex OR flat. + ori = orient3d(pa, pb, pd, pe); + if (ori > 0) { + // Found a 2-to-3 flip: [a,b,c] => [e,d] + reducflag = 1; + } else if (ori == 0) { + // [a,b] is flat. + if (n == 4) { + // The "flat" tet can be removed immediately by a 3-to-2 flip. + reducflag = 1; + // Check if [e,d] is a hull edge. + pf = apex(abtets[(i + 2) % n]); + hulledgeflag = (pf == dummypoint); + } + } + } } - // Are they belong to the same facet. - if (shellmark(spinsh) == shellmark(backtracksh)) { - // Find a coplanar subface. Walk into it. - *searchsh = spinsh; - break; + if (!reducflag) { + reflexlinkedgecount++; } - // 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; + } else { + // 'c' is dummypoint. + if (n == 4) { + // Let the vertex opposite to 'c' is 'f'. + // A 4-to-4 flip is possible if the two tets [d,e,f,a] and [e,d,f,b] + // are valid tets. + // Note: When the mesh is not convex, it is possible that [a,b] is + // locally non-convex (at hull faces [a,b,e] and [b,a,d]). + // In this case, an edge flip [a,b] to [e,d] is still possible. + pf = apex(abtets[(i + 2) % n]); + assert(pf != dummypoint); + ori = orient3d(pd, pe, pf, pa); + if (ori < 0) { + ori = orient3d(pe, pd, pf, pb); + if (ori < 0) { + // Found a 4-to-4 flip: [a,b] => [e,d] + reducflag = 1; + ori = 0; // Signal as a 4-to-4 flip (like a co-planar case). + hulledgeflag = 1; // [e,d] is a hull edge. + } + } } - } 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 - } -} + } // if (hullflag) + + if (reducflag) { + if (nonconvex && hulledgeflag) { + // We will create a hull edge [e,d]. Make sure it does not exist. + if (getedge(pe, pd, &spintet)) { + // The 2-to-3 flip is not a topological valid flip. + reducflag = 0; + } + } + } + + if (reducflag) { + // [a,b,c] could be removed by a 2-to-3 flip. + rejflag = 0; + if (fc->checkflipeligibility) { + // Check if the flip can be performed. + rejflag = checkflipeligibility(1, pa, pb, pc, pd, pe, level, + abedgepivot, fc); + } + if (!rejflag) { + // Do flip: [a,b,c] => [e,d]. + fliptets[0] = abtets[i]; + fsym(fliptets[0], fliptets[1]); // abtets[i-1]. + flip23(fliptets, hullflag, fc); + + // Shrink the array 'abtets', maintain the original order. + // Two tets 'abtets[i-1] ([a,b,e,c])' and 'abtets[i] ([a,b,c,d])' + // are flipped, i.e., they do not in Star(ab) anymore. + // 'fliptets[0]' ([e,d,a,b]) is in Star(ab), it is saved in + // 'abtets[i-1]' (adjust it to be [a,b,e,d]), see below: + // + // before after + // [0] |___________| [0] |___________| + // ... |___________| ... |___________| + // [i-1] |_[a,b,e,c]_| [i-1] |_[a,b,e,d]_| + // [i] |_[a,b,c,d]_| --> [i] |_[a,b,d,#]_| + // [i+1] |_[a,b,d,#]_| [i+1] |_[a,b,#,*]_| + // ... |___________| ... |___________| + // [n-2] |___________| [n-2] |___________| + // [n-1] |___________| [n-1] |_[i]_2-t-3_| + // + edestoppoself(fliptets[0]); // [a,b,e,d] + // Increase the counter of this new tet (it is in Star(ab)). + increaseelemcounter(fliptets[0]); + abtets[(i - 1 + n) % n] = fliptets[0]; + for (j = i; j < n - 1; j++) { + abtets[j] = abtets[j + 1]; // Upshift + } + // The last entry 'abtets[n-1]' is empty. It is used in two ways: + // (i) it remembers the vertex 'c' (in 'abtets[n-1].tet'), and + // (ii) it remembers the position [i] where this flip took place. + // These informations let us to either undo this flip or recover + // the original edge link (for collecting new created tets). + //abtets[n - 1] = fliptets[1]; // [e,d,b,c] is remembered. + abtets[n - 1].tet = (tetrahedron *) pc; + abtets[n - 1].ver = 0; // Clear it. + // 'abtets[n - 1].ver' is in range [0,11] -- only uses 4 bits. + // Use the 5th bit in 'abtets[n - 1].ver' to signal a 2-to-3 flip. + abtets[n - 1].ver |= (1 << 4); + // The poisition [i] of this flip is saved above the 7th bit. + abtets[n - 1].ver |= (i << 6); + + if (fc->collectnewtets) { + // Push the two new tets [e,d,b,c] and [e,d,c,a] into a stack. + // Re-use the global array 'cavetetlist'. + for (j = 1; j < 3; j++) { + cavetetlist->newindex((void **) &parytet); + *parytet = fliptets[j]; // fliptets[1], fliptets[2]. + } + } -/////////////////////////////////////////////////////////////////////////////// -// // -// 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'. // -// // -/////////////////////////////////////////////////////////////////////////////// + // Star(ab) is reduced. Try to flip the edge [a,b]. + nn = flipnm(abtets, n - 1, level, abedgepivot, fc); + + if (nn == 2) { + // The edge has been flipped. + return nn; + } else { // if (nn > 2) + // The edge is not flipped. + if (fc->unflip || (ori == 0)) { + // Undo the previous 2-to-3 flip, i.e., do a 3-to-2 flip to + // transform [e,d] => [a,b,c]. + // 'ori == 0' means that the previous flip created a degenerated + // tet. It must be removed. + // Remember that 'abtets[i-1]' is [a,b,e,d]. We can use it to + // find another two tets [e,d,b,c] and [e,d,c,a]. + fliptets[0] = abtets[(i-1 + (n-1)) % (n-1)]; // [a,b,e,d] + edestoppoself(fliptets[0]); // [e,d,a,b] + fnext(fliptets[0], fliptets[1]); // [1] is [e,d,b,c] + fnext(fliptets[1], fliptets[2]); // [2] is [e,d,c,a] + assert(apex(fliptets[0]) == oppo(fliptets[2])); // SELF_CHECK + // Restore the two original tets in Star(ab). + flip32(fliptets, hullflag, fc); + // Marktest the two restored tets in Star(ab). + for (j = 0; j < 2; j++) { + increaseelemcounter(fliptets[j]); + } + // Expand the array 'abtets', maintain the original order. + for (j = n - 2; j>= i; j--) { + abtets[j + 1] = abtets[j]; // Downshift + } + // Insert the two new tets 'fliptets[0]' [a,b,c,d] and + // 'fliptets[1]' [b,a,c,e] into the (i-1)-th and i-th entries, + // respectively. + esym(fliptets[1], abtets[(i - 1 + n) % n]); // [a,b,e,c] + abtets[i] = fliptets[0]; // [a,b,c,d] + nn++; + if (fc->collectnewtets) { + // Pop two (flipped) tets from the stack. + cavetetlist->objects -= 2; + } + } // if (unflip || (ori == 0)) + } // if (nn > 2) -enum tetgenmesh::locateresult tetgenmesh:: -adjustlocatesub(point searchpt, face* searchsh, enum locateresult precise, - REAL epspp) -{ - point pa, pb, pc; - bool s1, s2, s3; + if (!fc->unflip) { + // The flips are not reversed. The current Star(ab) can not be + // further reduced. Return its current size (# of tets). + return nn; + } + // unflip is set. + // Continue the search for flips. + } + } // if (reducflag) + } // i - pa = sorg(*searchsh); - pb = sdest(*searchsh); - pc = sapex(*searchsh); + // The Star(ab) is not reduced. + if (reflexlinkedgecount > 0) { + // There are reflex edges in the Link(ab). + if (((b->fliplinklevel < 0) && (level < autofliplinklevel)) || + ((b->fliplinklevel >= 0) && (level < b->fliplinklevel))) { + // Try to reduce the Star(ab) by flipping a reflex edge in Link(ab). + for (i = 0; i < n; i++) { + // Do not flip this face [a,b,c] if there are two Stars involved. + if ((elemcounter(abtets[i]) > 1) || + (elemcounter(abtets[(i - 1 + n) % n]) > 1)) { + continue; + } + pc = apex(abtets[i]); + if (pc == dummypoint) { + continue; // [a,b] is a hull edge. + } + pd = apex(abtets[(i + 1) % n]); + pe = apex(abtets[(i - 1 + n) % n]); + if ((pd == dummypoint) || (pe == dummypoint)) { + continue; // [a,b,c] is a hull face. + } + + + edgepivot = 0; // No edge is selected yet. + + // Test if [b,c] is locally convex or flat. + ori = orient3d(pb, pc, pd, pe); + if (ori <= 0) { + // Select the edge [c,b]. + enext(abtets[i], flipedge); // [b,c,a,d] + edgepivot = 1; + } + if (!edgepivot) { + // Test if [c,a] is locally convex or flat. + ori = orient3d(pc, pa, pd, pe); + if (ori <= 0) { + // Select the edge [a,c]. + eprev(abtets[i], flipedge); // [c,a,b,d]. + edgepivot = 2; + } + } - if (precise == ONEDGE) { - s1 = true; + if (!edgepivot) continue; + + // An edge is selected. + if (checksubsegflag) { + // Do not flip it if it is a segment. + if (issubseg(flipedge)) { + if (fc->collectencsegflag) { + face checkseg, *paryseg; + tsspivot1(flipedge, checkseg); + if (!sinfected(checkseg)) { + // Queue this segment in list. + sinfect(checkseg); + caveencseglist->newindex((void **) &paryseg); + *paryseg = checkseg; + } + } + continue; + } + } + + // Try to flip the selected edge ([c,b] or [a,c]). + esymself(flipedge); + // Count the number of tets at the edge. + n1 = 0; + j = 0; // Sum of the star counters. + spintet = flipedge; + while (1) { + n1++; + j += (elemcounter(spintet)); + fnextself(spintet); + if (spintet.tet == flipedge.tet) break; + } + assert(n1 >= 3); + if (j > 2) { + // The Star(flipedge) overlaps other Stars. + continue; // Do not flip this edge. + } + // Only two tets can be marktested. + assert(j == 2); + + if ((b->flipstarsize > 0) && (n1 > b->flipstarsize)) { + // The star size exceeds the given limit. + continue; // Do not flip it. + } + + // Allocate spaces for Star(flipedge). + tmpabtets = new triface[n1]; + // Form the Star(flipedge). + j = 0; + spintet = flipedge; + while (1) { + tmpabtets[j] = spintet; + // Increase the star counter of this tet. + increaseelemcounter(tmpabtets[j]); + j++; + fnextself(spintet); + if (spintet.tet == flipedge.tet) break; + } + + // Try to flip the selected edge away. + nn = flipnm(tmpabtets, n1, level + 1, edgepivot, fc); + + if (nn == 2) { + // The edge is flipped. Star(ab) is reduced. + // Shrink the array 'abtets', maintain the original order. + if (edgepivot == 1) { + // 'tmpabtets[0]' is [d,a,e,b] => contains [a,b]. + spintet = tmpabtets[0]; // [d,a,e,b] + enextself(spintet); + esymself(spintet); + enextself(spintet); // [a,b,e,d] + } else { + // 'tmpabtets[1]' is [b,d,e,a] => contains [a,b]. + spintet = tmpabtets[1]; // [b,d,e,a] + eprevself(spintet); + esymself(spintet); + eprevself(spintet); // [a,b,e,d] + } // edgepivot == 2 + assert(elemcounter(spintet) == 0); // It's a new tet. + increaseelemcounter(spintet); // It is in Star(ab). + // Put the new tet at [i-1]-th entry. + abtets[(i - 1 + n) % n] = spintet; + for (j = i; j < n - 1; j++) { + abtets[j] = abtets[j + 1]; // Upshift + } + // Remember the flips in the last entry of the array 'abtets'. + // They can be used to recover the flipped edge. + abtets[n - 1].tet = (tetrahedron *) tmpabtets; // The star(fedge). + abtets[n - 1].ver = 0; // Clear it. + // Use the 1st and 2nd bit to save 'edgepivot' (1 or 2). + abtets[n - 1].ver |= edgepivot; + // Use the 6th bit to signal this n1-to-m1 flip. + abtets[n - 1].ver |= (1 << 5); + // The poisition [i] of this flip is saved from 7th to 19th bit. + abtets[n - 1].ver |= (i << 6); + // The size of the star 'n1' is saved from 20th bit. + abtets[n - 1].ver |= (n1 << 19); + + // Remember the flipped link vertex 'c'. It can be used to recover + // the original edge link of [a,b], and to collect new tets. + tmpabtets[0].tet = (tetrahedron *) pc; + tmpabtets[0].ver = (1 << 5); // Flag it as a vertex handle. + + // Continue to flip the edge [a,b]. + nn = flipnm(abtets, n - 1, level, abedgepivot, fc); + + if (nn == 2) { + // The edge has been flipped. + return nn; + } else { // if (nn > 2) { + // The edge is not flipped. + if (fc->unflip) { + // Recover the flipped edge ([c,b] or [a,c]). + assert(nn == (n - 1)); + // The sequence of flips are saved in 'tmpabtets'. + // abtets[(i-1) % (n-1)] is [a,b,e,d], i.e., the tet created by + // the flipping of edge [c,b] or [a,c].It must still exist in + // Star(ab). It is the start tet to recover the flipped edge. + if (edgepivot == 1) { + // The flip edge is [c,b]. + tmpabtets[0] = abtets[((i-1)+(n-1))%(n-1)]; // [a,b,e,d] + eprevself(tmpabtets[0]); + esymself(tmpabtets[0]); + eprevself(tmpabtets[0]); // [d,a,e,b] + fsym(tmpabtets[0], tmpabtets[1]); // [a,d,e,c] + } else { + // The flip edge is [a,c]. + tmpabtets[1] = abtets[((i-1)+(n-1))%(n-1)]; // [a,b,e,d] + enextself(tmpabtets[1]); + esymself(tmpabtets[1]); + enextself(tmpabtets[1]); // [b,d,e,a] + fsym(tmpabtets[1], tmpabtets[0]); // [d,b,e,c] + } // if (edgepivot == 2) + + // Recover the flipped edge ([c,b] or [a,c]). + flipnm_post(tmpabtets, n1, 2, edgepivot, fc); + + // Insert the two recovered tets into Star(ab). + for (j = n - 2; j >= i; j--) { + abtets[j + 1] = abtets[j]; // Downshift + } + if (edgepivot == 1) { + // tmpabtets[0] is [c,b,d,a] ==> contains [a,b] + // tmpabtets[1] is [c,b,a,e] ==> contains [a,b] + // tmpabtets[2] is [c,b,e,d] + fliptets[0] = tmpabtets[1]; + enextself(fliptets[0]); + esymself(fliptets[0]); // [a,b,e,c] + fliptets[1] = tmpabtets[0]; + esymself(fliptets[1]); + eprevself(fliptets[1]); // [a,b,c,d] + } else { + // tmpabtets[0] is [a,c,d,b] ==> contains [a,b] + // tmpabtets[1] is [a,c,b,e] ==> contains [a,b] + // tmpabtets[2] is [a,c,e,d] + fliptets[0] = tmpabtets[1]; + eprevself(fliptets[0]); + esymself(fliptets[0]); // [a,b,e,c] + fliptets[1] = tmpabtets[0]; + esymself(fliptets[1]); + enextself(fliptets[1]); // [a,b,c,d] + } // edgepivot == 2 + for (j = 0; j < 2; j++) { + increaseelemcounter(fliptets[j]); + } + // Insert the two recovered tets into Star(ab). + abtets[(i - 1 + n) % n] = fliptets[0]; + abtets[i] = fliptets[1]; + nn++; + // Release the allocated spaces. + delete [] tmpabtets; + } // if (unflip) + } // if (nn > 2) + + if (!fc->unflip) { + // The flips are not reversed. The current Star(ab) can not be + // further reduced. Return its size (# of tets). + return nn; + } + // unflip is set. + // Continue the search for flips. + } else { + // The selected edge is not flipped. + if (fc->unflip) { + // The memory should already be freed. + assert(nn == n1); + } else { + // Release the memory used in this attempted flip. + flipnm_post(tmpabtets, n1, nn, edgepivot, fc); + } + // Decrease the star counters of tets in Star(flipedge). + for (j = 0; j < nn; j++) { + assert(elemcounter(tmpabtets[j]) > 0); // SELF_CHECK + decreaseelemcounter(tmpabtets[j]); + } + // Release the allocated spaces. + delete [] tmpabtets; + } + } // i + } // if (level...) + } // if (reflexlinkedgecount > 0) } 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; + // Check if a 3-to-2 flip is possible. + // Let the three apexes be c, d,and e. Hull tets may be involved. If so, + // we rearrange them such that the vertex e is dummypoint. + hullflag = 0; + + if (apex(abtets[0]) == dummypoint) { + pc = apex(abtets[1]); + pd = apex(abtets[2]); + pe = apex(abtets[0]); + hullflag = 1; + } else if (apex(abtets[1]) == dummypoint) { + pc = apex(abtets[2]); + pd = apex(abtets[0]); + pe = apex(abtets[1]); + hullflag = 2; } else { - // on edge pb->pc. - senextself(*searchsh); - return ONEDGE; + pc = apex(abtets[0]); + pd = apex(abtets[1]); + pe = apex(abtets[2]); + hullflag = (pe == dummypoint) ? 3 : 0; } - } 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. // -// // -/////////////////////////////////////////////////////////////////////////////// + reducflag = 0; + rejflag = 0; -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; + if (hullflag == 0) { + // Make sure that no inverted tet will be created, i.e. the new tets + // [d,c,e,a] and [c,d,e,b] must be valid tets. + ori = orient3d(pd, pc, pe, pa); + if (ori < 0) { + ori = orient3d(pc, pd, pe, pb); + if (ori < 0) { + reducflag = 1; + } } } 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; + // [a,b] is a hull edge. + // Note: This can happen when it is in the middle of a 4-to-4 flip. + // Note: [a,b] may even be a non-convex hull edge. + if (!nonconvex) { + // The mesh is convex, only do flip if it is a coplanar hull edge. + ori = orient3d(pa, pb, pc, pd); + if (ori == 0) { + reducflag = 1; + } + } else { // nonconvex + reducflag = 1; + } + if (reducflag == 1) { + // [a,b], [a,b,c] and [a,b,d] are on the convex hull. + // Make sure that no inverted tet will be created. + point searchpt = NULL, chkpt; + REAL bigvol = 0.0, ori1, ori2; + // Search an interior vertex which is an apex of edge [c,d]. + // In principle, it can be arbitrary interior vertex. To avoid + // numerical issue, we choose the vertex which belongs to a tet + // 't' at edge [c,d] and 't' has the biggest volume. + fliptets[0] = abtets[hullflag % 3]; // [a,b,c,d]. + eorgoppoself(fliptets[0]); // [d,c,b,a] + spintet = fliptets[0]; + while (1) { + fnextself(spintet); + chkpt = oppo(spintet); + if (chkpt == pb) break; + if ((chkpt != dummypoint) && (apex(spintet) != dummypoint)) { + ori = -orient3d(pd, pc, apex(spintet), chkpt); + assert(ori > 0); + if (ori > bigvol) { + bigvol = ori; + searchpt = chkpt; + } + } } - } 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; + if (searchpt != NULL) { + // Now valid the configuration. + ori1 = orient3d(pd, pc, searchpt, pa); + ori2 = orient3d(pd, pc, searchpt, pb); + if (ori1 * ori2 >= 0.0) { + reducflag = 0; // Not valid. + } else { + ori1 = orient3d(pa, pb, searchpt, pc); + ori2 = orient3d(pa, pb, searchpt, pd); + if (ori1 * ori2 >= 0.0) { + reducflag = 0; // Not valid. + } + } } else { -#ifdef SELF_CHECK - assert(searchpt[i] == pa[i]); -#endif - return ONVERTEX; + // No valid searchpt is found. + reducflag = 0; // Do not flip it. + } + } // if (reducflag == 1) + } // if (hullflag == 1) + + if (reducflag) { + // A 3-to-2 flip is possible. + if (checksubfaceflag) { + // This edge (must not be a segment) can be flipped ONLY IF it belongs + // to either 0 or 2 subfaces. In the latter case, a 2-to-2 flip in + // the surface mesh will be automatically performed within the + // 3-to-2 flip. + nn = 0; + edgepivot = -1; // Re-use it. + for (j = 0; j < 3; j++) { + if (issubface(abtets[j])) { + nn++; // Found a subface. + } else { + edgepivot = j; + } + } + assert(nn < 3); + if (nn == 1) { + // Found only 1 subface containing this edge. This can happen in + // the boundary recovery phase. The neighbor subface is not yet + // recovered. This edge should not be flipped at this moment. + rejflag = 1; + } else if (nn == 2) { + // Found two subfaces. A 2-to-2 flip is possible. Validate it. + // Below we check if the two faces [p,q,a] and [p,q,b] are subfaces. + eorgoppo(abtets[(edgepivot + 1) % 3], spintet); // [q,p,b,a] + if (issubface(spintet)) { + rejflag = 1; // Conflict to a 2-to-2 flip. + } else { + esymself(spintet); + if (issubface(spintet)) { + rejflag = 1; // Conflict to a 2-to-2 flip. + } + } } - } 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; - } + if (!rejflag && fc->checkflipeligibility) { + // Here we must exchange 'a' and 'b'. Since in the check... function, + // we assume the following point sequence, 'a,b,c,d,e', where + // the face [a,b,c] will be flipped and the edge [e,d] will be + // created. The two new tets are [a,b,c,d] and [b,a,c,e]. + rejflag = checkflipeligibility(2, pc, pd, pe, pb, pa, level, + abedgepivot, fc); + } + if (!rejflag) { + // Do flip: [a,b] => [c,d,e] + flip32(abtets, hullflag, fc); + if (fc->remove_ndelaunay_edge) { + if (level == 0) { + // It is the desired removing edge. Check if we have improved + // the objective function. + if ((fc->tetprism_vol_sum >= 0.0) || + (fabs(fc->tetprism_vol_sum) < fc->bak_tetprism_vol)) { + // No improvement! flip back: [c,d,e] => [a,b]. + flip23(abtets, hullflag, fc); + // Increase the element counter -- They are in cavity. + for (j = 0; j < 3; j++) { + increaseelemcounter(abtets[j]); + } + return 3; + } + } // if (level == 0) + } + if (fc->collectnewtets) { + // Collect new tets. + if (level == 0) { + // Push the two new tets into stack. + for (j = 0; j < 2; j++) { + cavetetlist->newindex((void **) &parytet); + *parytet = abtets[j]; + } + } else { + // Only one of the new tets is collected. The other one is inside + // the reduced edge star. 'abedgepivot' is either '1' or '2'. + cavetetlist->newindex((void **) &parytet); + if (abedgepivot == 1) { // [c,b] + *parytet = abtets[1]; + } else { + assert(abedgepivot == 2); // [a,c] + *parytet = abtets[0]; + } + } + } // if (fc->collectnewtets) + return 2; + } + } // if (reducflag) + } // if (n == 3) - return precise; + // The current (reduced) Star size. + return n; } -//// //// -//// //// -//// geom_cxx ///////////////////////////////////////////////////////////////// - -//// flip_cxx ///////////////////////////////////////////////////////////////// -//// //// -//// //// - /////////////////////////////////////////////////////////////////////////////// // // -// enqueueflipface(), enqueueflipedge() Queue a face (or an edge). // +// flipnm_post() Post process a n-to-m flip. // // // -// 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. // +// IMPORTANT: This routine only works when there is no other flip operation // +// is done after flipnm([a,b]) which attempts to remove an edge [a,b]. // // // -/////////////////////////////////////////////////////////////////////////////// - -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); -} - -/////////////////////////////////////////////////////////////////////////////// +// 'abtets' is an array of 'n' (>= 3) tets which are in the original star of // +// [a,b] before flipnm([a,b]). 'nn' (< n) is the value returned by flipnm. // +// If 'nn == 2', the edge [a,b] has been flipped. 'abtets[0]' and 'abtets[1]'// +// are [c,d,e,b] and [d,c,e,a], i.e., a 2-to-3 flip can recover the edge [a, // +// b] and its initial Star([a,b]). If 'nn >= 3' edge [a,b] still exists in // +// current mesh and 'nn' is the current number of tets in Star([a,b]). // // // -// flip23() Perform a 2-to-3 flip. // +// Each 'abtets[i]', where nn <= i < n, saves either a 2-to-3 flip or a // +// flipnm([p1,p2]) operation ([p1,p2] != [a,b]) which created the tet // +// 'abtets[t-1]', where '0 <= t <= i'. These information can be used to // +// undo the flips performed in flipnm([a,b]) or to collect new tets created // +// by the flipnm([a,b]) operation. // // // -// 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. // +// Default, this routine only walks through the flips and frees the spaces // +// allocated during the flipnm([a,b]) operation. // // // -// 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. // +// If the flag 'fc->unflip' is set, this routine un-does the flips performed // +// in flipnm([a,b]) so that the mesh is returned to its original state // +// before doing the flipnm([a,b]) operation. // // // -// 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) +int tetgenmesh::flipnm_post(triface* abtets, int n, int nn, int abedgepivot, + flipconstraints* fc) { - 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; + triface fliptets[3], flipface; + triface *tmpabtets; + int fliptype; + int edgepivot; + int t, n1; + int i, j; - 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 > 1) { - 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); - } - 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); - } + if (nn == 2) { + // The edge [a,b] has been flipped. + // 'abtets[0]' is [c,d,e,b] or [#,#,#,b]. + // 'abtets[1]' is [d,c,e,a] or [#,#,#,a]. + if (fc->unflip) { + // Do a 2-to-3 flip to recover the edge [a,b]. There may be hull tets. + flip23(abtets, 1, fc); + if (fc->collectnewtets) { + // Pop up new (flipped) tets from the stack. + if (abedgepivot == 0) { + // Two new tets were collected. + cavetetlist->objects -= 2; + } else { + // Only one of the two new tets was collected. + cavetetlist->objects -= 1; + } + } + } + // The initial size of Star(ab) is 3. + nn++; } - if (checksubsegs) { - for (i = 0; i < 6; i++) { - edab.loc = edge2locver[i][0]; - edab.ver = edge2locver[i][1]; - tssdissolve1(edab); + + // Walk through the performed flips. + for (i = nn; i < n; i++) { + // At the beginning of each step 'i', the size of the Star([a,b]) is 'i'. + // At the end of this step, the size of the Star([a,b]) is 'i+1'. + // The sizes of the Link([a,b]) are the same. + fliptype = ((abtets[i].ver >> 4) & 3); // 0, 1, or 2. + if (fliptype == 1) { + // It was a 2-to-3 flip: [a,b,c]->[e,d]. + t = (abtets[i].ver >> 6); + assert(t <= i); + if (fc->unflip) { + if (b->verbose > 2) { + printf(" Recover a 2-to-3 flip at f[%d].\n", t); + } + // 'abtets[(t-1)%i]' is the tet [a,b,e,d] in current Star(ab), i.e., + // it is created by a 2-to-3 flip [a,b,c] => [e,d]. + fliptets[0] = abtets[((t - 1) + i) % i]; // [a,b,e,d] + eprevself(fliptets[0]); + esymself(fliptets[0]); + enextself(fliptets[0]); // [e,d,a,b] + fnext(fliptets[0], fliptets[1]); // [e,d,b,c] + fnext(fliptets[1], fliptets[2]); // [e,d,c,a] + // Do a 3-to-2 flip: [e,d] => [a,b,c]. + // NOTE: hull tets may be invloved. + flip32(fliptets, 1, fc); + // Expand the array 'abtets', maintain the original order. + // The new array length is (i+1). + for (j = i - 1; j >= t; j--) { + abtets[j + 1] = abtets[j]; // Downshift + } + // The tet abtets[(t-1)%i] is deleted. Insert the two new tets + // 'fliptets[0]' [a,b,c,d] and 'fliptets[1]' [b,a,c,e] into + // the (t-1)-th and t-th entries, respectively. + esym(fliptets[1], abtets[((t-1) + (i+1)) % (i+1)]); // [a,b,e,c] + abtets[t] = fliptets[0]; // [a,b,c,d] + if (fc->collectnewtets) { + // Pop up two (flipped) tets from the stack. + cavetetlist->objects -= 2; + } + } + } else if (fliptype == 2) { + tmpabtets = (triface *) (abtets[i].tet); + n1 = ((abtets[i].ver >> 19) & 8191); // \sum_{i=0^12}{2^i} = 8191 + edgepivot = (abtets[i].ver & 3); + t = ((abtets[i].ver >> 6) & 8191); + assert(t <= i); + if (fc->unflip) { + if (b->verbose > 2) { + printf(" Recover a %d-to-m flip at e[%d] of f[%d].\n", n1, + edgepivot, t); + } + // Recover the flipped edge ([c,b] or [a,c]). + // abtets[(t - 1 + i) % i] is [a,b,e,d], i.e., the tet created by + // the flipping of edge [c,b] or [a,c]. It must still exist in + // Star(ab). Use it to recover the flipped edge. + if (edgepivot == 1) { + // The flip edge is [c,b]. + tmpabtets[0] = abtets[(t - 1 + i) % i]; // [a,b,e,d] + eprevself(tmpabtets[0]); + esymself(tmpabtets[0]); + eprevself(tmpabtets[0]); // [d,a,e,b] + fsym(tmpabtets[0], tmpabtets[1]); // [a,d,e,c] + } else { + // The flip edge is [a,c]. + tmpabtets[1] = abtets[(t - 1 + i) % i]; // [a,b,e,d] + enextself(tmpabtets[1]); + esymself(tmpabtets[1]); + enextself(tmpabtets[1]); // [b,d,e,a] + fsym(tmpabtets[1], tmpabtets[0]); // [d,b,e,c] + } // if (edgepivot == 2) + + // Do a n1-to-m1 flip to recover the flipped edge ([c,b] or [a,c]). + flipnm_post(tmpabtets, n1, 2, edgepivot, fc); + + // Insert the two recovered tets into the original Star(ab). + for (j = i - 1; j >= t; j--) { + abtets[j + 1] = abtets[j]; // Downshift + } + if (edgepivot == 1) { + // tmpabtets[0] is [c,b,d,a] ==> contains [a,b] + // tmpabtets[1] is [c,b,a,e] ==> contains [a,b] + // tmpabtets[2] is [c,b,e,d] + fliptets[0] = tmpabtets[1]; + enextself(fliptets[0]); + esymself(fliptets[0]); // [a,b,e,c] + fliptets[1] = tmpabtets[0]; + esymself(fliptets[1]); + eprevself(fliptets[1]); // [a,b,c,d] + } else { + // tmpabtets[0] is [a,c,d,b] ==> contains [a,b] + // tmpabtets[1] is [a,c,b,e] ==> contains [a,b] + // tmpabtets[2] is [a,c,e,d] + fliptets[0] = tmpabtets[1]; + eprevself(fliptets[0]); + esymself(fliptets[0]); // [a,b,e,c] + fliptets[1] = tmpabtets[0]; + esymself(fliptets[1]); + enextself(fliptets[1]); // [a,b,c,d] + } // edgepivot == 2 + // Insert the two recovered tets into Star(ab). + abtets[((t-1) + (i+1)) % (i+1)] = fliptets[0]; + abtets[t] = fliptets[1]; + } + else { + // Only free the spaces. + flipnm_post(tmpabtets, n1, 2, edgepivot, fc); + } // if (!unflip) + if (b->verbose > 2) { + printf(" Release %d spaces at f[%d].\n", n1, i); + } + delete [] tmpabtets; } - 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); - } - - 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); - } - - // 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)); - - 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; + } // i + + return 1; } /////////////////////////////////////////////////////////////////////////////// // // -// 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. // +// insertpoint() Insert a point into current tetrahedralization. // // // -// On completion, 'flipface' returns abcd. If 'flipqueue' is not NULL, all // -// possibly non-Delaunay faces are added into it. // +// The Bowyer-Watson (B-W) algorithm is used to add a new point p into the // +// tetrahedralization T. It first finds a "cavity", denoted as C, in T, C // +// consists of tetrahedra in T that "conflict" with p. If T is a Delaunay // +// tetrahedralization, then all boundary faces (triangles) of C are visible // +// by p, i.e.,C is star-shaped. We can insert p into T by first deleting all // +// tetrahedra in C, then creating new tetrahedra formed by boundary faces of // +// C and p. If T is not a DT, then C may be not star-shaped. It must be // +// modified so that it becomes star-shaped. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::flip32(triface* flipface, queue* flipqueue) +int tetgenmesh::insertpoint(point insertpt, triface *searchtet, face *splitsh, + face *splitseg, insertvertexflags *ivf) { - 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; + arraypool *swaplist; + triface *cavetet, spintet, neightet, neineitet, *parytet; + triface oldtet, newtet, newneitet; + face checksh, neighsh, *parysh; + face checkseg, *paryseg; + point *pts, pa, pb, pc, *parypt; + enum locateresult loc = OUTSIDE; + REAL sign, ori; + REAL attrib, volume; + bool enqflag; + int t1ver; + int i, j, k, s; - 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(" Insert point %d\n", pointmark(insertpt)); } - 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)); - } - 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) { - tspivot(oldabd, abdsh); - tspivot(oldbcd, bcdsh); - tspivot(oldcad, cadsh); - tspivot(oldbae, baesh); - tspivot(oldcbe, cbesh); - tspivot(oldace, acesh); - } - if (checksubsegs) { - enext(edab, worktet); - tsspivot1(worktet, adseg); - enext2(edab, worktet); - tsspivot1(worktet, aeseg); - enext(edbc, worktet); - tsspivot1(worktet, bdseg); - enext2(edbc, worktet); - tsspivot1(worktet, beseg); - enext(edca, worktet); - tsspivot1(worktet, cdseg); - enext2(edca, worktet); - tsspivot1(worktet, ceseg); - enextfnext(edab, worktet); - enextself(worktet); - tsspivot1(worktet, abseg); - enextfnext(edbc, worktet); - enextself(worktet); - tsspivot1(worktet, bcseg); - enextfnext(edca, worktet); - enextself(worktet); - tsspivot1(worktet, caseg); - } - - // Creating the new configuration inside the convex hull. - abcd.tet = edab.tet; // Update edab to be abcd. - setorg (abcd, pa); - setdest(abcd, pb); - setapex(abcd, pc); - setoppo(abcd, pd); - bace.tet = edbc.tet; // Update edbc to be bace. - setorg (bace, pb); - setdest(bace, pa); - setapex(bace, pc); - setoppo(bace, pe); - // Dealloc a redundant tetrahedron (edca). - tetrahedrondealloc(edca.tet); - - // Clear the old bonds in abcd (was edab) and bace (was edbc). - for (i = 0; i < 4; i ++) { - abcd.tet[i] = (tetrahedron) dummytet; - } - for (i = 0; i < 4; i ++) { - bace.tet[i] = (tetrahedron) dummytet; - } - // Bond the inside face of the convex hull. - abcd.loc = 0; - bace.loc = 0; - bond(abcd, bace); - // Bond the outside faces of the convex hull. - abcd.loc = 1; - bond(abcd, abdcasing); - abcd.loc = 2; - bond(abcd, bcdcasing); - abcd.loc = 3; - bond(abcd, cadcasing); - bace.loc = 1; - bond(bace, baecasing); - bace.loc = 3; - bond(bace, cbecasing); - bace.loc = 2; - bond(bace, acecasing); - if (checksubfaces) { - // Clear old bonds in abcd(was edab) and bace(was edbc). - for (i = 0; i < 4; i ++) { - abcd.loc = i; - tsdissolve(abcd); - } - for (i = 0; i < 4; i ++) { - bace.loc = i; - tsdissolve(bace); - } - if (abdsh.sh != dummysh) { - abcd.loc = 1; - tsbond(abcd, abdsh); - } - if (bcdsh.sh != dummysh) { - abcd.loc = 2; - tsbond(abcd, bcdsh); - } - if (cadsh.sh != dummysh) { - abcd.loc = 3; - tsbond(abcd, cadsh); - } - if (baesh.sh != dummysh) { - bace.loc = 1; - tsbond(bace, baesh); - } - if (cbesh.sh != dummysh) { - bace.loc = 3; - tsbond(bace, cbesh); - } - if (acesh.sh != dummysh) { - bace.loc = 2; - tsbond(bace, acesh); - } - } - if (checksubsegs) { - for (i = 0; i < 6; i++) { - abcd.loc = edge2locver[i][0]; - abcd.ver = edge2locver[i][1]; - tssdissolve1(abcd); - } - for (i = 0; i < 6; i++) { - bace.loc = edge2locver[i][0]; - bace.ver = edge2locver[i][1]; - tssdissolve1(bace); - } - abcd.loc = abcd.ver = 0; - bace.loc = bace.ver = 0; - tssbond1(abcd, abseg); // 1 - enext(abcd, worktet); - tssbond1(worktet, bcseg); // 2 - enext2(abcd, worktet); - tssbond1(worktet, caseg); // 3 - fnext(abcd, worktet); - enext2self(worktet); - tssbond1(worktet, adseg); // 4 - enextfnext(abcd, worktet); - enext2self(worktet); - tssbond1(worktet, bdseg); // 5 - enext2fnext(abcd, worktet); - enext2self(worktet); - tssbond1(worktet, cdseg); // 6 - tssbond1(bace, abseg); - enext2(bace, worktet); - tssbond1(worktet, bcseg); - enext(bace, worktet); - tssbond1(worktet, caseg); - fnext(bace, worktet); - enextself(worktet); - tssbond1(worktet, aeseg); // 7 - enext2fnext(bace, worktet); - enextself(worktet); - tssbond1(worktet, beseg); // 8 - enextfnext(bace, worktet); - enextself(worktet); - tssbond1(worktet, ceseg); // 9 - } - - abcd.loc = 0; - bace.loc = 0; - if (b->verbose > 3) { - printf(" Updating abcd "); - printtet(&abcd); - printf(" Updating bace "); - printtet(&bace); - printf(" Deleting edca "); - // 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); - fnext(bace, baecasing); - enqueueflipface(baecasing, flipqueue); - enextfnext(abcd, bcdcasing); - enqueueflipface(bcdcasing, flipqueue); - enextfnext(bace, cbecasing); - enqueueflipface(cbecasing, flipqueue); - enext2fnext(abcd, cadcasing); - enqueueflipface(cadcasing, flipqueue); - enext2fnext(bace, acecasing); - enqueueflipface(acecasing, flipqueue); - } - - // Save a live handle in 'recenttet'. - recenttet = abcd; - // Set the return handle be abcd. - *flipface = abcd; -} + // Locate the point. + if (searchtet->tet != NULL) { + loc = (enum locateresult) ivf->iloc; + } -/////////////////////////////////////////////////////////////////////////////// -// // -// flip22() Perform a 2-to-2 (or 4-to-4) flip. // -// // -// On input, 'flipface' represents the face will be flipped. Let it is abe, // -// ab is the flipable edge, the two tetrahedra sharing abe are abce and bade,// -// hence a, b, c and d are coplanar. If abc, bad are interior faces, the two // -// tetrahedra opposite to e are bacf and abdf. ab is not a subsegment. // -// // -// A 2-to-2 flip is to change two tetrahedra abce and bade into another two // -// tetrahedra dcae and cdbe. If bacf and abdf exist, they're changed to cdaf // -// and dcbf, thus a 4-to-4 flip. As a result, two or four tetrahedra have // -// rotated counterclockwise (using right-hand rule with thumb points to e): // -// abce->dcae, bade->cdbe, and bacf->cdaf, abdf->dcbf. // -// // -// If abc and bad are subfaces, a 2-to-2 flip is performed simultaneously by // -// calling routine flip22sub(), hence abc->dca, bad->cdb. The edge rings of // -// the flipped subfaces dca and cdb have the same orientation as abc and bad.// -// Hence, they have the same orientation as other subfaces of the facet with // -// respect to the lift point of this facet. // -// // -// On completion, 'flipface' holds edge dc of tetrahedron dcae. 'flipqueue' // -// contains all possibly non-Delaunay faces if it is not NULL. // -// // -/////////////////////////////////////////////////////////////////////////////// + if (loc == OUTSIDE) { + if (searchtet->tet == NULL) { + if (!b->weighted) { + randomsample(insertpt, searchtet); + } else { + // Weighted DT. There may exist dangling vertex. + *searchtet = recenttet; + } + } + // Locate the point. + loc = locate(insertpt, searchtet); + } -void tetgenmesh::flip22(triface* flipface, queue* flipqueue) -{ - triface abce, bade; - triface oldbce, oldcae, oldade, olddbe; - triface bcecasing, caecasing, adecasing, dbecasing; - face bcesh, caesh, adesh, dbesh; - triface bacf, abdf; - triface oldacf, oldcbf, oldbdf, olddaf; - triface acfcasing, cbfcasing, bdfcasing, dafcasing; - triface worktet; - face acfsh, cbfsh, bdfsh, dafsh; - face abc, bad; - face adseg, dbseg, bcseg, caseg; // Coplanar segs. - face aeseg, deseg, beseg, ceseg; // Above segs. - face afseg, dfseg, bfseg, cfseg; // Below segs. - point pa, pb, pc, pd, pe, pf; - int mirrorflag, i; - - adjustedgering(*flipface, CCW); // 'flipface' is bae. - fnext(*flipface, abce); - esymself(abce); - adjustedgering(*flipface, CW); // 'flipface' is abe. - fnext(*flipface, bade); -#ifdef SELF_CHECK - assert(bade.tet != dummytet); -#endif - esymself(bade); - pa = org(abce); - pb = dest(abce); - pc = apex(abce); - pd = apex(bade); - pe = oppo(bade); -#ifdef SELF_CHECK - assert(oppo(abce) == pe); -#endif - sym(abce, bacf); - mirrorflag = bacf.tet != dummytet; - if (mirrorflag) { - // findedge(&bacf, pb, pa); - bacf.ver = 0; - for (i = 0; (i < 3) && (org(bacf) != pb); i++) { - enextself(bacf); - } - sym(bade, abdf); -#ifdef SELF_CHECK - assert(abdf.tet != dummytet); -#endif - // findedge(&abdf, pa, pb); - abdf.ver = 0; - for (i = 0; (i < 3) && (org(abdf) != pa); i++) { - enextself(abdf); - } - pf = oppo(bacf); -#ifdef SELF_CHECK - assert(oppo(abdf) == pf); -#endif - } + ivf->iloc = (int) loc; // The return value. - 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"); - } - mirrorflag ? flip44s++ : flip22s++; - - // Save the old configuration at the convex hull. - enextfnext(abce, oldbce); - enext2fnext(abce, oldcae); - enextfnext(bade, oldade); - enext2fnext(bade, olddbe); - sym(oldbce, bcecasing); - sym(oldcae, caecasing); - sym(oldade, adecasing); - sym(olddbe, dbecasing); - if (checksubfaces) { - tspivot(oldbce, bcesh); - tspivot(oldcae, caesh); - tspivot(oldade, adesh); - tspivot(olddbe, dbesh); - tspivot(abce, abc); - tspivot(bade, bad); - } - if (checksubsegs) { - // Coplanar segs: a->d->b->c. - enext(bade, worktet); - tsspivot1(worktet, adseg); - enext2(bade, worktet); - tsspivot1(worktet, dbseg); - enext(abce, worktet); - tsspivot1(worktet, bcseg); - enext2(abce, worktet); - tsspivot1(worktet, caseg); - // Above segs: a->e, d->e, b->e, c->e. - fnext(bade, worktet); - enextself(worktet); - tsspivot1(worktet, aeseg); - enextfnext(bade, worktet); - enextself(worktet); - tsspivot1(worktet, deseg); - enext2fnext(bade, worktet); - enextself(worktet); - tsspivot1(worktet, beseg); - enextfnext(abce, worktet); - enextself(worktet); - tsspivot1(worktet, ceseg); - } - if (mirrorflag) { - enextfnext(bacf, oldacf); - enext2fnext(bacf, oldcbf); - enextfnext(abdf, oldbdf); - enext2fnext(abdf, olddaf); - sym(oldacf, acfcasing); - sym(oldcbf, cbfcasing); - sym(oldbdf, bdfcasing); - sym(olddaf, dafcasing); - if (checksubfaces) { - tspivot(oldacf, acfsh); - tspivot(oldcbf, cbfsh); - tspivot(oldbdf, bdfsh); - tspivot(olddaf, dafsh); - } - if (checksubsegs) { - // Below segs: a->f, d->f, b->f, c->f. - fnext(abdf, worktet); - enext2self(worktet); - tsspivot1(worktet, afseg); - enext2fnext(abdf, worktet); - enext2self(worktet); - tsspivot1(worktet, dfseg); - enextfnext(abdf, worktet); - enext2self(worktet); - tsspivot1(worktet, bfseg); - enextfnext(bacf, worktet); - enextself(worktet); - tsspivot1(worktet, cfseg); - } - } - - // Rotate abce, bade one-quarter turn counterclockwise. - bond(oldbce, caecasing); - bond(oldcae, adecasing); - bond(oldade, dbecasing); - bond(olddbe, bcecasing); - if (checksubfaces) { - // Check for subfaces and rebond them to the rotated tets. - if (caesh.sh == dummysh) { - tsdissolve(oldbce); - } else { - tsbond(oldbce, caesh); - } - if (adesh.sh == dummysh) { - tsdissolve(oldcae); - } else { - tsbond(oldcae, adesh); + if (b->weighted) { + if (loc != OUTSIDE) { + // Check if this vertex is regular. + pts = (point *) searchtet->tet; + assert(pts[7] != dummypoint); + sign = orient4d_s(pts[4], pts[5], pts[6], pts[7], insertpt, + pts[4][3], pts[5][3], pts[6][3], pts[7][3], + insertpt[3]); + if (sign > 0) { + // This new vertex does not lie below the lower hull. Skip it. + setpointtype(insertpt, NREGULARVERTEX); + nonregularcount++; + ivf->iloc = (int) NONREGULAR; + return 0; + } } - if (dbesh.sh == dummysh) { - tsdissolve(oldade); - } else { - tsbond(oldade, dbesh); - } - if (bcesh.sh == dummysh) { - tsdissolve(olddbe); - } else { - tsbond(olddbe, bcesh); - } - } - if (checksubsegs) { - // 5 edges in abce are changed. - enext(abce, worktet); // fit b->c into c->a. - if (caseg.sh == dummysh) { - tssdissolve1(worktet); - } else { - tssbond1(worktet, caseg); - } - enext2(abce, worktet); // fit c->a into a->d. - if (adseg.sh == dummysh) { - tssdissolve1(worktet); - } else { - tssbond1(worktet, adseg); - } - fnext(abce, worktet); // fit b->e into c->e. - enextself(worktet); - if (ceseg.sh == dummysh) { - tssdissolve1(worktet); - } else { - tssbond1(worktet, ceseg); - } - enextfnext(abce, worktet); // fit c->e into a->e. - enextself(worktet); - if (aeseg.sh == dummysh) { - tssdissolve1(worktet); - } else { - tssbond1(worktet, aeseg); - } - enext2fnext(abce, worktet); // fit a->e into d->e. - enextself(worktet); - if (deseg.sh == dummysh) { - tssdissolve1(worktet); - } else { - tssbond1(worktet, deseg); - } - // 5 edges in bade are changed. - enext(bade, worktet); // fit a->d into d->b. - if (dbseg.sh == dummysh) { - tssdissolve1(worktet); - } else { - tssbond1(worktet, dbseg); + } + + // Create the initial cavity C(p) which contains all tetrahedra that + // intersect p. It may include 1, 2, or n tetrahedra. + // If p lies on a segment or subface, also create the initial sub-cavity + // sC(p) which contains all subfaces (and segment) which intersect p. + + if (loc == OUTSIDE) { + flip14count++; + // The current hull will be enlarged. + // Add four adjacent boundary tets into list. + for (i = 0; i < 4; i++) { + decode(searchtet->tet[i], neightet); + neightet.ver = epivot[neightet.ver]; + cavebdrylist->newindex((void **) &parytet); + *parytet = neightet; } - enext2(bade, worktet); // fit d->b into b->c. - if (bcseg.sh == dummysh) { - tssdissolve1(worktet); - } else { - tssbond1(worktet, bcseg); + infect(*searchtet); + caveoldtetlist->newindex((void **) &parytet); + *parytet = *searchtet; + } else if (loc == INTETRAHEDRON) { + flip14count++; + // Add four adjacent boundary tets into list. + for (i = 0; i < 4; i++) { + decode(searchtet->tet[i], neightet); + neightet.ver = epivot[neightet.ver]; + cavebdrylist->newindex((void **) &parytet); + *parytet = neightet; } - fnext(bade, worktet); // fit a->e into d->e. - enextself(worktet); - if (deseg.sh == dummysh) { - tssdissolve1(worktet); - } else { - tssbond1(worktet, deseg); + infect(*searchtet); + caveoldtetlist->newindex((void **) &parytet); + *parytet = *searchtet; + } else if (loc == ONFACE) { + flip26count++; + // Add six adjacent boundary tets into list. + j = (searchtet->ver & 3); // The current face number. + for (i = 1; i < 4; i++) { + decode(searchtet->tet[(j + i) % 4], neightet); + neightet.ver = epivot[neightet.ver]; + cavebdrylist->newindex((void **) &parytet); + *parytet = neightet; } - enextfnext(bade, worktet); // fit d->e into b->e. - enextself(worktet); - if (beseg.sh == dummysh) { - tssdissolve1(worktet); - } else { - tssbond1(worktet, beseg); + decode(searchtet->tet[j], spintet); + j = (spintet.ver & 3); // The current face number. + for (i = 1; i < 4; i++) { + decode(spintet.tet[(j + i) % 4], neightet); + neightet.ver = epivot[neightet.ver]; + cavebdrylist->newindex((void **) &parytet); + *parytet = neightet; } - enext2fnext(bade, worktet); // fit b->e into c->e. - enextself(worktet); - if (ceseg.sh == dummysh) { - tssdissolve1(worktet); - } else { - tssbond1(worktet, ceseg); - } - } - if (mirrorflag) { - // Rotate bacf, abdf one-quarter turn counterclockwise. - bond(oldcbf, acfcasing); - bond(oldacf, dafcasing); - bond(olddaf, bdfcasing); - bond(oldbdf, cbfcasing); - if (checksubfaces) { - // Check for subfaces and rebond them to the rotated tets. - if (acfsh.sh == dummysh) { - tsdissolve(oldcbf); - } else { - tsbond(oldcbf, acfsh); - } - if (dafsh.sh == dummysh) { - tsdissolve(oldacf); - } else { - tsbond(oldacf, dafsh); - } - if (bdfsh.sh == dummysh) { - tsdissolve(olddaf); - } else { - tsbond(olddaf, bdfsh); - } - if (cbfsh.sh == dummysh) { - tsdissolve(oldbdf); - } else { - tsbond(oldbdf, cbfsh); - } - } - if (checksubsegs) { - // 5 edges in bacf are changed. - enext2(bacf, worktet); // fit b->c into c->a. - if (caseg.sh == dummysh) { - tssdissolve1(worktet); - } else { - tssbond1(worktet, caseg); - } - enext(bacf, worktet); // fit c->a into a->d. - if (adseg.sh == dummysh) { - tssdissolve1(worktet); - } else { - tssbond1(worktet, adseg); - } - fnext(bacf, worktet); // fit b->f into c->f. - enext2self(worktet); - if (cfseg.sh == dummysh) { - tssdissolve1(worktet); - } else { - tssbond1(worktet, cfseg); - } - enext2fnext(bacf, worktet); // fit c->f into a->f. - enext2self(worktet); - if (afseg.sh == dummysh) { - tssdissolve1(worktet); - } else { - tssbond1(worktet, afseg); - } - enextfnext(bacf, worktet); // fit a->f into d->f. - enext2self(worktet); - if (dfseg.sh == dummysh) { - tssdissolve1(worktet); - } else { - tssbond1(worktet, dfseg); - } - // 5 edges in abdf are changed. - enext2(abdf, worktet); // fit a->d into d->b. - if (dbseg.sh == dummysh) { - tssdissolve1(worktet); - } else { - tssbond1(worktet, dbseg); - } - enext(abdf, worktet); // fit d->b into b->c. - if (bcseg.sh == dummysh) { - tssdissolve1(worktet); - } else { - tssbond1(worktet, bcseg); - } - fnext(abdf, worktet); // fit a->f into d->f. - enext2self(worktet); - if (dfseg.sh == dummysh) { - tssdissolve1(worktet); - } else { - tssbond1(worktet, dfseg); + infect(spintet); + caveoldtetlist->newindex((void **) &parytet); + *parytet = spintet; + infect(*searchtet); + caveoldtetlist->newindex((void **) &parytet); + *parytet = *searchtet; + + if (ivf->splitbdflag) { + if ((splitsh != NULL) && (splitsh->sh != NULL)) { + // Create the initial sub-cavity sC(p). + smarktest(*splitsh); + caveshlist->newindex((void **) &parysh); + *parysh = *splitsh; } - enext2fnext(abdf, worktet); // fit d->f into b->f. - enext2self(worktet); - if (bfseg.sh == dummysh) { - tssdissolve1(worktet); - } else { - tssbond1(worktet, bfseg); + } // if (splitbdflag) + } else if (loc == ONEDGE) { + flipn2ncount++; + // Add all adjacent boundary tets into list. + spintet = *searchtet; + while (1) { + eorgoppo(spintet, neightet); + decode(neightet.tet[neightet.ver & 3], neightet); + neightet.ver = epivot[neightet.ver]; + cavebdrylist->newindex((void **) &parytet); + *parytet = neightet; + edestoppo(spintet, neightet); + decode(neightet.tet[neightet.ver & 3], neightet); + neightet.ver = epivot[neightet.ver]; + cavebdrylist->newindex((void **) &parytet); + *parytet = neightet; + infect(spintet); + caveoldtetlist->newindex((void **) &parytet); + *parytet = spintet; + fnextself(spintet); + if (spintet.tet == searchtet->tet) break; + } // while (1) + + if (ivf->splitbdflag) { + // Create the initial sub-cavity sC(p). + if ((splitseg != NULL) && (splitseg->sh != NULL)) { + smarktest(*splitseg); + splitseg->shver = 0; + spivot(*splitseg, *splitsh); + } + if (splitsh != NULL) { + if (splitsh->sh != NULL) { + // Collect all subfaces share at this edge. + pa = sorg(*splitsh); + neighsh = *splitsh; + while (1) { + // Adjust the origin of its edge to be 'pa'. + if (sorg(neighsh) != pa) { + sesymself(neighsh); + } + // Add this face into list (in B-W cavity). + smarktest(neighsh); + caveshlist->newindex((void **) &parysh); + *parysh = neighsh; + // Add this face into face-at-splitedge list. + cavesegshlist->newindex((void **) &parysh); + *parysh = neighsh; + // Go to the next face at the edge. + spivotself(neighsh); + // Stop if all faces at the edge have been visited. + if (neighsh.sh == splitsh->sh) break; + if (neighsh.sh == NULL) break; + } // while (1) + } // if (not a dangling segment) + } + } // if (splitbdflag) + } else if (loc == INSTAR) { + // We assume that all tets in the star are given in 'caveoldtetlist', + // and they are all infected. + assert(caveoldtetlist->objects > 0); + // Collect the boundary faces of the star. + for (i = 0; i < caveoldtetlist->objects; i++) { + cavetet = (triface *) fastlookup(caveoldtetlist, i); + // Check its 4 neighbor tets. + for (j = 0; j < 4; j++) { + decode(cavetet->tet[j], neightet); + if (!infected(neightet)) { + // It's a boundary face. + neightet.ver = epivot[neightet.ver]; + cavebdrylist->newindex((void **) &parytet); + *parytet = neightet; + } } - enextfnext(abdf, worktet); // fit b->f into c->f. - enext2self(worktet); - if (cfseg.sh == dummysh) { - tssdissolve1(worktet); - } else { - tssbond1(worktet, cfseg); - } - } - } - - // New vertex assignments for the rotated tetrahedra. - setorg(abce, pd); // Update abce to dcae - setdest(abce, pc); - setapex(abce, pa); - setorg(bade, pc); // Update bade to cdbe - setdest(bade, pd); - setapex(bade, pb); - if (mirrorflag) { - setorg(bacf, pc); // Update bacf to cdaf - setdest(bacf, pd); - setapex(bacf, pa); - setorg(abdf, pd); // Update abdf to dcbf - setdest(abdf, pc); - 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 - assert(bad.sh != dummysh); -#endif - // Adjust the edge be ab, so the rotation of subfaces is according with - // the rotation of tetrahedra. - findedge(&abc, pa, pb); - // Flip an edge of two subfaces, ignore non-Delaunay edges. - flip22sub(&abc, NULL); - } - - if (b->verbose > 3) { - printf(" Updating abce "); - printtet(&abce); - printf(" Updating bade "); - printtet(&bade); - if (mirrorflag) { - printf(" Updating bacf "); - printtet(&bacf); - printf(" Updating abdf "); - printtet(&abdf); - } - } - - if (flipqueue != (queue *) NULL) { - enextfnext(abce, bcecasing); - enqueueflipface(bcecasing, flipqueue); - enext2fnext(abce, caecasing); - enqueueflipface(caecasing, flipqueue); - enextfnext(bade, adecasing); - enqueueflipface(adecasing, flipqueue); - enext2fnext(bade, dbecasing); - enqueueflipface(dbecasing, flipqueue); - if (mirrorflag) { - enextfnext(bacf, acfcasing); - enqueueflipface(acfcasing, flipqueue); - enext2fnext(bacf, cbfcasing); - enqueueflipface(cbfcasing, flipqueue); - enextfnext(abdf, bdfcasing); - enqueueflipface(bdfcasing, flipqueue); - enext2fnext(abdf, dafcasing); - enqueueflipface(dafcasing, flipqueue); - } - // The two new faces dcae (abce), cdbe (bade) may still not be locally - // Delaunay, and may need be flipped (flip23). On the other hand, in - // conforming Delaunay algorithm, two new subfaces dca (abc), and cdb - // (bad) may be non-conforming Delaunay, they need be queued if they - // are locally Delaunay but non-conforming Delaunay. - enqueueflipface(abce, flipqueue); - enqueueflipface(bade, flipqueue); - } - - // Save a live handle in 'recenttet'. - recenttet = abce; -} + } + } else if (loc == ONVERTEX) { + // The point already exist. Do nothing and return. + return 0; + } -/////////////////////////////////////////////////////////////////////////////// -// // -// flip22sub() Perform a 2-to-2 flip on a subface edge. // -// // -// The flip edge is given by subface 'flipedge'. Let it is abc, where ab is // -// the flipping edge. The other subface is bad, where a, b, c, d form a // -// convex quadrilateral. ab is not a subsegment. // -// // -// A 2-to-2 subface flip is to change two subfaces abc and bad to another // -// two subfaces dca and cdb. Hence, edge ab has been removed and dc becomes // -// an edge. If a point e is above abc, this flip is equal to rotate abc and // -// bad counterclockwise using right-hand rule with thumb points to e. It is // -// important to know that the edge rings of the flipped subfaces dca and cdb // -// are keeping the same orientation as their original subfaces. So they have // -// the same orientation with respect to the lift point of this facet. // -// // -// During rotating, the face rings of the four edges bc, ca, ad, and de need // -// be re-connected. If the edge is not a subsegment, then its face ring has // -// only two faces, a sbond() will bond them together. If it is a subsegment, // -// one should use sbond1() twice to bond two different handles to the rotat- // -// ing subface, one is predecssor (-casin), another is successor (-casout). // -// // -// If 'flipqueue' is not NULL, it returns four edges bc, ca, ad, de, which // -// may be non-Delaunay. // -// // -/////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::flip22sub(face* flipedge, queue* flipqueue) -{ - face abc, bad; - face oldbc, oldca, oldad, olddb; - face bccasin, bccasout, cacasin, cacasout; - face adcasin, adcasout, dbcasin, dbcasout; - face bc, ca, ad, db; - face spinsh; - point pa, pb, pc, pd; + if (ivf->assignmeshsize) { + // Assign mesh size for the new point. + if (bgm != NULL) { + // Interpolate the mesh size from the background mesh. + bgm->decode(point2bgmtet(org(*searchtet)), neightet); + int bgmloc = (int) bgm->scoutpoint(insertpt, &neightet, 0); + if (bgmloc != (int) OUTSIDE) { + insertpt[pointmtrindex] = + bgm->getpointmeshsize(insertpt, &neightet, bgmloc); + setpoint2bgmtet(insertpt, bgm->encode(neightet)); + } + } else { + insertpt[pointmtrindex] = getpointmeshsize(insertpt,searchtet,(int)loc); + } + } // if (assignmeshsize) + + if (ivf->bowywat) { + // Update the cavity C(p) using the Bowyer-Watson algorithm. + swaplist = cavetetlist; + cavetetlist = cavebdrylist; + cavebdrylist = swaplist; + for (i = 0; i < cavetetlist->objects; i++) { + // 'cavetet' is an adjacent tet at outside of the cavity. + cavetet = (triface *) fastlookup(cavetetlist, i); + // The tet may be tested and included in the (enlarged) cavity. + if (!infected(*cavetet)) { + // Check for two possible cases for this tet: + // (1) It is a cavity tet, or + // (2) it is a cavity boundary face. + enqflag = false; + if (!marktested(*cavetet)) { + // Do Delaunay (in-sphere) test. + pts = (point *) cavetet->tet; + if (pts[7] != dummypoint) { + // A volume tet. Operate on it. + if (b->weighted) { + sign = orient4d_s(pts[4], pts[5], pts[6], pts[7], insertpt, + pts[4][3], pts[5][3], pts[6][3], pts[7][3], + insertpt[3]); + } else { + sign = insphere_s(pts[4], pts[5], pts[6], pts[7], insertpt); + } + enqflag = (sign < 0.0); + } else { + if (!nonconvex) { + // Test if this hull face is visible by the new point. + ori = orient3d(pts[4], pts[5], pts[6], insertpt); + if (ori < 0) { + // A visible hull face. + //if (!nonconvex) { + // Include it in the cavity. The convex hull will be enlarged. + enqflag = true; // (ori < 0.0); + //} + } else if (ori == 0.0) { + // A coplanar hull face. We need to test if this hull face is + // Delaunay or not. We test if the adjacent tet (not faked) + // of this hull face is Delaunay or not. + decode(cavetet->tet[3], neineitet); + if (!infected(neineitet)) { + if (!marktested(neineitet)) { + // Do Delaunay test on this tet. + pts = (point *) neineitet.tet; + assert(pts[7] != dummypoint); + if (b->weighted) { + sign = orient4d_s(pts[4],pts[5],pts[6],pts[7], insertpt, + pts[4][3], pts[5][3], pts[6][3], + pts[7][3], insertpt[3]); + } else { + sign = insphere_s(pts[4],pts[5],pts[6],pts[7], insertpt); + } + enqflag = (sign < 0.0); + } + } else { + // The adjacent tet is non-Delaunay. The hull face is non- + // Delaunay as well. Include it in the cavity. + enqflag = true; + } // if (!infected(neineitet)) + } // if (ori == 0.0) + } else { + // A hull face (must be a subface). + // We FIRST include it in the initial cavity if the adjacent tet + // (not faked) of this hull face is not Delaunay wrt p. + // Whether it belongs to the final cavity will be determined + // during the validation process. 'validflag'. + decode(cavetet->tet[3], neineitet); + if (!infected(neineitet)) { + if (!marktested(neineitet)) { + // Do Delaunay test on this tet. + pts = (point *) neineitet.tet; + assert(pts[7] != dummypoint); + if (b->weighted) { + sign = orient4d_s(pts[4],pts[5],pts[6],pts[7], insertpt, + pts[4][3], pts[5][3], pts[6][3], + pts[7][3], insertpt[3]); + } else { + sign = insphere_s(pts[4],pts[5],pts[6],pts[7], insertpt); + } + enqflag = (sign < 0.0); + } + } else { + // The adjacent tet is non-Delaunay. The hull face is non- + // Delaunay as well. Include it in the cavity. + enqflag = true; + } // if (infected(neineitet)) + } // if (nonconvex) + } // if (pts[7] != dummypoint) + marktest(*cavetet); // Only test it once. + } // if (!marktested(*cavetet)) + + if (enqflag) { + // Found a tet in the cavity. Put other three faces in check list. + k = (cavetet->ver & 3); // The current face number + for (j = 1; j < 4; j++) { + decode(cavetet->tet[(j + k) % 4], neightet); + cavetetlist->newindex((void **) &parytet); + *parytet = neightet; + } + infect(*cavetet); + caveoldtetlist->newindex((void **) &parytet); + *parytet = *cavetet; + } else { + // Found a boundary face of the cavity. + cavetet->ver = epivot[cavetet->ver]; + cavebdrylist->newindex((void **) &parytet); + *parytet = *cavetet; + } + } // if (!infected(*cavetet)) + } // i - abc = *flipedge; - spivot(abc, bad); - if (sorg(bad) != sdest(abc)) { - sesymself(bad); - } - pa = sorg(abc); - pb = sdest(abc); - pc = sapex(abc); - pd = sapex(bad); + cavetetlist->restart(); // Clear the working list. + } // if (ivf->bowywat) - if (b->verbose > 1) { - printf(" Flip subedge (%d, %d) to (%d, %d).\n", pointmark(pa), - pointmark(pb), pointmark(pc), pointmark(pd)); - } - - // 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); - senext(bad, oldad); - senext2(bad, olddb); - // Get the outside connection. Becareful if there is a subsegment on the - // quadrilateral, two casings (casin and casout) are needed to save for - // keeping the face link. - spivot(oldbc, bccasout); - sspivot(oldbc, bc); - if (bc.sh != dummysh) { - // 'bc' is a subsegment. - 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) { - // 'ca' is a subsegment. - 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); - } - spivot(oldad, adcasout); - sspivot(oldad, ad); - if (ad.sh != dummysh) { - // 'ad' is a subsegment. - if (adcasout.sh != dummysh) { - if (oldad.sh != adcasout.sh) { - // 'adcasout' is not self-bonded. - spinsh = adcasout; - do { - adcasin = spinsh; - spivotself(spinsh); - } while (spinsh.sh != oldad.sh); - } else { - adcasout.sh = dummysh; + if (checksubsegflag) { + // Collect all segments of C(p). + shellface *ssptr; + for (i = 0; i < caveoldtetlist->objects; i++) { + cavetet = (triface *) fastlookup(caveoldtetlist, i); + if ((ssptr = (shellface*) cavetet->tet[8]) != NULL) { + for (j = 0; j < 6; j++) { + if (ssptr[j]) { + sdecode(ssptr[j], checkseg); + if (!sinfected(checkseg)) { + sinfect(checkseg); + cavetetseglist->newindex((void **) &paryseg); + *paryseg = checkseg; + } + } + } // j } - } - ssdissolve(oldad); - } - spivot(olddb, dbcasout); - sspivot(olddb, db); - if (db.sh != dummysh) { - // 'db' is a subsegment. - if (dbcasout.sh != dummysh) { - if (olddb.sh != dbcasout.sh) { - // 'dbcasout' is not self-bonded. - spinsh = dbcasout; - do { - dbcasin = spinsh; - spivotself(spinsh); - } while (spinsh.sh != olddb.sh); - } else { - dbcasout.sh = dummysh; + } // i + // Uninfect collected segments. + for (i = 0; i < cavetetseglist->objects; i++) { + paryseg = (face *) fastlookup(cavetetseglist, i); + suninfect(*paryseg); + } + + if (ivf->rejflag & 1) { + // Reject this point if it encroaches upon any segment. + face *paryseg1; + for (i = 0; i < cavetetseglist->objects; i++) { + paryseg1 = (face *) fastlookup(cavetetseglist, i); + if (checkseg4encroach((point) paryseg1->sh[3], (point) paryseg1->sh[4], + insertpt)) { + encseglist->newindex((void **) &paryseg); + *paryseg = *paryseg1; + } + } // i + if (encseglist->objects > 0) { + insertpoint_abort(splitseg, ivf); + ivf->iloc = (int) ENCSEGMENT; + return 0; } } - ssdissolve(olddb); - } - - // Rotate abc and bad one-quarter turn counterclockwise. - if (ca.sh != dummysh) { - if (cacasout.sh != dummysh) { - sbond1(cacasin, oldbc); - sbond1(oldbc, cacasout); - } else { - // Bond 'oldbc' to itself. - sdissolve(oldbc); // sbond(oldbc, oldbc); - // Make sure that dummysh always correctly bonded. - dummysh[0] = sencode(oldbc); - } - ssbond(oldbc, ca); - } else { - sbond(oldbc, cacasout); - } - if (ad.sh != dummysh) { - if (adcasout.sh != dummysh) { - sbond1(adcasin, oldca); - sbond1(oldca, adcasout); - } else { - // Bond 'oldca' to itself. - sdissolve(oldca); // sbond(oldca, oldca); - // Make sure that dummysh always correctly bonded. - dummysh[0] = sencode(oldca); - } - ssbond(oldca, ad); - } else { - sbond(oldca, adcasout); - } - if (db.sh != dummysh) { - if (dbcasout.sh != dummysh) { - sbond1(dbcasin, oldad); - sbond1(oldad, dbcasout); - } else { - // Bond 'oldad' to itself. - sdissolve(oldad); // sbond(oldad, oldad); - // Make sure that dummysh always correctly bonded. - dummysh[0] = sencode(oldad); - } - ssbond(oldad, db); - } else { - sbond(oldad, dbcasout); - } - if (bc.sh != dummysh) { - if (bccasout.sh != dummysh) { - sbond1(bccasin, olddb); - sbond1(olddb, bccasout); - } else { - // Bond 'olddb' to itself. - sdissolve(olddb); // sbond(olddb, olddb); - // Make sure that dummysh always correctly bonded. - dummysh[0] = sencode(olddb); - } - ssbond(olddb, bc); - } else { - sbond(olddb, bccasout); - } - - // New vertex assignments for the rotated subfaces. - setsorg(abc, pd); // Update abc to dca. - setsdest(abc, pc); - setsapex(abc, pa); - setsorg(bad, pc); // Update bad to cdb. - 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); - } -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// lawson3d() Perform 3D Lawson flips on non-Delaunay faces/edges. // -// // -/////////////////////////////////////////////////////////////////////////////// - -long tetgenmesh::lawson3d(queue* flipqueue) -{ - badface *qface; - triface flipface, symface, flipedge; - triface neighface, symneighface; - face checksh, checkseg; - face neighsh, symneighsh; - point pa, pb, pc, pd, pe; - point end1, end2; - REAL sign, ori1, ori2, ori3; - REAL ori4, len, vol; - long flipcount; - int copflag; - int i; + } // if (checksubsegflag) - if (b->verbose > 1) { - printf(" Lawson flip: %ld faces.\n", flipqueue->len()); + if (checksubfaceflag) { + // Collect all subfaces of C(p). + shellface *sptr; + for (i = 0; i < caveoldtetlist->objects; i++) { + cavetet = (triface *) fastlookup(caveoldtetlist, i); + if ((sptr = (shellface*) cavetet->tet[9]) != NULL) { + for (j = 0; j < 4; j++) { + if (sptr[j]) { + sdecode(sptr[j], checksh); + if (!sinfected(checksh)) { + sinfect(checksh); + cavetetshlist->newindex((void **) &parysh); + *parysh = checksh; + } + } + } // j + } + } // i + // Uninfect collected subfaces. + for (i = 0; i < cavetetshlist->objects; i++) { + parysh = (face *) fastlookup(cavetetshlist, i); + suninfect(*parysh); + } + + if (ivf->rejflag & 2) { + REAL rd, cent[3]; + badface *bface; + // Reject this point if it encroaches upon any subface. + for (i = 0; i < cavetetshlist->objects; i++) { + parysh = (face *) fastlookup(cavetetshlist, i); + if (checkfac4encroach((point) parysh->sh[3], (point) parysh->sh[4], + (point) parysh->sh[5], insertpt, cent, &rd)) { + encshlist->newindex((void **) &bface); + bface->ss = *parysh; + bface->forg = (point) parysh->sh[3]; // Not a dad one. + for (j = 0; j < 3; j++) bface->cent[j] = cent[j]; + bface->key = rd; + } + } + if (encshlist->objects > 0) { + insertpoint_abort(splitseg, ivf); + ivf->iloc = (int) ENCSUBFACE; + return 0; + } + } + } // if (checksubfaceflag) + + if ((ivf->iloc == (int) OUTSIDE) && ivf->refineflag) { + // The vertex lies outside of the domain. And it does not encroach + // upon any boundary segment or subface. Do not insert it. + insertpoint_abort(splitseg, ivf); + return 0; } - flipcount = flip23s + flip32s + flip22s + flip44s; - // Loop until the queue is empty. - 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; - } + if (ivf->splitbdflag) { + // The new point locates in surface mesh. Update the sC(p). + // We have already 'smarktested' the subfaces which directly intersect + // with p in 'caveshlist'. From them, we 'smarktest' their neighboring + // subfaces which are included in C(p). Do not across a segment. + for (i = 0; i < caveshlist->objects; i++) { + parysh = (face *) fastlookup(caveshlist, i); + assert(smarktested(*parysh)); + checksh = *parysh; + for (j = 0; j < 3; j++) { + if (!isshsubseg(checksh)) { + spivot(checksh, neighsh); + assert(neighsh.sh != NULL); + if (!smarktested(neighsh)) { + stpivot(neighsh, neightet); + if (infected(neightet)) { + fsymself(neightet); + if (infected(neightet)) { + // This subface is inside C(p). + // Check if its diametrical circumsphere encloses 'p'. + // The purpose of this check is to avoid forming invalid + // subcavity in surface mesh. + sign = incircle3d(sorg(neighsh), sdest(neighsh), + sapex(neighsh), insertpt); + if (sign < 0) { + smarktest(neighsh); + caveshlist->newindex((void **) &parysh); + *parysh = neighsh; } - // } - } 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. - } + } + } + senextself(checksh); + } // j + } // i + } // if (ivf->splitbdflag) + + if (ivf->validflag) { + // Validate C(p) and update it if it is not star-shaped. + int cutcount = 0; + + if (ivf->respectbdflag) { + // The initial cavity may include subfaces which are not on the facets + // being splitting. Find them and make them as boundary of C(p). + // Comment: We have already 'smarktested' the subfaces in sC(p). They + // are completely inside C(p). + for (i = 0; i < cavetetshlist->objects; i++) { + parysh = (face *) fastlookup(cavetetshlist, i); + stpivot(*parysh, neightet); + if (infected(neightet)) { + fsymself(neightet); + if (infected(neightet)) { + // Found a subface inside C(p). + if (!smarktested(*parysh)) { + // It is possible that this face is a boundary subface. + // Check if it is a hull face. + //assert(apex(neightet) != dummypoint); + if (oppo(neightet) != dummypoint) { + fsymself(neightet); } - } 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. + if (oppo(neightet) != dummypoint) { + ori = orient3d(org(neightet), dest(neightet), apex(neightet), + insertpt); + if (ori < 0) { + // A visible face, get its neighbor face. + fsymself(neightet); + ori = -ori; // It must be invisible by p. } - } - } - } - } 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. - } + // A hull tet. It needs to be cut. + ori = 1; } - } 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); - } + // Cut this tet if it is either invisible by or coplanar with p. + if (ori >= 0) { + uninfect(neightet); + unmarktest(neightet); + cutcount++; + neightet.ver = epivot[neightet.ver]; + cavebdrylist->newindex((void **) &parytet); + *parytet = neightet; + // Add three new faces to find new boundaries. + for (j = 0; j < 3; j++) { + esym(neightet, neineitet); + neineitet.ver = epivot[neineitet.ver]; + cavebdrylist->newindex((void **) &parytet); + *parytet = neineitet; + enextself(neightet); } - } else { // ori2 == 0 - if (ori3 > 0) { // (-0+) - assert(0); - } else { - if (ori3 < 0) { // (-0-) - assert(0); - } else { // (-00) - assert(0); + } // if (ori >= 0) + } + } + } + } // i + + // The initial cavity may include segments in its interior. We need to + // Update the cavity so that these segments are on the boundary of + // the cavity. + for (i = 0; i < cavetetseglist->objects; i++) { + paryseg = (face *) fastlookup(cavetetseglist, i); + // Check this segment if it is not a splitting segment. + if (!smarktested(*paryseg)) { + sstpivot1(*paryseg, neightet); + spintet = neightet; + while (1) { + if (!infected(spintet)) break; + fnextself(spintet); + if (spintet.tet == neightet.tet) break; + } + if (infected(spintet)) { + // Find an adjacent tet at this segment such that both faces + // at this segment are not visible by p. + pa = org(neightet); + pb = dest(neightet); + spintet = neightet; + j = 0; + while (1) { + // Check if this face is visible by p. + pc = apex(spintet); + if (pc != dummypoint) { + ori = orient3d(pa, pb, pc, insertpt); + if (ori >= 0) { + // Not visible. Check another face in this tet. + esym(spintet, neineitet); + pc = apex(neineitet); + if (pc != dummypoint) { + ori = orient3d(pb, pa, pc, insertpt); + if (ori >= 0) { + // Not visible. Found this face. + j = 1; // Flag that it is found. + break; + } } } } + fnextself(spintet); + if (spintet.tet == neightet.tet) break; } - } 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); - } - } - } + if (j == 0) { + // Not found such a face. + assert(0); // debug this case. + } + neightet = spintet; + if (b->verbose > 3) { + printf(" Cut tet (%d, %d, %d, %d)\n", + pointmark(org(neightet)), pointmark(dest(neightet)), + pointmark(apex(neightet)), pointmark(oppo(neightet))); + } + uninfect(neightet); + unmarktest(neightet); + cutcount++; + neightet.ver = epivot[neightet.ver]; + cavebdrylist->newindex((void **) &parytet); + *parytet = neightet; + // Add three new faces to find new boundaries. + for (j = 0; j < 3; j++) { + esym(neightet, neineitet); + neineitet.ver = epivot[neineitet.ver]; + cavebdrylist->newindex((void **) &parytet); + *parytet = neineitet; + enextself(neightet); } } } + } // i + } // if (ivf->respectbdflag) - // 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); - } - } 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 { - // 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); - } - } + // Update the cavity by removing invisible faces until it is star-shaped. + for (i = 0; i < cavebdrylist->objects; i++) { + cavetet = (triface *) fastlookup(cavebdrylist, i); + // 'cavetet' is an exterior tet adjacent to the cavity. + // Check if its neighbor is inside C(p). + fsym(*cavetet, neightet); + if (infected(neightet)) { + if (apex(*cavetet) != dummypoint) { + // It is a cavity boundary face. Check its visibility. + if (oppo(neightet) != dummypoint) { + ori = orient3d(org(*cavetet), dest(*cavetet), apex(*cavetet), + insertpt); + enqflag = (ori > 0); + // Comment: if ori == 0 (coplanar case), we also cut the tet. } 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); - } - } - } else { - if (symneighface.tet == dummytet) { - // Found a 2-to-2 flip. - flip22(&flipedge, flipqueue); + // It is a hull face. And its adjacent tet (at inside of the + // domain) has been cut from the cavity. Cut it as well. + //assert(nonconvex); + enqflag = false; + } + } else { + enqflag = true; // A hull edge. + } + if (enqflag) { + // This face is valid, save it. + cavetetlist->newindex((void **) &parytet); + *parytet = *cavetet; + } else { + uninfect(neightet); + unmarktest(neightet); + cutcount++; + // Add three new faces to find new boundaries. + for (j = 0; j < 3; j++) { + esym(neightet, neineitet); + neineitet.ver = epivot[neineitet.ver]; + cavebdrylist->newindex((void **) &parytet); + *parytet = neineitet; + enextself(neightet); + } + // 'cavetet' is not on the cavity boundary anymore. + unmarktest(*cavetet); + } + } else { + // 'cavetet' is not on the cavity boundary anymore. + unmarktest(*cavetet); + } + } // i + + if (cutcount > 0) { + // The cavity has been updated. + // Update the cavity boundary faces. + cavebdrylist->restart(); + for (i = 0; i < cavetetlist->objects; i++) { + cavetet = (triface *) fastlookup(cavetetlist, i); + // 'cavetet' was an exterior tet adjacent to the cavity. + fsym(*cavetet, neightet); + if (infected(neightet)) { + // It is a cavity boundary face. + cavebdrylist->newindex((void **) &parytet); + *parytet = *cavetet; + } else { + // Not a cavity boundary face. + unmarktest(*cavetet); + } + } + + // Update the list of old tets. + cavetetlist->restart(); + for (i = 0; i < caveoldtetlist->objects; i++) { + cavetet = (triface *) fastlookup(caveoldtetlist, i); + if (infected(*cavetet)) { + cavetetlist->newindex((void **) &parytet); + *parytet = *cavetet; + } + } + // Swap 'cavetetlist' and 'caveoldtetlist'. + swaplist = caveoldtetlist; + caveoldtetlist = cavetetlist; + cavetetlist = swaplist; + + // The cavity should contain at least one tet. + if (caveoldtetlist->objects == 0l) { + insertpoint_abort(splitseg, ivf); + ivf->iloc = (int) BADELEMENT; + return 0; + } + + if (ivf->splitbdflag) { + int cutshcount = 0; + // Update the sub-cavity sC(p). + for (i = 0; i < caveshlist->objects; i++) { + parysh = (face *) fastlookup(caveshlist, i); + if (smarktested(*parysh)) { + enqflag = false; + stpivot(*parysh, neightet); + if (infected(neightet)) { + fsymself(neightet); + if (infected(neightet)) { + enqflag = true; } } + if (!enqflag) { + sunmarktest(*parysh); + // Use the last entry of this array to fill this entry. + j = caveshlist->objects - 1; + checksh = * (face *) fastlookup(caveshlist, j); + *parysh = checksh; + cutshcount++; + caveshlist->objects--; // The list is shrinked. + i--; + } } } - } // if (sign > 0) - } - } // while (!flipqueue->empty()) + if (cutshcount > 0) { + i = 0; // Count the number of invalid subfaces/segments. + // Valid the updated sub-cavity sC(p). + if (loc == ONFACE) { + if ((splitsh != NULL) && (splitsh->sh != NULL)) { + // The to-be split subface should be in sC(p). + if (!smarktested(*splitsh)) i++; + } + } else if (loc == ONEDGE) { + if ((splitseg != NULL) && (splitseg->sh != NULL)) { + // The to-be split segment should be in sC(p). + if (!smarktested(*splitseg)) i++; + } + if ((splitsh != NULL) && (splitsh->sh != NULL)) { + // All subfaces at this edge should be in sC(p). + pa = sorg(*splitsh); + neighsh = *splitsh; + while (1) { + // Adjust the origin of its edge to be 'pa'. + if (sorg(neighsh) != pa) { + sesymself(neighsh); + } + // Add this face into list (in B-W cavity). + if (!smarktested(neighsh)) i++; + // Go to the next face at the edge. + spivotself(neighsh); + // Stop if all faces at the edge have been visited. + if (neighsh.sh == splitsh->sh) break; + if (neighsh.sh == NULL) break; + } // while (1) + } + } - flipcount = flip23s + flip32s + flip22s + flip44s - flipcount; - if (b->verbose > 1) { - printf(" %ld flips.\n", flipcount); - } + if (i > 0) { + // The updated sC(p) is invalid. Do not insert this vertex. + insertpoint_abort(splitseg, ivf); + ivf->iloc = (int) BADELEMENT; + return 0; + } + } // if (cutshcount > 0) + } // if (ivf->splitbdflag) + } // if (cutcount > 0) - return flipcount; -} + } // if (ivf->validflag) -/////////////////////////////////////////////////////////////////////////////// -// // -// lawson() Perform lawson flips on non-Delaunay edges. // -// // -// Assumpation: Current triangulation T contains non-Delaunay edges (after // -// inserting a point or performing a flip). Non-Delaunay edges are queued in // -// 'facequeue'. Returns the total number of flips done during this call. // -// // -/////////////////////////////////////////////////////////////////////////////// + if (ivf->refineflag) { + // The new point is inserted by Delaunay refinement, i.e., it is the + // circumcenter of a tetrahedron, or a subface, or a segment. + // Do not insert this point if the tetrahedron, or subface, or segment + // is not inside the final cavity. + if (((ivf->refineflag == 1) && !infected(ivf->refinetet)) || + ((ivf->refineflag == 2) && !smarktested(ivf->refinesh))) { + insertpoint_abort(splitseg, ivf); + ivf->iloc = (int) BADELEMENT; + return 0; + } + } // if (ivf->refineflag) -long tetgenmesh::lawson(queue* flipqueue) -{ - badface *qedge; - face flipedge, symedge; - face checkseg; - point pa, pb, pc, pd; - REAL vab[3], vac[3], vad[3]; - REAL dot1, dot2, lac, lad; - REAL sign, ori; - int edgeflips, maxflips; - int i; + if (b->plc && (loc != INSTAR)) { + // Reject the new point if it lies too close to an existing point (b->plc), + // or it lies inside a protecting ball of near vertex (ivf->rejflag & 4). + // Collect the list of vertices of the initial cavity. + if (loc == OUTSIDE) { + pts = (point *) &(searchtet->tet[4]); + for (i = 0; i < 3; i++) { + cavetetvertlist->newindex((void **) &parypt); + *parypt = pts[i]; + } + } else if (loc == INTETRAHEDRON) { + pts = (point *) &(searchtet->tet[4]); + for (i = 0; i < 4; i++) { + cavetetvertlist->newindex((void **) &parypt); + *parypt = pts[i]; + } + } else if (loc == ONFACE) { + pts = (point *) &(searchtet->tet[4]); + for (i = 0; i < 3; i++) { + cavetetvertlist->newindex((void **) &parypt); + *parypt = pts[i]; + } + if (pts[3] != dummypoint) { + cavetetvertlist->newindex((void **) &parypt); + *parypt = pts[3]; + } + fsym(*searchtet, spintet); + if (oppo(spintet) != dummypoint) { + cavetetvertlist->newindex((void **) &parypt); + *parypt = oppo(spintet); + } + } else if (loc == ONEDGE) { + spintet = *searchtet; + cavetetvertlist->newindex((void **) &parypt); + *parypt = org(spintet); + cavetetvertlist->newindex((void **) &parypt); + *parypt = dest(spintet); + while (1) { + if (apex(spintet) != dummypoint) { + cavetetvertlist->newindex((void **) &parypt); + *parypt = apex(spintet); + } + fnextself(spintet); + if (spintet.tet == searchtet->tet) break; + } + } - if (b->verbose > 1) { - printf(" Lawson flip: %ld edges.\n", flipqueue->len()); - } + int rejptflag = (ivf->rejflag & 4); + REAL rd; + pts = NULL; - if (b->diagnose) { - maxflips = (int) ((flipqueue->len() + 1l) * 3l); - maxflips *= maxflips; - } else { - maxflips = -1; - } - edgeflips = 0; - - while (!flipqueue->empty() && maxflips != 0) { - qedge = (badface *) flipqueue->pop(); - flipedge = qedge->ss; - if (flipedge.sh == dummysh) continue; - if ((sorg(flipedge) != qedge->forg) || - (sdest(flipedge) != qedge->fdest)) continue; - sspivot(flipedge, checkseg); - if (checkseg.sh != dummysh) continue; // Can't flip a subsegment. - spivot(flipedge, symedge); - if (symedge.sh == dummysh) continue; // Can't flip a hull edge. - pa = sorg(flipedge); - pb = sdest(flipedge); - pc = sapex(flipedge); - pd = sapex(symedge); - // Choose the triangle abc or abd as the base depending on the angle1 - // (Vac, Vab) and angle2 (Vad, Vab). - for (i = 0; i < 3; i++) vab[i] = pb[i] - pa[i]; - for (i = 0; i < 3; i++) vac[i] = pc[i] - pa[i]; - for (i = 0; i < 3; i++) vad[i] = pd[i] - pa[i]; - dot1 = dot(vac, vab); - dot2 = dot(vad, vab); - dot1 *= dot1; - dot2 *= dot2; - lac = dot(vac, vac); - lad = dot(vad, vad); - if (lad * dot1 <= lac * dot2) { - // angle1 is closer to 90 than angle2, choose abc (flipedge). - abovepoint = facetabovepointarray[shellmark(flipedge)]; - if (abovepoint == (point) NULL) { - getfacetabovepoint(&flipedge); - } - sign = insphere(pa, pb, pc, abovepoint, pd); - ori = orient3d(pa, pb, pc, abovepoint); - } else { - // angle2 is closer to 90 than angle1, choose abd (symedge). - abovepoint = facetabovepointarray[shellmark(symedge)]; - if (abovepoint == (point) NULL) { - getfacetabovepoint(&symedge); + for (i = 0; i < cavetetvertlist->objects; i++) { + parypt = (point *) fastlookup(cavetetvertlist, i); + rd = distance(*parypt, insertpt); + // Is the point very close to an existing point? + if (rd < b->minedgelength) { + pts = parypt; + loc = NEARVERTEX; + break; + } + if (rejptflag) { + // Is the point encroaches upon an existing point? + if (rd < (0.5 * (*parypt)[pointmtrindex])) { + pts = parypt; + loc = ENCVERTEX; + break; + } } - sign = insphere(pa, pb, pd, abovepoint, pc); - ori = orient3d(pa, pb, pd, abovepoint); - } - // Correct the sign. - sign = ori > 0.0 ? sign : -sign; - if (sign > 0.0) { - // Flip the non-Delaunay edge. - flip22sub(&flipedge, flipqueue); - edgeflips++; - if (maxflips > 0) maxflips--; } - } + cavetetvertlist->restart(); // Clear the work list. - if (!maxflips && !b->quiet) { - printf("Warning: Maximal number of flips reached !\n"); - } + if (pts != NULL) { + // The point is either too close to an existing vertex (NEARVERTEX) + // or encroaches upon (inside the protecting ball) of that vertex. + if (loc == NEARVERTEX) { + if (b->nomergevertex) { // -M0/1 option. + // In this case, we still insert this vertex. Although it is very + // close to an existing vertex. Give a warning, anyway. + if (!b->quiet) { + printf("Warning: Two points, %d and %d, are very close.\n", + pointmark(insertpt), pointmark(*pts)); + printf(" Creating a very short edge (len = %g) (< %g).\n", + rd, b->minedgelength); + printf(" You may try a smaller tolerance (-T) (current is %g)\n", + b->epsilon); + printf(" to avoid this warning.\n"); + } + } else { + insertpt[3] = rd; // Only for reporting. + setpoint2ppt(insertpt, *pts); + insertpoint_abort(splitseg, ivf); + ivf->iloc = (int) loc; + return 0; + } + } else { // loc == ENCVERTEX + // The point lies inside the protection ball. + setpoint2ppt(insertpt, *pts); + insertpoint_abort(splitseg, ivf); + ivf->iloc = (int) loc; + return 0; + } + } + } // if (b->plc && (loc != INSTAR)) - if (b->verbose > 1) { - printf(" Total %d flips.\n", edgeflips); + if (b->weighted || ivf->cdtflag || ivf->smlenflag + ) { + // There may be other vertices inside C(p). We need to find them. + // Collect all vertices of C(p). + for (i = 0; i < caveoldtetlist->objects; i++) { + cavetet = (triface *) fastlookup(caveoldtetlist, i); + //assert(infected(*cavetet)); + pts = (point *) &(cavetet->tet[4]); + for (j = 0; j < 4; j++) { + if (pts[j] != dummypoint) { + if (!pinfected(pts[j])) { + pinfect(pts[j]); + cavetetvertlist->newindex((void **) &parypt); + *parypt = pts[j]; + } + } + } // j + } // i + // Uninfect all collected (cavity) vertices. + for (i = 0; i < cavetetvertlist->objects; i++) { + parypt = (point *) fastlookup(cavetetvertlist, i); + puninfect(*parypt); + } + if (ivf->smlenflag) { + REAL len; + // Get the length of the shortest edge connecting to 'newpt'. + parypt = (point *) fastlookup(cavetetvertlist, 0); + ivf->smlen = distance(*parypt, insertpt); + ivf->parentpt = *parypt; + for (i = 1; i < cavetetvertlist->objects; i++) { + parypt = (point *) fastlookup(cavetetvertlist, i); + len = distance(*parypt, insertpt); + if (len < ivf->smlen) { + ivf->smlen = len; + ivf->parentpt = *parypt; + } + } + } } - return edgeflips; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// removetetbypeeloff() Remove a boundary tet by peeling it off. // -// // -// 'striptet' (abcd) is on boundary and can be removed by stripping it off. // -// Let abc and bad are the external boundary faces. // -// // -// To strip 'abcd' from the mesh is to detach its two interal faces (dca and // -// 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) -{ - 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. - enextfnext(abcd, cdbcasing); - enext2fnext(abcd, dcacasing); - symself(cdbcasing); - symself(dcacasing); - // Do the neighboring tets exist? During optimization. It is possible - // that the neighboring tets are already dead. - if ((cdbcasing.tet == dummytet) || (dcacasing.tet == dummytet)) { - // Do not strip this tet. - return false; + if (ivf->cdtflag) { + // Unmark tets. + for (i = 0; i < caveoldtetlist->objects; i++) { + cavetet = (triface *) fastlookup(caveoldtetlist, i); + unmarktest(*cavetet); + } + for (i = 0; i < cavebdrylist->objects; i++) { + cavetet = (triface *) fastlookup(cavebdrylist, i); + unmarktest(*cavetet); + } + // Clean up arrays which are not needed. + cavetetlist->restart(); + if (checksubsegflag) { + cavetetseglist->restart(); + } + if (checksubfaceflag) { + cavetetshlist->restart(); + } + return 1; } - // Are there subfaces? - if (checksubfaces) { - // Get the external subfaces abc, bad. - fnext(abcd, badc); - esymself(badc); - tspivot(abcd, abc); - tspivot(badc, bad); - if (abc.sh != dummysh) { - assert(bad.sh != dummysh); - findedge(&abc, org(abcd), dest(abcd)); - findedge(&bad, org(badc), dest(badc)); - // Is ab a segment? - sspivot(abc, abseg); - if (abseg.sh != dummysh) { - // Does a segment allow to be removed? - if ((b->optlevel > 3) && (b->nobisect == 0)) { - // Only remove this segment if the dihedal angle at ab is between - // [b->maxdihedral-9, 180] (deg). This avoids mistakely fliping - // ab when it has actually no big dihedral angle while cd has. - ang = facedihedral(org(abcd), dest(abcd), apex(abcd), oppo(abcd)); - ang = ang * 180.0 / PI; - if ((ang + 9.0) > b->maxdihedral) { - if (b->verbose > 1) { - printf(" Remove a segment during peeling.\n"); - } - face prevseg, nextseg; - // It is only shared by abc and bad (abcd is a tet). - ssdissolve(abc); - ssdissolve(bad); - abseg.shver = 0; - senext(abseg, nextseg); - spivotself(nextseg); - if (nextseg.sh != dummysh) { - ssdissolve(nextseg); - } - senext2(abseg, prevseg); - spivotself(prevseg); - if (prevseg.sh != dummysh) { - ssdissolve(prevseg); - } - shellfacedealloc(subsegs, abseg.sh); - optcount[1]++; + // Before re-mesh C(p). Process the segments and subfaces which are on the + // boundary of C(p). Make sure that each such segment or subface is + // connecting to a tet outside C(p). So we can re-connect them to the + // new tets inside the C(p) later. + + if (checksubsegflag) { + for (i = 0; i < cavetetseglist->objects; i++) { + paryseg = (face *) fastlookup(cavetetseglist, i); + // Operate on it if it is not the splitting segment, i.e., in sC(p). + if (!smarktested(*paryseg)) { + // Check if the segment is inside the cavity. + // 'j' counts the num of adjacent tets of this seg. + // 'k' counts the num of adjacent tets which are 'sinfected'. + j = k = 0; + sstpivot1(*paryseg, neightet); + spintet = neightet; + while (1) { + j++; + if (!infected(spintet)) { + neineitet = spintet; // An outer tet. Remember it. } else { - return false; + k++; // An in tet. + } + fnextself(spintet); + if (spintet.tet == neightet.tet) break; + } + // assert(j > 0); + if (k == 0) { + // The segment is not connect to C(p) anymore. Remove it by + // Replacing it by the last entry of this list. + s = cavetetseglist->objects - 1; + checkseg = * (face *) fastlookup(cavetetseglist, s); + *paryseg = checkseg; + cavetetseglist->objects--; + i--; + } else if (k < j) { + // The segment is on the boundary of C(p). + sstbond1(*paryseg, neineitet); + } else { // k == j + // The segment is inside C(p). + if (!ivf->splitbdflag) { + checkseg = *paryseg; + sinfect(checkseg); // Flag it as an interior segment. + caveencseglist->newindex((void **) &paryseg); + *paryseg = checkseg; + } else { + assert(0); // Not possible. + } + } + } else { + // assert(smarktested(*paryseg)); + // Flag it as an interior segment. Do not queue it, since it will + // be deleted after the segment splitting. + sinfect(*paryseg); + } + } // i + } // if (checksubsegflag) + + if (checksubfaceflag) { + for (i = 0; i < cavetetshlist->objects; i++) { + parysh = (face *) fastlookup(cavetetshlist, i); + // Operate on it if it is not inside the sub-cavity sC(p). + if (!smarktested(*parysh)) { + // Check if this subface is inside the cavity. + k = 0; + for (j = 0; j < 2; j++) { + stpivot(*parysh, neightet); + if (!infected(neightet)) { + checksh = *parysh; // Remember this side. + } else { + k++; + } + sesymself(*parysh); + } + if (k == 0) { + // The subface is not connected to C(p). Remove it. + s = cavetetshlist->objects - 1; + checksh = * (face *) fastlookup(cavetetshlist, s); + *parysh = checksh; + cavetetshlist->objects--; + i--; + } else if (k == 1) { + // This side is the outer boundary of C(p). + *parysh = checksh; + } else { // k == 2 + if (!ivf->splitbdflag) { + checksh = *parysh; + sinfect(checksh); // Flag it. + caveencshlist->newindex((void **) &parysh); + *parysh = checksh; + } else { + assert(0); // Not possible. } - } else { - return false; } + } else { + // assert(smarktested(*parysh)); + // Flag it as an interior subface. Do not queue it. It will be + // deleted after the facet point insertion. + sinfect(*parysh); } - // Do a 2-to-2 flip on abc and bad, transform abc->dca, bad->cdb. - flip22sub(&abc, NULL); - // The two internal faces become boundary faces. - tsbond(cdbcasing, bad); - tsbond(dcacasing, abc); + } // i + } // if (checksubfaceflag) + + // Create new tetrahedra to fill the cavity. + + for (i = 0; i < cavebdrylist->objects; i++) { + cavetet = (triface *) fastlookup(cavebdrylist, i); + neightet = *cavetet; + unmarktest(neightet); // Unmark it. + // Get the oldtet (inside the cavity). + fsym(neightet, oldtet); + if (apex(neightet) != dummypoint) { + // Create a new tet in the cavity. + maketetrahedron(&newtet); + setorg(newtet, dest(neightet)); + setdest(newtet, org(neightet)); + setapex(newtet, apex(neightet)); + setoppo(newtet, insertpt); + } else { + // Create a new hull tet. + hullsize++; + maketetrahedron(&newtet); + setorg(newtet, org(neightet)); + setdest(newtet, dest(neightet)); + setapex(newtet, insertpt); + setoppo(newtet, dummypoint); // It must opposite to face 3. + // Adjust back to the cavity bounday face. + esymself(newtet); + } + // The new tet inherits attribtes from the old tet. + for (j = 0; j < numelemattrib; j++) { + attrib = elemattribute(oldtet.tet, j); + setelemattribute(newtet.tet, j, attrib); } - } - - // Detach abcd from the two internal faces. - dissolve(cdbcasing); - dissolve(dcacasing); - // Delete abcd. - tetrahedrondealloc(abcd.tet); - - adjtetlist[0] = cdbcasing; - adjtetlist[1] = dcacasing; - - return true; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// removeedgebyflip22() Remove an edge by a 2-to-2 (or 4-to-4) flip. // -// // -// 'abtetlist' contains n tets (n is 2 or 4) sharing edge ab, abtetlist[0] // -// and abtetlist[1] are tets abec and abde, respectively (NOTE, both are in // -// CW edge ring), where a, b, c, and d are coplanar. If n = 4, abtetlist[2] // -// and abtetlist[3] are tets abfd and abcf, respectively. This routine uses // -// flip22() to replace edge ab with cd, the surrounding tets are rotated. // -// // -// If 'key' != NULL. The old tets are replaced by the new tets only if the // -// local mesh quality is improved. Current 'key' = cos(\theta), where \theta // -// is the maximum dihedral angle in the old tets. // -// // -/////////////////////////////////////////////////////////////////////////////// - -bool tetgenmesh::removeedgebyflip22(REAL *key, int n, triface *abtetlist, - queue *flipque) -{ - point pa, pb, pc, pd, pe, pf; - REAL cosmaxd, d1, d2, d3; - bool doflip; - - doflip = true; - adjustedgering(abtetlist[0], CW); - pa = org(abtetlist[0]); - pb = dest(abtetlist[0]); - pe = apex(abtetlist[0]); - pc = oppo(abtetlist[0]); - pd = apex(abtetlist[1]); - if (n == 4) { - pf = apex(abtetlist[2]); - } - if (key && (*key > -1.0)) { - tetalldihedral(pc, pd, pe, pa, NULL, &d1, NULL); - tetalldihedral(pd, pc, pe, pb, NULL, &d2, NULL); - cosmaxd = d1 < d2 ? d1 : d2; // Choose the bigger angle. - if (n == 4) { - tetalldihedral(pd, pc, pf, pa, NULL, &d1, NULL); - tetalldihedral(pc, pd, pf, pb, NULL, &d2, NULL); - d3 = d1 < d2 ? d1 : d2; // Choose the bigger angle. - cosmaxd = cosmaxd < d3 ? cosmaxd : d3; // Choose the bigger angle. - } - doflip = (*key < cosmaxd); // Can local quality be improved? - } - - if (doflip) { - flip22(&abtetlist[0], NULL); - // Return the improved quality value. - if (key) *key = cosmaxd; - } - - return doflip; -} + if (b->varvolume) { + volume = volumebound(oldtet.tet); + setvolumebound(newtet.tet, volume); + } + // Connect newtet <==> neightet, this also disconnect the old bond. + bond(newtet, neightet); + // oldtet still connects to neightet. + *cavetet = oldtet; // *cavetet = newtet; + } // i -/////////////////////////////////////////////////////////////////////////////// -// // -// removefacebyflip23() Remove a face by a 2-to-3 flip. // -// // -// 'abctetlist' contains 2 tets sharing abc, which are [0]abcd and [1]bace. // -// This routine forms three new tets that abc is not a face anymore. Save // -// them in 'newtetlist': [0]edab, [1]edbc, and [2]edca. Note that the new // -// tets may not valid if one of them get inverted. return false if so. // -// // -// If 'key' != NULL. The old tets are replaced by the new tets only if the // -// local mesh quality is improved. Current 'key' = cos(\theta), where \theta // -// is the maximum dihedral angle in the old tets. // -// // -// If the face is flipped, 'newtetlist' returns the three new tets. The two // -// tets in 'abctetlist' are NOT deleted. The caller has the right to either // -// delete them or reverse the operation. // -// // -/////////////////////////////////////////////////////////////////////////////// + // Set a handle for speeding point location. + recenttet = newtet; + //setpoint2tet(insertpt, encode(newtet)); + setpoint2tet(insertpt, (tetrahedron) (newtet.tet)); -bool tetgenmesh::removefacebyflip23(REAL *key, triface *abctetlist, - triface *newtetlist, queue *flipque) -{ - triface edab, edbc, edca; // new configuration. - triface newfront, oldfront, adjfront; - face checksh; - point pa, pb, pc, pd, pe; - REAL ori, cosmaxd, d1, d2, d3; - REAL attrib, volume; - bool doflip; - int i; + // Re-use this list to save new interior cavity faces. + cavetetlist->restart(); - adjustedgering(abctetlist[0], CCW); - pa = org(abctetlist[0]); - pb = dest(abctetlist[0]); - pc = apex(abctetlist[0]); - pd = oppo(abctetlist[0]); - pe = oppo(abctetlist[1]); - - // Check if the flip creates valid new tets. - ori = orient3d(pe, pd, pa, pb); - if (ori < 0.0) { - ori = orient3d(pe, pd, pb, pc); - if (ori < 0.0) { - ori = orient3d(pe, pd, pc, pa); - } - } - doflip = (ori < 0.0); // Can abc be flipped away? - if (doflip && (key != (REAL *) NULL)) { - if (*key > -1.0) { - // Test if the new tets reduce the maximal dihedral angle. - tetalldihedral(pe, pd, pa, pb, NULL, &d1, NULL); - tetalldihedral(pe, pd, pb, pc, NULL, &d2, NULL); - tetalldihedral(pe, pd, pc, pa, NULL, &d3, NULL); - cosmaxd = d1 < d2 ? d1 : d2; // Choose the bigger angle. - cosmaxd = cosmaxd < d3 ? cosmaxd : d3; // Choose the bigger angle. - doflip = (*key < cosmaxd); // Can local quality be improved? - } - } - - if (doflip) { - // A valid (2-to-3) flip is found. - flip23s++; - // Create the new tets. - maketetrahedron(&edab); - setorg(edab, pe); - setdest(edab, pd); - setapex(edab, pa); - setoppo(edab, pb); - maketetrahedron(&edbc); - setorg(edbc, pe); - setdest(edbc, pd); - setapex(edbc, pb); - setoppo(edbc, pc); - maketetrahedron(&edca); - setorg(edca, pe); - setdest(edca, pd); - setapex(edca, pc); - setoppo(edca, pa); - // Transfer the element attributes. - for (i = 0; i < in->numberoftetrahedronattributes; i++) { - attrib = elemattribute(abctetlist[0].tet, i); - setelemattribute(edab.tet, i, attrib); - setelemattribute(edbc.tet, i, attrib); - setelemattribute(edca.tet, i, attrib); - } - // Transfer the volume constraints. - if (b->varvolume && !b->refine) { - volume = volumebound(abctetlist[0].tet); - setvolumebound(edab.tet, volume); - setvolumebound(edbc.tet, volume); - setvolumebound(edca.tet, volume); - } - // Return two new tets. - newtetlist[0] = edab; - newtetlist[1] = edbc; - newtetlist[2] = edca; - // Glue the three new tets. - for (i = 0; i < 3; i++) { - fnext(newtetlist[i], newfront); - bond(newfront, newtetlist[(i + 1) % 3]); - } - // Substitute the three new tets into the old cavity. - for (i = 0; i < 3; i++) { - fnext(abctetlist[0], oldfront); - sym(oldfront, adjfront); // may be outside. - enextfnext(newtetlist[i], newfront); - bond(newfront, adjfront); - if (checksubfaces) { - tspivot(oldfront, checksh); - if (checksh.sh != dummysh) { - tsbond(newfront, checksh); + // Connect adjacent new tetrahedra together. + for (i = 0; i < cavebdrylist->objects; i++) { + cavetet = (triface *) fastlookup(cavebdrylist, i); + // cavtet is an oldtet, get the newtet at this face. + oldtet = *cavetet; + fsym(oldtet, neightet); + fsym(neightet, newtet); + // Comment: oldtet and newtet must be at the same directed edge. + // Connect the three other faces of this newtet. + for (j = 0; j < 3; j++) { + esym(newtet, neightet); // Go to the face. + if (neightet.tet[neightet.ver & 3] == NULL) { + // Find the adjacent face of this newtet. + spintet = oldtet; + while (1) { + fnextself(spintet); + if (!infected(spintet)) break; + } + fsym(spintet, newneitet); + esymself(newneitet); + assert(newneitet.tet[newneitet.ver & 3] == NULL); + bond(neightet, newneitet); + if (ivf->lawson > 1) { + cavetetlist->newindex((void **) &parytet); + *parytet = neightet; } } - if (flipque != (queue *) NULL) { - enqueueflipface(newfront, flipque); + //setpoint2tet(org(newtet), encode(newtet)); + setpoint2tet(org(newtet), (tetrahedron) (newtet.tet)); + enextself(newtet); + enextself(oldtet); + } + *cavetet = newtet; // Save the new tet. + } // i + + if (checksubfaceflag) { + // Connect subfaces on the boundary of the cavity to the new tets. + for (i = 0; i < cavetetshlist->objects; i++) { + parysh = (face *) fastlookup(cavetetshlist, i); + // Connect it if it is not a missing subface. + if (!sinfected(*parysh)) { + stpivot(*parysh, neightet); + fsym(neightet, spintet); + sesymself(*parysh); + tsbond(spintet, *parysh); } - enextself(abctetlist[0]); } - findedge(&(abctetlist[1]), pb, pa); - for (i = 0; i < 3; i++) { - fnext(abctetlist[1], oldfront); - sym(oldfront, adjfront); // may be outside. - enext2fnext(newtetlist[i], newfront); - bond(newfront, adjfront); - if (checksubfaces) { - tspivot(oldfront, checksh); - if (checksh.sh != dummysh) { - tsbond(newfront, checksh); + } + + if (checksubsegflag) { + // Connect segments on the boundary of the cavity to the new tets. + for (i = 0; i < cavetetseglist->objects; i++) { + paryseg = (face *) fastlookup(cavetetseglist, i); + // Connect it if it is not a missing segment. + if (!sinfected(*paryseg)) { + sstpivot1(*paryseg, neightet); + spintet = neightet; + while (1) { + tssbond1(spintet, *paryseg); + fnextself(spintet); + if (spintet.tet == neightet.tet) break; } } - if (flipque != (queue *) NULL) { - enqueueflipface(newfront, flipque); - } - enext2self(abctetlist[1]); } - // Do not delete the old tets. - // for (i = 0; i < 2; i++) { - // tetrahedrondealloc(abctetlist[i].tet); - // } - // Return the improved quality value. - if (key != (REAL *) NULL) *key = cosmaxd; - return true; } - return false; -} + if (((splitsh != NULL) && (splitsh->sh != NULL)) || + ((splitseg != NULL) && (splitseg->sh != NULL))) { + // Split a subface or a segment. + sinsertvertex(insertpt, splitsh, splitseg, ivf->sloc, ivf->sbowywat, 0); + } -/////////////////////////////////////////////////////////////////////////////// -// // -// removeedgebyflip32() Remove an edge by a 3-to-2 flip. // -// // -// '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 // -// if one of them get inverted. return false if so. // -// // -// If 'key' != NULL. The old tets are replaced by the new tets only if the // -// local mesh quality is improved. Current 'key' = cos(\theta), where \theta // -// is the maximum dihedral angle in the old tets. // -// // -// If the edge is flipped, 'newtetlist' returns the two new tets. The three // -// tets in 'abtetlist' are NOT deleted. The caller has the right to either // -// delete them or reverse the operation. // -// // -/////////////////////////////////////////////////////////////////////////////// + if (checksubfaceflag) { + if (ivf->splitbdflag) { + // Recover new subfaces in C(p). + for (i = 0; i < caveshbdlist->objects; i++) { + // Get an old subface at edge [a, b]. + parysh = (face *) fastlookup(caveshbdlist, i); + spivot(*parysh, checksh); // The new subface [a, b, p]. + // Do not recover a deleted new face (degenerated). + if (checksh.sh[3] != NULL) { + // Note that the old subface still connects to adjacent old tets + // of C(p), which still connect to the tets outside C(p). + stpivot(*parysh, neightet); + assert(infected(neightet)); + // Find the adjacent tet containing the edge [a,b] outside C(p). + spintet = neightet; + while (1) { + fnextself(spintet); + if (!infected(spintet)) break; + assert(spintet.tet != neightet.tet); + } + // The adjacent tet connects to a new tet in C(p). + fsym(spintet, neightet); + assert(!infected(neightet)); + // Find the tet containing the face [a, b, p]. + spintet = neightet; + while (1) { + fnextself(spintet); + if (apex(spintet) == insertpt) break; + assert(spintet.tet != neightet.tet); + } + // Adjust the edge direction in spintet and checksh. + if (sorg(checksh) != org(spintet)) { + sesymself(checksh); + assert(sorg(checksh) == org(spintet)); + } + assert(sdest(checksh) == dest(spintet)); + // Connect the subface to two adjacent tets. + tsbond(spintet, checksh); + fsymself(spintet); + sesymself(checksh); + tsbond(spintet, checksh); + } // if (checksh.sh[3] != NULL) + } + // There should be no missing interior subfaces in C(p). + assert(caveencshlist->objects == 0l); + } else { + // The Boundary recovery phase. + // Put all new subfaces into stack for recovery. + for (i = 0; i < caveshbdlist->objects; i++) { + // Get an old subface at edge [a, b]. + parysh = (face *) fastlookup(caveshbdlist, i); + spivot(*parysh, checksh); // The new subface [a, b, p]. + // Do not recover a deleted new face (degenerated). + if (checksh.sh[3] != NULL) { + subfacstack->newindex((void **) &parysh); + *parysh = checksh; + } + } + // Put all interior subfaces into stack for recovery. + for (i = 0; i < caveencshlist->objects; i++) { + parysh = (face *) fastlookup(caveencshlist, i); + assert(sinfected(*parysh)); + // Some subfaces inside C(p) might be split in sinsertvertex(). + // Only queue those faces which are not split. + if (!smarktested(*parysh)) { + checksh = *parysh; + suninfect(checksh); + stdissolve(checksh); // Detach connections to old tets. + subfacstack->newindex((void **) &parysh); + *parysh = checksh; + } + } + } + } // if (checksubfaceflag) -bool tetgenmesh::removeedgebyflip32(REAL *key, triface *abtetlist, - triface *newtetlist, queue *flipque) -{ - triface dcea, cdeb; // new configuration. - triface newfront, oldfront, adjfront; - face checksh, checkseg; - point pa, pb, pc, pd, pe; - REAL ori, cosmaxd, d1, d2; - REAL attrib, volume; - bool doflip; - int i; + if (checksubsegflag) { + if (ivf->splitbdflag) { + if (splitseg != NULL) { + // Recover the two new subsegments in C(p). + for (i = 0; i < cavesegshlist->objects; i++) { + paryseg = (face *) fastlookup(cavesegshlist, i); + // Insert this subsegment into C(p). + checkseg = *paryseg; + // Get the adjacent new subface. + checkseg.shver = 0; + spivot(checkseg, checksh); + if (checksh.sh != NULL) { + // Get the adjacent new tetrahedron. + stpivot(checksh, neightet); + } else { + // It's a dangling segment. + point2tetorg(sorg(checkseg), neightet); + finddirection(&neightet, sdest(checkseg)); + assert(dest(neightet) == sdest(checkseg)); + } + assert(!infected(neightet)); + sstbond1(checkseg, neightet); + spintet = neightet; + while (1) { + tssbond1(spintet, checkseg); + fnextself(spintet); + if (spintet.tet == neightet.tet) break; + } + } + } // if (splitseg != NULL) + // There should be no interior segment in C(p). + assert(caveencseglist->objects == 0l); + } else { + // The Boundary Recovery Phase. + // Queue missing segments in C(p) for recovery. + if (splitseg != NULL) { + // Queue two new subsegments in C(p) for recovery. + for (i = 0; i < cavesegshlist->objects; i++) { + paryseg = (face *) fastlookup(cavesegshlist, i); + checkseg = *paryseg; + //sstdissolve1(checkseg); // It has not been connected yet. + s = randomnation(subsegstack->objects + 1); + subsegstack->newindex((void **) &paryseg); + *paryseg = * (face *) fastlookup(subsegstack, s); + paryseg = (face *) fastlookup(subsegstack, s); + *paryseg = checkseg; + } + } // if (splitseg != NULL) + for (i = 0; i < caveencseglist->objects; i++) { + paryseg = (face *) fastlookup(caveencseglist, i); + assert(sinfected(*paryseg)); + if (!smarktested(*paryseg)) { // It may be split. + checkseg = *paryseg; + suninfect(checkseg); + sstdissolve1(checkseg); // Detach connections to old tets. + s = randomnation(subsegstack->objects + 1); + subsegstack->newindex((void **) &paryseg); + *paryseg = * (face *) fastlookup(subsegstack, s); + paryseg = (face *) fastlookup(subsegstack, s); + *paryseg = checkseg; + } + } + } + } // if (checksubsegflag) + + if (b->weighted + ) { + // Some vertices may be completed inside the cavity. They must be + // detected and added to recovering list. + // Since every "live" vertex must contain a pointer to a non-dead + // tetrahedron, we can check for each vertex this pointer. + for (i = 0; i < cavetetvertlist->objects; i++) { + pts = (point *) fastlookup(cavetetvertlist, i); + decode(point2tet(*pts), *searchtet); + assert(searchtet->tet != NULL); // No tet has been deleted yet. + if (infected(*searchtet)) { + if (b->weighted) { + if (b->verbose > 1) { + printf(" Point #%d is non-regular after the insertion of #%d.\n", + pointmark(*pts), pointmark(insertpt)); + } + setpointtype(*pts, NREGULARVERTEX); + nonregularcount++; + } + } + } + } - pa = org(abtetlist[0]); - pb = dest(abtetlist[0]); - pc = apex(abtetlist[0]); - pd = apex(abtetlist[1]); - pe = apex(abtetlist[2]); + if (ivf->chkencflag & 1) { + // Queue all segment outside C(p). + for (i = 0; i < cavetetseglist->objects; i++) { + paryseg = (face *) fastlookup(cavetetseglist, i); + // Skip if it is the split segment. + if (!sinfected(*paryseg)) { + enqueuesubface(badsubsegs, paryseg); + } + } + if (splitseg != NULL) { + // Queue the two new subsegments inside C(p). + for (i = 0; i < cavesegshlist->objects; i++) { + paryseg = (face *) fastlookup(cavesegshlist, i); + enqueuesubface(badsubsegs, paryseg); + } + } + } // if (chkencflag & 1) - ori = orient3d(pd, pc, pe, pa); - if (ori < 0.0) { - ori = orient3d(pc, pd, pe, pb); - } - doflip = (ori < 0.0); // Can ab be flipped away? + if (ivf->chkencflag & 2) { + // Queue all subfaces outside C(p). + for (i = 0; i < cavetetshlist->objects; i++) { + parysh = (face *) fastlookup(cavetetshlist, i); + // Skip if it is a split subface. + if (!sinfected(*parysh)) { + enqueuesubface(badsubfacs, parysh); + } + } + // Queue all new subfaces inside C(p). + for (i = 0; i < caveshbdlist->objects; i++) { + // Get an old subface at edge [a, b]. + parysh = (face *) fastlookup(caveshbdlist, i); + spivot(*parysh, checksh); // checksh is a new subface [a, b, p]. + // Do not recover a deleted new face (degenerated). + if (checksh.sh[3] != NULL) { + enqueuesubface(badsubfacs, &checksh); + } + } + } // if (chkencflag & 2) - // Does the caller ensure a valid configuration? - if (doflip && (key != (REAL *) NULL)) { - if (*key > -1.0) { - // Test if the new tets reduce the maximal dihedral angle. - tetalldihedral(pd, pc, pe, pa, NULL, &d1, NULL); - tetalldihedral(pc, pd, pe, pb, NULL, &d2, NULL); - cosmaxd = d1 < d2 ? d1 : d2; // Choose the bigger angle. - doflip = (*key < cosmaxd); // Can local quality be improved? - // Return the key - *key = cosmaxd; + if (ivf->chkencflag & 4) { + // Queue all new tetrahedra in C(p). + for (i = 0; i < cavebdrylist->objects; i++) { + cavetet = (triface *) fastlookup(cavebdrylist, i); + enqueuetetrahedron(cavetet); } } - // 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; + // C(p) is re-meshed successfully. + + // Delete the old tets in C(p). + for (i = 0; i < caveoldtetlist->objects; i++) { + searchtet = (triface *) fastlookup(caveoldtetlist, i); + if (ishulltet(*searchtet)) { + hullsize--; } + tetrahedrondealloc(searchtet->tet); } - if (doflip) { - // Create the new tets. - maketetrahedron(&dcea); - setorg(dcea, pd); - setdest(dcea, pc); - setapex(dcea, pe); - setoppo(dcea, pa); - maketetrahedron(&cdeb); - setorg(cdeb, pc); - setdest(cdeb, pd); - setapex(cdeb, pe); - setoppo(cdeb, pb); - // Transfer the element attributes. - for (i = 0; i < in->numberoftetrahedronattributes; i++) { - attrib = elemattribute(abtetlist[0].tet, i); - setelemattribute(dcea.tet, i, attrib); - setelemattribute(cdeb.tet, i, attrib); - } - // Transfer the volume constraints. - if (b->varvolume && !b->refine) { - volume = volumebound(abtetlist[0].tet); - setvolumebound(dcea.tet, volume); - setvolumebound(cdeb.tet, volume); - } - // Return two new tets. - newtetlist[0] = dcea; - newtetlist[1] = cdeb; - // Glue the two new tets. - bond(dcea, cdeb); - // Substitute the two new tets into the old three-tets cavity. - for (i = 0; i < 3; i++) { - fnext(dcea, newfront); // face dca, cea, eda. - esym(abtetlist[(i + 1) % 3], oldfront); - enextfnextself(oldfront); - // Get the adjacent tet at the face (may be a dummytet). - sym(oldfront, adjfront); - bond(newfront, adjfront); - if (checksubfaces) { - tspivot(oldfront, checksh); - if (checksh.sh != dummysh) { - tsbond(newfront, checksh); + if (((splitsh != NULL) && (splitsh->sh != NULL)) || + ((splitseg != NULL) && (splitseg->sh != NULL))) { + // Delete the old subfaces in sC(p). + for (i = 0; i < caveshlist->objects; i++) { + parysh = (face *) fastlookup(caveshlist, i); + if (checksubfaceflag) {//if (bowywat == 2) { + // It is possible that this subface still connects to adjacent + // tets which are not in C(p). If so, clear connections in the + // adjacent tets at this subface. + stpivot(*parysh, neightet); + if (neightet.tet != NULL) { + if (neightet.tet[4] != NULL) { + // Found an adjacent tet. It must be not in C(p). + assert(!infected(neightet)); + tsdissolve(neightet); + fsymself(neightet); + assert(!infected(neightet)); + tsdissolve(neightet); + } } } - if (flipque != (queue *) NULL) { - enqueueflipface(newfront, flipque); + shellfacedealloc(subfaces, parysh->sh); + } + if ((splitseg != NULL) && (splitseg->sh != NULL)) { + // Delete the old segment in sC(p). + shellfacedealloc(subsegs, splitseg->sh); + } + } + + if (ivf->lawson) { + for (i = 0; i < cavebdrylist->objects; i++) { + searchtet = (triface *) fastlookup(cavebdrylist, i); + flippush(flipstack, searchtet); + } + if (ivf->lawson > 1) { + for (i = 0; i < cavetetlist->objects; i++) { + searchtet = (triface *) fastlookup(cavetetlist, i); + flippush(flipstack, searchtet); } - enext2self(dcea); } - for (i = 0; i < 3; i++) { - fnext(cdeb, newfront); // face cdb, deb, ecb. - esym(abtetlist[(i + 1) % 3], oldfront); - enext2fnextself(oldfront); - // Get the adjacent tet at the face (may be a dummytet). - sym(oldfront, adjfront); - bond(newfront, adjfront); - if (checksubfaces) { - tspivot(oldfront, checksh); - if (checksh.sh != dummysh) { - tsbond(newfront, checksh); - } - } - if (flipque != (queue *) NULL) { - enqueueflipface(newfront, flipque); - } - enextself(cdeb); - } - // Do not delete the old tets. - // for (i = 0; i < 3; i++) { - // tetrahedrondealloc(abtetlist[i].tet); - // } - return true; - } // if (doflip) + } + + + // Clean the working lists. + + caveoldtetlist->restart(); + cavebdrylist->restart(); + cavetetlist->restart(); + + if (checksubsegflag) { + cavetetseglist->restart(); + caveencseglist->restart(); + } + + if (checksubfaceflag) { + cavetetshlist->restart(); + caveencshlist->restart(); + } + + if (b->weighted || ivf->validflag) { + cavetetvertlist->restart(); + } + + if (((splitsh != NULL) && (splitsh->sh != NULL)) || + ((splitseg != NULL) && (splitseg->sh != NULL))) { + caveshlist->restart(); + caveshbdlist->restart(); + cavesegshlist->restart(); + } - return false; + return 1; // Point is inserted. } /////////////////////////////////////////////////////////////////////////////// // // -// removeedgebytranNM() Remove an edge by transforming n-to-m tets. // -// // -// This routine attempts to remove a given edge (ab) by transforming the set // -// T of tets surrounding ab into another set T' of tets. T and T' have the // -// same outer faces and ab is not an edge of T' anymore. Let |T|=n, and |T'| // -// =m, it is actually a n-to-m flip for n > 3. The relation between n and m // -// depends on the method, ours is found below. // -// // -// 'abtetlist' contains n tets sharing ab. Imaging that ab is perpendicular // -// to the screen, where a lies in front of and b lies behind it. Let the // -// projections of the n apexes onto screen in clockwise order are: p_0, ... // -// p_n-1, respectively. The tets in the list are: [0]abp_0p_n-1,[1]abp_1p_0, // -// ..., [n-1]abp_n-1p_n-2, respectively. // -// // -// The principle of the approach is: Recursively reduce the link of ab by // -// using flip23 until only three faces remain, hence a flip32 can be applied // -// to remove ab. For a given face a.b.p_0, check a flip23 can be applied on // -// it, i.e, edge p_1.p_n-1 crosses it. NOTE*** We do the flip even p_1.p_n-1 // -// intersects with a.b (they are coplanar). If so, a degenerate tet (a.b.p_1.// -// p_n-1) is temporarily created, but it will be eventually removed by the // -// final flip32. This relaxation splits a flip44 into flip23 + flip32. *NOTE // -// Now suppose a.b.p_0 gets flipped, p_0 is not on the link of ab anymore. // -// The link is then reduced (by 1). 2 of the 3 new tets, p_n-1.p_1.p_0.a and // -// p_1.p_n-1.p_0.b, will be part of the new configuration. The left new tet,// -// a.b.p_1.p_n-1, goes into the new link of ab. A recurrence can be applied. // +// insertpoint_abort() Abort the insertion of a new vertex. // // // -// If 'e1' and 'e2' are not NULLs, they specify an wanted edge to appear in // -// the new tet configuration. In such case, only do flip23 if edge e1<->e2 // -// can be recovered. It is used in removeedgebycombNM(). // -// // -// If ab gets removed. 'newtetlist' contains m new tets. By using the above // -// approach, the pairs (n, m) can be easily enumerated. For example, (3, 2),// -// (4, 4), (5, 6), (6, 8), (7, 10), (8, 12), (9, 14), (10, 16), and so on. // -// It is easy to deduce, that m = (n - 2) * 2, when n >= 3. The n tets in // -// 'abtetlist' are NOT deleted in this routine. The caller has the right to // -// either delete them or reverse this operation. // +// The cavity will be restored. All working lists are cleared. // // // /////////////////////////////////////////////////////////////////////////////// -bool tetgenmesh::removeedgebytranNM(REAL *key, int n, triface *abtetlist, - triface *newtetlist, point e1, point e2, queue *flipque) +void tetgenmesh::insertpoint_abort(face *splitseg, insertvertexflags *ivf) { - triface tmpabtetlist[21]; // Temporary max 20 tets configuration. - triface newfront, oldfront, adjfront; - face checksh; - point pa, pb, p[21]; - 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 - // Two points a and b are fixed. - pa = org(abtetlist[0]); - pb = dest(abtetlist[0]); - // The points p_0, p_1, ..., p_n-1 are permuted in each new configuration. - // These permutations can be easily done in the following loop. - // Loop through all the possible new tets configurations. Stop on finding - // a valid new tet configuration which also immproves the quality value. - for (i = 0; i < n; i++) { - // Get other n points for the current configuration. - for (j = 0; j < n; j++) { - p[j] = apex(abtetlist[(i + j) % n]); - } - // Is there a wanted edge? - if ((e1 != (point) NULL) && (e2 != (point) NULL)) { - // Yes. Skip this face if p[1]<->p[n-1] is not the edge. - if (!(((p[1] == e1) && (p[n - 1] == e2)) || - ((p[1] == e2) && (p[n - 1] == e1)))) continue; - } - // Test if face a.b.p_0 can be flipped (by flip23), ie, to check if the - // edge p_n-1.p_1 crosses face a.b.p_0 properly. - // Note. It is possible that face a.b.p_0 has type flip44, ie, a,b,p_1, - // and p_n-1 are coplanar. A trick is to split the flip44 into two - // steps: frist a flip23, then a flip32. The first step creates a - // degenerate tet (vol=0) which will be removed by the second flip. - ori = orient3d(pa, pb, p[1], p[n - 1]); - copflag = (ori == 0.0); // Are they coplanar? - if (ori >= 0.0) { - // Accept the coplanar case which supports flip44. - ori = orient3d(pb, p[0], p[1], p[n - 1]); - if (ori > 0.0) { - ori = orient3d(p[0], pa, p[1], p[n - 1]); - } - } - // Is face abc flipable? - if (ori > 0.0) { - // A valid (2-to-3) flip (or 4-to-4 flip) is found. - copflag ? flip44s++ : flip23s++; - doflip = true; - if (key != (REAL *) NULL) { - if (*key > -1.0) { - // Test if the new tets reduce the maximal dihedral angle. Only 2 - // tets, p_n-1.p_1.p_0.a and p_1.p_n-1.p_0.b, need to be tested - // The left one a.b.p_n-1.p_1 goes into the new link of ab. - tetalldihedral(p[n - 1], p[1], p[0], pa, NULL, &d1, NULL); - tetalldihedral(p[1], p[n - 1], p[0], pb, NULL, &d2, NULL); - cosmaxd = d1 < d2 ? d1 : d2; // Choose the bigger angle. - 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. - maketetrahedron(&(newtetlist[0])); - setorg(newtetlist[0], p[n - 1]); - setdest(newtetlist[0], p[1]); - setapex(newtetlist[0], p[0]); - setoppo(newtetlist[0], pa); - maketetrahedron(&(newtetlist[1])); - setorg(newtetlist[1], p[1]); - setdest(newtetlist[1], p[n - 1]); - setapex(newtetlist[1], p[0]); - setoppo(newtetlist[1], pb); - // Create the n - 1 temporary new tets (the new Star(ab)). - maketetrahedron(&(tmpabtetlist[0])); - setorg(tmpabtetlist[0], pa); - setdest(tmpabtetlist[0], pb); - setapex(tmpabtetlist[0], p[n - 1]); - setoppo(tmpabtetlist[0], p[1]); - for (j = 1; j < n - 1; j++) { - maketetrahedron(&(tmpabtetlist[j])); - setorg(tmpabtetlist[j], pa); - setdest(tmpabtetlist[j], pb); - setapex(tmpabtetlist[j], p[j]); - setoppo(tmpabtetlist[j], p[j + 1]); - } - // Transfer the element attributes. - for (j = 0; j < in->numberoftetrahedronattributes; j++) { - attrib = elemattribute(abtetlist[0].tet, j); - setelemattribute(newtetlist[0].tet, j, attrib); - setelemattribute(newtetlist[1].tet, j, attrib); - for (k = 0; k < n - 1; k++) { - setelemattribute(tmpabtetlist[k].tet, j, attrib); - } - } - // Transfer the volume constraints. - if (b->varvolume && !b->refine) { - volume = volumebound(abtetlist[0].tet); - setvolumebound(newtetlist[0].tet, volume); - setvolumebound(newtetlist[1].tet, volume); - for (k = 0; k < n - 1; k++) { - setvolumebound(tmpabtetlist[k].tet, volume); - } - } - // Glue the new tets at their internal faces: 2 + (n - 1). - bond(newtetlist[0], newtetlist[1]); // p_n-1.p_1.p_0. - fnext(newtetlist[0], newfront); - enext2fnext(tmpabtetlist[0], adjfront); - bond(newfront, adjfront); // p_n-1.p_1.a. - fnext(newtetlist[1], newfront); - enextfnext(tmpabtetlist[0], adjfront); - bond(newfront, adjfront); // p_n-1.p_1.b. - // Glue n - 1 internal faces around ab. - for (j = 0; j < n - 1; j++) { - fnext(tmpabtetlist[j], newfront); - bond(newfront, tmpabtetlist[(j + 1) % (n - 1)]); // a.b.p_j+1 - } - // Substitute the old tets with the new tets by connecting the new - // tets to the adjacent tets in the mesh. There are n * 2 (outer) - // faces of the new tets need to be operated. - // Note, after the substitution, the old tets still have pointers to - // their adjacent tets in the mesh. These pointers can be re-used - // to inverse the substitution. - for (j = 0; j < n; j++) { - // Get an old tet: [0]a.b.p_0.p_n-1 or [j]a.b.p_j.p_j-1, (j > 0). - oldfront = abtetlist[(i + j) % n]; - esymself(oldfront); - enextfnextself(oldfront); - // Get an adjacent tet at face: [0]a.p_0.p_n-1 or [j]a.p_j.p_j-1. - sym(oldfront, adjfront); // adjfront may be dummy. - // Get the corresponding face from the new tets. - if (j == 0) { - enext2fnext(newtetlist[0], newfront); // a.p_0.n_n-1 - } else if (j == 1) { - enextfnext(newtetlist[0], newfront); // a.p_1.p_0 - } else { // j >= 2. - enext2fnext(tmpabtetlist[j - 1], newfront); // a.p_j.p_j-1 - } - bond(newfront, adjfront); - if (checksubfaces) { - tspivot(oldfront, checksh); - if (checksh.sh != dummysh) { - tsbond(newfront, checksh); - } - } - if (flipque != (queue *) NULL) { - // Only queue the faces of the two new tets. - if (j < 2) enqueueflipface(newfront, flipque); - } - } - for (j = 0; j < n; j++) { - // Get an old tet: [0]a.b.p_0.p_n-1 or [j]a.b.p_j.p_j-1, (j > 0). - oldfront = abtetlist[(i + j) % n]; - esymself(oldfront); - enext2fnextself(oldfront); - // Get an adjacent tet at face: [0]b.p_0.p_n-1 or [j]b.p_j.p_j-1. - sym(oldfront, adjfront); // adjfront may be dummy. - // Get the corresponding face from the new tets. - if (j == 0) { - enextfnext(newtetlist[1], newfront); // b.p_0.n_n-1 - } else if (j == 1) { - enext2fnext(newtetlist[1], newfront); // b.p_1.p_0 - } else { // j >= 2. - enextfnext(tmpabtetlist[j - 1], newfront); // b.p_j.p_j-1 - } - bond(newfront, adjfront); - if (checksubfaces) { - tspivot(oldfront, checksh); - if (checksh.sh != dummysh) { - tsbond(newfront, checksh); - } - } - if (flipque != (queue *) NULL) { - // Only queue the faces of the two new tets. - if (j < 2) enqueueflipface(newfront, flipque); - } - } - // Adjust the faces in the temporary new tets at ab for recursively - // processing on the n-1 tets.(See the description at beginning) - for (j = 0; j < n - 1; j++) { - fnextself(tmpabtetlist[j]); - } - if (n > 4) { - success = removeedgebytranNM(&tmpkey, n-1, tmpabtetlist, - &(newtetlist[2]), NULL, NULL, flipque); - } else { // assert(n == 4); - success = removeedgebyflip32(&tmpkey, tmpabtetlist, - &(newtetlist[2]), flipque); - } - // No matter it was success or not, delete the temporary tets. - for (j = 0; j < n - 1; j++) { - tetrahedrondealloc(tmpabtetlist[j].tet); - } - if (success) { - // The new configuration is good. - // Do not delete the old tets. - // for (j = 0; j < n; j++) { - // tetrahedrondealloc(abtetlist[j].tet); - // } - // Save the minimal improved quality value. - if (key != (REAL *) NULL) { - *key = (tmpkey < cosmaxd ? tmpkey : cosmaxd); - } - 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); - enextfnextself(oldfront); // [0]a.p_0.p_n-1, [j]a.p_j.p_j-1. - sym(oldfront, adjfront); // adjfront may be dummy. - bond(oldfront, adjfront); - if (checksubfaces) { - tspivot(oldfront, checksh); - if (checksh.sh != dummysh) { - tsbond(oldfront, checksh); - } - } - } - for (j = 0; j < n; j++) { - oldfront = abtetlist[(i + j) % n]; - esymself(oldfront); - enext2fnextself(oldfront); // [0]b.p_0.p_n-1, [j]b.p_j.p_j-1. - sym(oldfront, adjfront); // adjfront may be dummy - bond(oldfront, adjfront); - if (checksubfaces) { - tspivot(oldfront, checksh); - if (checksh.sh != dummysh) { - tsbond(oldfront, checksh); - } - } - } - // Delete the new tets. - tetrahedrondealloc(newtetlist[0].tet); - tetrahedrondealloc(newtetlist[1].tet); - // If tmpkey has been modified, then the failure was not due to - // unflipable configuration, but the non-improvement. - if (key && (tmpkey < *key)) { - *key = tmpkey; - return false; - } - } // if (success) - } // if (doflip) - } // if (ori > 0.0) - } // for (i = 0; i < n; i++) + triface *cavetet; + face *parysh; + int i; - return false; + for (i = 0; i < caveoldtetlist->objects; i++) { + cavetet = (triface *) fastlookup(caveoldtetlist, i); + uninfect(*cavetet); + unmarktest(*cavetet); + } + for (i = 0; i < cavebdrylist->objects; i++) { + cavetet = (triface *) fastlookup(cavebdrylist, i); + unmarktest(*cavetet); + } + cavetetlist->restart(); + cavebdrylist->restart(); + caveoldtetlist->restart(); + cavetetseglist->restart(); + cavetetshlist->restart(); + if (ivf->splitbdflag) { + if ((splitseg != NULL) && (splitseg->sh != NULL)) { + sunmarktest(*splitseg); + } + for (i = 0; i < caveshlist->objects; i++) { + parysh = (face *) fastlookup(caveshlist, i); + assert(smarktested(*parysh)); + sunmarktest(*parysh); + } + caveshlist->restart(); + cavesegshlist->restart(); + } } +//// //// +//// //// +//// flip_cxx ///////////////////////////////////////////////////////////////// + +//// delaunay_cxx ///////////////////////////////////////////////////////////// +//// //// +//// //// + /////////////////////////////////////////////////////////////////////////////// // // -// removeedgebycombNM() Remove an edge by combining two flipNMs. // -// // -// Given a set T of tets surrounding edge ab. The premise is that ab can not // -// be removed by a flipNM. This routine attempts to remove ab by two flipNMs,// -// i.e., first find and flip an edge af (or bf) by flipNM, then flip ab by // -// flipNM. If it succeeds, two sets T(ab) and T(af) of tets are replaced by // -// a new set T' and both ab and af are not edges in T' anymore. // +// transfernodes() Read the vertices from the input (tetgenio). // // // -// 'abtetlist' contains n tets sharing ab. Imaging that ab is perpendicular // -// to the screen, such that a lies in front of and b lies behind it. Let the // -// projections of the n apexes on the screen in clockwise order are: p_0,...,// -// p_n-1, respectively. So the list of tets are: [0]abp_0p_n-1, [1]abp_1p_0, // -// ..., [n-1]abp_n-1p_n-2, respectively. // -// // -// The principle of the approach is: for a face a.b.p_0, check if edge b.p_0 // -// is of type N32 (or N44). If it is, then try to do a flipNM on it. If the // -// flip is successful, then try to do another flipNM on a.b. If one of the // -// two flipNMs fails, restore the old tets as they have never been flipped. // -// Then try the next face a.b.p_1. The process can be looped for all faces // -// having ab. Stop if ab is removed or all faces have been visited. Note in // -// the above description only b.p_0 is considered, a.p_0 is done by swapping // -// the position of a and b. // -// // -// Similar operations have been described in [Joe,1995]. My approach checks // -// more cases for finding flips than Joe's. For instance, the cases (1)-(7) // -// of Joe only consider abf for finding a flip (T23/T32). My approach looks // -// all faces at ab for finding flips. Moreover, the flipNM can flip an edge // -// whose star may have more than 3 tets while Joe's only works on 3-tet case.// -// // -// If ab is removed, 'newtetlist' contains the new tets. Two sets 'abtetlist'// -// (n tets) and 'bftetlist' (n1 tets) have been replaced. The number of new // -// tets can be calculated by follows: the 1st flip transforms n1 tets into // -// (n1 - 2) * 2 new tets, however,one of the new tets goes into the new link // -// of ab, i.e., the reduced tet number in Star(ab) is n - 1; the 2nd flip // -// transforms n - 1 tets into (n - 3) * 2 new tets. Hence the number of new // -// tets are: m = ((n1 - 2) * 2 - 1) + (n - 3) * 2. The old tets are NOT del-// -// eted. The caller has the right to delete them or reverse the operation. // +// Transferring all points from input ('in->pointlist') to TetGen's 'points'.// +// All points are indexed (the first point index is 'in->firstnumber'). Each // +// point's type is initialized as UNUSEDVERTEX. The bounding box (xmax, xmin,// +// ...) and the diameter (longest) of the point set are calculated. // // // /////////////////////////////////////////////////////////////////////////////// -bool tetgenmesh::removeedgebycombNM(REAL *key, int n, triface *abtetlist, - int *n1, triface *bftetlist, triface *newtetlist, queue *flipque) +void tetgenmesh::transfernodes() { - triface tmpabtetlist[21]; - triface newfront, oldfront, adjfront; - face checksh; - point pa, pb, p[21]; - 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. + point pointloop; + REAL x, y, z, w; + int coordindex; + int attribindex; + int mtrindex; + int i, j; - // Maximal 20 tets in Star(ab). - assert(n < 20); // n <= b->maxflipedgelinksize + if (b->psc) { + assert(in->pointparamlist != NULL); + } - // 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. - twice = 0; - do { - // Two points a and b are fixed. - pa = org(abtetlist[0]); - pb = dest(abtetlist[0]); - // The points p_0, ..., p_n-1 are permuted in the following loop. - for (i = 0; i < n; i++) { - // Get the n points for the current configuration. - for (j = 0; j < n; j++) { - p[j] = apex(abtetlist[(i + j) % n]); - } - // Check if b.p_0 is of type N32 or N44. - ori = orient3d(pb, p[0], p[1], p[n - 1]); - if ((ori > 0) && (key != (REAL *) NULL)) { - // b.p_0 is not N32. However, it is possible that the tet b.p_0.p_1. - // p_n-1 has worse quality value than the key. In such case, also - // try to flip b.p_0. - 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]; - enextself(bftetlist[0]);// go to edge b.p_0. - adjustedgering(bftetlist[0], CW); // edge p_0.b. - assert(apex(bftetlist[0]) == pa); - // Form Star(b.p_0). - doflip = true; - *n1 = 0; - do { - // Is the list full? - if (*n1 == 20) break; - if (checksubfaces) { - // Stop if a subface appears. - tspivot(bftetlist[*n1], checksh); - if (checksh.sh != dummysh) { - doflip = false; break; - } - } - // 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; - } - (*n1)++; - } while (apex(bftetlist[*n1]) != pa); - // 2 < n1 <= b->maxflipedgelinksize. - if (doflip) { - success = false; - tmpkey = -1.0; // = acos(pi). - if (key != (REAL *) NULL) tmpkey = *key; - m = 0; - if (*n1 == 3) { - // Three tets case. Try flip32. - success = removeedgebyflip32(&tmpkey,bftetlist,newtetlist,flipque); - m = 2; - } else if ((*n1 > 3) && (*n1 <= b->maxflipedgelinksize)) { - // Four or more tets case. Try flipNM. - success = removeedgebytranNM(&tmpkey, *n1, bftetlist, newtetlist, - p[1], p[n - 1], flipque); - // If success, the number of new tets. - m = ((*n1) - 2) * 2; - } else { - if (b->verbose > 1) { - printf(" !! Unhandled case: n1 = %d.\n", *n1); - } - } - if (success) { - // b.p_0 is flipped. The link of ab is reduced (by 1), i.e., p_0 - // is not on the link of ab. Two old tets a.b.p_0.p_n-1 and - // a.b.p_1.p_0 have been removed from the Star(ab) and one new - // tet t = a.b.p_1.p_n-1 belongs to Star(ab). - // Find t in the 'newtetlist' and remove it from the list. - setpointmark(pa, -pointmark(pa) - 1); - setpointmark(pb, -pointmark(pb) - 1); - assert(m > 0); - for (j = 0; j < m; j++) { - tmpabtetlist[0] = newtetlist[j]; - // Does it has ab? - count = 0; - for (k = 0; k < 4; k++) { - if (pointmark((point)(tmpabtetlist[0].tet[4+k])) < 0) count++; - } - if (count == 2) { - // It is. Adjust t to be the edge ab. - for (tmpabtetlist[0].loc = 0; tmpabtetlist[0].loc < 4; - tmpabtetlist[0].loc++) { - if ((oppo(tmpabtetlist[0]) != pa) && - (oppo(tmpabtetlist[0]) != pb)) break; - } - // The face of t must contain ab. - assert(tmpabtetlist[0].loc < 4); - findedge(&(tmpabtetlist[0]), pa, pb); - break; - } - } - assert(j < m); // The tet must exist. - // Remove t from list. Fill t's position by the last tet. - newtetlist[j] = newtetlist[m - 1]; - setpointmark(pa, -(pointmark(pa) + 1)); - setpointmark(pb, -(pointmark(pb) + 1)); - // Create the temporary Star(ab) for the next flipNM. - adjustedgering(tmpabtetlist[0], CCW); - if (org(tmpabtetlist[0]) != pa) { - fnextself(tmpabtetlist[0]); - esymself(tmpabtetlist[0]); - } -#ifdef SELF_CHECK - // Make sure current edge is a->b. - assert(org(tmpabtetlist[0]) == pa); - assert(dest(tmpabtetlist[0]) == pb); - assert(apex(tmpabtetlist[0]) == p[n - 1]); - assert(oppo(tmpabtetlist[0]) == p[1]); -#endif // SELF_CHECK - // There are n - 2 left temporary tets. - for (j = 1; j < n - 1; j++) { - maketetrahedron(&(tmpabtetlist[j])); - setorg(tmpabtetlist[j], pa); - setdest(tmpabtetlist[j], pb); - setapex(tmpabtetlist[j], p[j]); - setoppo(tmpabtetlist[j], p[j + 1]); - } - // Transfer the element attributes. - for (j = 0; j < in->numberoftetrahedronattributes; j++) { - attrib = elemattribute(abtetlist[0].tet, j); - for (k = 0; k < n - 1; k++) { - setelemattribute(tmpabtetlist[k].tet, j, attrib); - } - } - // Transfer the volume constraints. - if (b->varvolume && !b->refine) { - volume = volumebound(abtetlist[0].tet); - for (k = 0; k < n - 1; k++) { - setvolumebound(tmpabtetlist[k].tet, volume); - } - } - // Glue n - 1 internal faces of Star(ab). - for (j = 0; j < n - 1; j++) { - fnext(tmpabtetlist[j], newfront); - bond(newfront, tmpabtetlist[(j + 1) % (n - 1)]); // a.b.p_j+1 - } - // Substitute the old tets with the new tets by connecting the - // new tets to the adjacent tets in the mesh. There are (n-2) - // * 2 (outer) faces of the new tets need to be operated. - // Note that the old tets still have the pointers to their - // adjacent tets in the mesh. These pointers can be re-used - // to inverse the substitution. - for (j = 2; j < n; j++) { - // Get an old tet: [j]a.b.p_j.p_j-1, (j > 1). - oldfront = abtetlist[(i + j) % n]; - esymself(oldfront); - enextfnextself(oldfront); - // Get an adjacent tet at face: [j]a.p_j.p_j-1. - sym(oldfront, adjfront); // adjfront may be dummy. - // Get the corresponding face from the new tets. - // j >= 2. - enext2fnext(tmpabtetlist[j - 1], newfront); // a.p_j.p_j-1 - bond(newfront, adjfront); - if (checksubfaces) { - tspivot(oldfront, checksh); - if (checksh.sh != dummysh) { - tsbond(newfront, checksh); - } - } - } - for (j = 2; j < n; j++) { - // Get an old tet: [j]a.b.p_j.p_j-1, (j > 2). - oldfront = abtetlist[(i + j) % n]; - esymself(oldfront); - enext2fnextself(oldfront); - // Get an adjacent tet at face: [j]b.p_j.p_j-1. - sym(oldfront, adjfront); // adjfront may be dummy. - // Get the corresponding face from the new tets. - // j >= 2. - enextfnext(tmpabtetlist[j - 1], newfront); // b.p_j.p_j-1 - bond(newfront, adjfront); - if (checksubfaces) { - tspivot(oldfront, checksh); - if (checksh.sh != dummysh) { - tsbond(newfront, checksh); - } - } - } - // Adjust the faces in the temporary new tets at ab for - // recursively processing on the n-1 tets. - for (j = 0; j < n - 1; j++) { - fnextself(tmpabtetlist[j]); - } - 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); - } else { // assert((n - 1) >= 4); - success = removeedgebytranNM(&tmpkey2, n - 1, tmpabtetlist, - &(newtetlist[m - 1]), NULL, NULL, flipque); - } - // No matter it was success or not, delete the temporary tets. - for (j = 0; j < n - 1; j++) { - tetrahedrondealloc(tmpabtetlist[j].tet); - } - if (success) { - // The new configuration is good. - // Do not delete the old tets. - // for (j = 0; j < n; j++) { - // tetrahedrondealloc(abtetlist[j].tet); - // } - // Return the bigger dihedral in the two sets of new tets. - if (key != (REAL *) NULL) { - *key = tmpkey2 < tmpkey ? tmpkey2 : tmpkey; - } - 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); - enextfnextself(oldfront); // [0]a.p_0.p_n-1, [j]a.p_j.p_j-1. - sym(oldfront, adjfront); // adjfront may be dummy. - bond(oldfront, adjfront); - if (checksubfaces) { - tspivot(oldfront, checksh); - if (checksh.sh != dummysh) { - tsbond(oldfront, checksh); - } - } - } - for (j = 0; j < n; j++) { - oldfront = abtetlist[(i + j) % n]; - esymself(oldfront); - enext2fnextself(oldfront); // [0]b.p_0.p_n-1, [j]b.p_j.p_j-1. - sym(oldfront, adjfront); // adjfront may be dummy - bond(oldfront, adjfront); - if (checksubfaces) { - tspivot(oldfront, checksh); - if (checksh.sh != dummysh) { - tsbond(oldfront, checksh); - } - } - } - // Substitute back the old tets of the first flip. - for (j = 0; j < *n1; j++) { - oldfront = bftetlist[j]; - esymself(oldfront); - enextfnextself(oldfront); - sym(oldfront, adjfront); // adjfront may be dummy. - bond(oldfront, adjfront); - if (checksubfaces) { - tspivot(oldfront, checksh); - if (checksh.sh != dummysh) { - tsbond(oldfront, checksh); - } - } - } - for (j = 0; j < *n1; j++) { - oldfront = bftetlist[j]; - esymself(oldfront); - enext2fnextself(oldfront); // [0]b.p_0.p_n-1, [j]b.p_j.p_j-1. - sym(oldfront, adjfront); // adjfront may be dummy - bond(oldfront, adjfront); - if (checksubfaces) { - tspivot(oldfront, checksh); - if (checksh.sh != dummysh) { - tsbond(oldfront, checksh); - } - } - } - // Delete the new tets of the first flip. Note that one new - // tet has already been removed from the list. - for (j = 0; j < m - 1; j++) { - tetrahedrondealloc(newtetlist[j].tet); - } - } // if (success) - } // if (success) - } // if (doflip) - } // if (ori <= 0.0) - } // for (i = 0; i < n; i++) - // Inverse a and b and the tets configuration. - for (i = 0; i < n; i++) newtetlist[i] = abtetlist[i]; - for (i = 0; i < n; i++) { - oldfront = newtetlist[n - i - 1]; - esymself(oldfront); - fnextself(oldfront); - abtetlist[i] = oldfront; + // Read the points. + coordindex = 0; + attribindex = 0; + mtrindex = 0; + for (i = 0; i < in->numberofpoints; i++) { + makepoint(&pointloop, UNUSEDVERTEX); + // Read the point coordinates. + x = pointloop[0] = in->pointlist[coordindex++]; + y = pointloop[1] = in->pointlist[coordindex++]; + z = pointloop[2] = in->pointlist[coordindex++]; + // Read the point attributes. (Including point weights.) + for (j = 0; j < in->numberofpointattributes; j++) { + pointloop[3 + j] = in->pointattributelist[attribindex++]; + } + // Read the point metric tensor. + for (j = 0; j < in->numberofpointmtrs; j++) { + pointloop[pointmtrindex + j] = in->pointmtrlist[mtrindex++]; + } + if (b->weighted) { // -w option + if (in->numberofpointattributes > 0) { + // The first point attribute is its weight. + //w = in->pointattributelist[in->numberofpointattributes * i]; + w = pointloop[3]; + } else { + // No given weight available. Default choose the maximum + // absolute value among its coordinates. + w = fabs(x); + if (w < fabs(y)) w = fabs(y); + if (w < fabs(z)) w = fabs(z); + } + if (b->weighted_param == 0) { + pointloop[3] = x * x + y * y + z * z - w; // Weighted DT. + } else { // -w1 option + pointloop[3] = w; // Regular tetrahedralization. + } + } + // Determine the smallest and largest 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; + } + if (b->psc) { + // Read the geometry parameters. + setpointgeomuv(pointloop, 0, in->pointparamlist[i].uv[0]); + setpointgeomuv(pointloop, 1, in->pointparamlist[i].uv[1]); + setpointgeomtag(pointloop, in->pointparamlist[i].tag); + if (in->pointparamlist[i].type == 0) { + setpointtype(pointloop, RIDGEVERTEX); + } else if (in->pointparamlist[i].type == 1) { + setpointtype(pointloop, FREESEGVERTEX); + } else if (in->pointparamlist[i].type == 2) { + setpointtype(pointloop, FREEFACETVERTEX); + } else if (in->pointparamlist[i].type == 3) { + setpointtype(pointloop, FREEVOLVERTEX); + } } - twice++; - } while (twice < 2); + } + + // '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(this, 3); + } - return false; + // Two identical points are distinguished by 'lengthlimit'. + if (b->minedgelength == 0.0) { + b->minedgelength = longest * b->epsilon; + } } /////////////////////////////////////////////////////////////////////////////// // // -// splittetrahedron() Insert a point into a tetrahedron, split it into // -// four tetrahedra. // -// // -// The tetrahedron is given by 'splittet'. Let it is abcd. The inserting // -// point 'newpoint' v should lie strictly inside abcd. // +// hilbert_init() Initialize the Gray code permutation table. // // // -// Splitting a tetrahedron is to shrink abcd to abcv, and create three new // -// tetrahedra badv, cbdv, and acdv. // +// The table 'transgc' has 8 x 3 x 8 entries. It contains all possible Gray // +// code sequences traveled by the 1st order Hilbert curve in 3 dimensions. // +// The first column is the Gray code of the entry point of the curve, and // +// the second column is the direction (0, 1, or 2, 0 means the x-axis) where // +// the exit point of curve lies. // // // -// On completion, 'splittet' returns abcv. If 'flipqueue' is not NULL, it // -// contains all possibly non-locally Delaunay faces. // +// The table 'tsb1mod3' contains the numbers of trailing set '1' bits of the // +// indices from 0 to 7, modulo by '3'. The code for generating this table is // +// from: http://graphics.stanford.edu/~seander/bithacks.html. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::splittetrahedron(point newpoint, triface* splittet, - queue* flipqueue) +void tetgenmesh::hilbert_init(int n) { - triface oldabd, oldbcd, oldcad; // Old configuration. - triface abdcasing, bcdcasing, cadcasing; - face abdsh, bcdsh, cadsh; - triface abcv, badv, cbdv, acdv; // New configuration. - triface worktet; - face abseg, bcseg, caseg; - face adseg, bdseg, cdseg; - point pa, pb, pc, pd; - REAL attrib, volume; + int gc[8], N, mask, travel_bit; + int e, d, f, k, g; + int v, c; int i; - abcv = *splittet; - abcv.ver = 0; - // Set the changed vertices and new tetrahedron. - pa = org(abcv); - pb = dest(abcv); - pc = apex(abcv); - pd = oppo(abcv); + N = (n == 2) ? 4 : 8; + mask = (n == 2) ? 3 : 7; - if (b->verbose > 1) { - printf(" Inserting point %d in tetrahedron (%d, %d, %d, %d).\n", - pointmark(newpoint), pointmark(pa), pointmark(pb), pointmark(pc), - pointmark(pd)); - } - - fnext(abcv, oldabd); - enextfnext(abcv, oldbcd); - enext2fnext(abcv, oldcad); - sym(oldabd, abdcasing); - sym(oldbcd, bcdcasing); - sym(oldcad, cadcasing); - maketetrahedron(&badv); - maketetrahedron(&cbdv); - maketetrahedron(&acdv); - - // Set 'badv' vertices. - setorg (badv, pb); - setdest(badv, pa); - setapex(badv, pd); - setoppo(badv, newpoint); - // Set 'cbdv' vertices. - setorg (cbdv, pc); - setdest(cbdv, pb); - setapex(cbdv, pd); - setoppo(cbdv, newpoint); - // Set 'acdv' vertices. - setorg (acdv, pa); - setdest(acdv, pc); - setapex(acdv, pd); - setoppo(acdv, newpoint); - // Set 'abcv' vertices - setoppo(abcv, newpoint); - - // Set the element attributes of the new tetrahedra. - for (i = 0; i < in->numberoftetrahedronattributes; i++) { - attrib = elemattribute(abcv.tet, i); - setelemattribute(badv.tet, i, attrib); - setelemattribute(cbdv.tet, i, attrib); - setelemattribute(acdv.tet, i, attrib); - } - // Set the volume constraint of the new tetrahedra. - if (b->varvolume) { - volume = volumebound(abcv.tet); - setvolumebound(badv.tet, volume); - setvolumebound(cbdv.tet, volume); - setvolumebound(acdv.tet, volume); - } - - // Bond the new triangles to the surrounding tetrahedron. - bond(badv, abdcasing); - bond(cbdv, bcdcasing); - bond(acdv, cadcasing); - // There may exist subfaces need to be bonded to the new tetrahedra. - if (checksubfaces) { - tspivot(oldabd, abdsh); - if (abdsh.sh != dummysh) { - tsdissolve(oldabd); - tsbond(badv, abdsh); - } - tspivot(oldbcd, bcdsh); - if (bcdsh.sh != dummysh) { - tsdissolve(oldbcd); - tsbond(cbdv, bcdsh); - } - tspivot(oldcad, cadsh); - if (cadsh.sh != dummysh) { - tsdissolve(oldcad); - tsbond(acdv, cadsh); - } - } else if (checksubsegs) { - tsspivot1(abcv, abseg); - if (abseg.sh != dummysh) { - tssbond1(badv, abseg); - } - enext(abcv, worktet); - tsspivot1(worktet, bcseg); - if (bcseg.sh != dummysh) { - tssbond1(cbdv, bcseg); - } - enext2(abcv, worktet); - tsspivot1(worktet, caseg); - if (caseg.sh != dummysh) { - tssbond1(acdv, caseg); - } - fnext(abcv, worktet); - enext2self(worktet); - tsspivot1(worktet, adseg); - if (adseg.sh != dummysh) { - tssdissolve1(worktet); - enext(badv, worktet); - tssbond1(worktet, adseg); - enext2(acdv, worktet); - tssbond1(worktet, adseg); - } - enextfnext(abcv, worktet); - enext2self(worktet); - tsspivot1(worktet, bdseg); - if (bdseg.sh != dummysh) { - tssdissolve1(worktet); - enext(cbdv, worktet); - tssbond1(worktet, bdseg); - enext2(badv, worktet); - tssbond1(worktet, bdseg); - } - enext2fnext(abcv, worktet); - enext2self(worktet); - tsspivot1(worktet, cdseg); - if (cdseg.sh != dummysh) { - tssdissolve1(worktet); - enext(acdv, worktet); - tssbond1(worktet, cdseg); - enext2(cbdv, worktet); - tssbond1(worktet, cdseg); - } - } - badv.loc = 3; - cbdv.loc = 2; - bond(badv, cbdv); - cbdv.loc = 3; - acdv.loc = 2; - bond(cbdv, acdv); - acdv.loc = 3; - badv.loc = 2; - bond(acdv, badv); - badv.loc = 1; - bond(badv, oldabd); - cbdv.loc = 1; - bond(cbdv, oldbcd); - acdv.loc = 1; - bond(acdv, oldcad); - - badv.loc = 0; - cbdv.loc = 0; - acdv.loc = 0; - if (b->verbose > 3) { - printf(" Updating abcv "); - printtet(&abcv); - printf(" Creating badv "); - printtet(&badv); - printf(" Creating cbdv "); - printtet(&cbdv); - printf(" Creating acdv "); - printtet(&acdv); - } - - if (flipqueue != (queue *) NULL) { - enqueueflipface(abcv, flipqueue); - enqueueflipface(badv, flipqueue); - enqueueflipface(cbdv, flipqueue); - enqueueflipface(acdv, flipqueue); - } - - // Save a handle for quick point location. - recenttet = abcv; - // Set the return handle be abcv. - *splittet = abcv; + // Generate the Gray code sequence. + for (i = 0; i < N; i++) { + gc[i] = i ^ (i >> 1); + } + + for (e = 0; e < N; e++) { + for (d = 0; d < n; d++) { + // Calculate the end point (f). + f = e ^ (1 << d); // Toggle the d-th bit of 'e'. + // travel_bit = 2**p, the bit we want to travel. + travel_bit = e ^ f; + for (i = 0; i < N; i++) { + // // Rotate gc[i] left by (p + 1) % n bits. + k = gc[i] * (travel_bit * 2); + g = ((k | (k / N)) & mask); + // Calculate the permuted Gray code by xor with the start point (e). + transgc[e][d][i] = (g ^ e); + } + assert(transgc[e][d][0] == e); + assert(transgc[e][d][N - 1] == f); + } // d + } // e + + // Count the consecutive '1' bits (trailing) on the right. + tsb1mod3[0] = 0; + for (i = 1; i < N; i++) { + v = ~i; // Count the 0s. + v = (v ^ (v - 1)) >> 1; // Set v's trailing 0s to 1s and zero rest + for (c = 0; v; c++) { + v >>= 1; + } + tsb1mod3[i] = c % n; + } } /////////////////////////////////////////////////////////////////////////////// // // -// splittetface() Insert a point on a face of a mesh. // -// // -// 'splittet' is the splitting face. Let it is abcd, where abc is the face // -// will be split. If abc is not a hull face, abce is the tetrahedron at the // -// opposite of d. // -// // -// To split face abc by a point v is to shrink the tetrahedra abcd to abvd, // -// create two new tetrahedra bcvd, cavd. If abc is not a hull face, shrink // -// the tetrahedra bace to bave, create two new tetrahedra cbve, acve. // -// // -// If abc is a subface, it is split into three subfaces simultaneously by // -// calling routine splitsubface(), hence, abv, bcv, cav. The edge rings of // -// the split subfaces have the same orientation as abc's. // -// // -// On completion, 'splittet' returns abvd. If 'flipqueue' is not NULL, it // -// contains all possibly non-locally Delaunay faces. // +// hilbert_sort3() Sort points using the 3d Hilbert curve. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::splittetface(point newpoint, triface* splittet, - queue* flipqueue) +int tetgenmesh::hilbert_split(point* vertexarray,int arraysize,int gc0,int gc1, + REAL bxmin, REAL bxmax, REAL bymin, REAL bymax, + REAL bzmin, REAL bzmax) { - triface abcd, bace; // Old configuration. - triface oldbcd, oldcad, oldace, oldcbe; - triface bcdcasing, cadcasing, acecasing, cbecasing; - face abcsh, bcdsh, cadsh, acesh, cbesh; - triface abvd, bcvd, cavd, bave, cbve, acve; // New configuration. - triface worktet; - face bcseg, caseg; - face adseg, bdseg, cdseg; - face aeseg, beseg, ceseg; - point pa, pb, pc, pd, pe; - REAL attrib, volume; - bool mirrorflag; - int i; - - abcd = *splittet; - // abcd.ver = 0; // Adjust to be CCW edge ring. - adjustedgering(abcd, CCW); - pa = org(abcd); - pb = dest(abcd); - pc = apex(abcd); - pd = oppo(abcd); - pe = (point) NULL; // avoid a compile warning. - // Is there a second tetrahderon? - mirrorflag = issymexist(&abcd); - if (mirrorflag) { - // This is an interior face. - sym(abcd, bace); - findedge(&bace, dest(abcd), org(abcd)); - pe = oppo(bace); - } - if (checksubfaces) { - // Is there a subface need to be split together? - tspivot(abcd, abcsh); - if (abcsh.sh != dummysh) { - // Exists! Keep the edge ab of both handles be the same. - findedge(&abcsh, org(abcd), dest(abcd)); - } - } - - if (b->verbose > 1) { - printf(" Inserting point %d on face (%d, %d, %d).\n", pointmark(newpoint), - pointmark(pa), pointmark(pb), pointmark(pc)); - } - - // Save the old configuration at faces bcd and cad. - enextfnext(abcd, oldbcd); - enext2fnext(abcd, oldcad); - sym(oldbcd, bcdcasing); - sym(oldcad, cadcasing); - // Create two new tetrahedra. - maketetrahedron(&bcvd); - maketetrahedron(&cavd); - if (mirrorflag) { - // Save the old configuration at faces bce and cae. - enextfnext(bace, oldace); - enext2fnext(bace, oldcbe); - sym(oldace, acecasing); - sym(oldcbe, cbecasing); - // Create two new tetrahedra. - maketetrahedron(&acve); - maketetrahedron(&cbve); - } else { - // Splitting a boundary face increases the number of boundary faces. - hullsize += 2; - } - - // Set vertices to the changed tetrahedron and new tetrahedra. - abvd = abcd; // Update 'abcd' to 'abvd'. - setapex(abvd, newpoint); - setorg (bcvd, pb); // Set 'bcvd'. - setdest(bcvd, pc); - setapex(bcvd, newpoint); - setoppo(bcvd, pd); - setorg (cavd, pc); // Set 'cavd'. - setdest(cavd, pa); - setapex(cavd, newpoint); - setoppo(cavd, pd); - // Set the element attributes of the new tetrahedra. - for (i = 0; i < in->numberoftetrahedronattributes; i++) { - attrib = elemattribute(abvd.tet, i); - setelemattribute(bcvd.tet, i, attrib); - setelemattribute(cavd.tet, i, attrib); - } - if (b->varvolume) { - // Set the area constraint of the new tetrahedra. - volume = volumebound(abvd.tet); - setvolumebound(bcvd.tet, volume); - setvolumebound(cavd.tet, volume); - } - if (mirrorflag) { - bave = bace; // Update 'bace' to 'bave'. - setapex(bave, newpoint); - setorg (acve, pa); // Set 'acve'. - setdest(acve, pc); - setapex(acve, newpoint); - setoppo(acve, pe); - setorg (cbve, pc); // Set 'cbve'. - setdest(cbve, pb); - setapex(cbve, newpoint); - setoppo(cbve, pe); - // Set the element attributes of the new tetrahedra. - for (i = 0; i < in->numberoftetrahedronattributes; i++) { - attrib = elemattribute(bave.tet, i); - setelemattribute(acve.tet, i, attrib); - setelemattribute(cbve.tet, i, attrib); - } - if (b->varvolume) { - // Set the area constraint of the new tetrahedra. - volume = volumebound(bave.tet); - setvolumebound(acve.tet, volume); - setvolumebound(cbve.tet, volume); - } - } - - // Bond the new tetrahedra to the surrounding tetrahedra. - bcvd.loc = 1; - bond(bcvd, bcdcasing); - cavd.loc = 1; - bond(cavd, cadcasing); - bcvd.loc = 3; - bond(bcvd, oldbcd); - cavd.loc = 2; - bond(cavd, oldcad); - bcvd.loc = 2; - cavd.loc = 3; - bond(bcvd, cavd); - if (mirrorflag) { - acve.loc = 1; - bond(acve, acecasing); - cbve.loc = 1; - bond(cbve, cbecasing); - acve.loc = 3; - bond(acve, oldace); - cbve.loc = 2; - bond(cbve, oldcbe); - acve.loc = 2; - cbve.loc = 3; - bond(acve, cbve); - // Bond two new coplanar facets. - bcvd.loc = 0; - cbve.loc = 0; - bond(bcvd, cbve); - cavd.loc = 0; - acve.loc = 0; - bond(cavd, acve); - } - - // There may exist subface needed to be bonded to the new tetrahedra. - if (checksubfaces) { - tspivot(oldbcd, bcdsh); - if (bcdsh.sh != dummysh) { - tsdissolve(oldbcd); - bcvd.loc = 1; - tsbond(bcvd, bcdsh); - } - tspivot(oldcad, cadsh); - if (cadsh.sh != dummysh) { - tsdissolve(oldcad); - cavd.loc = 1; - tsbond(cavd, cadsh); - } - if (mirrorflag) { - tspivot(oldace, acesh); - if (acesh.sh != dummysh) { - tsdissolve(oldace); - acve.loc = 1; - tsbond(acve, acesh); - } - tspivot(oldcbe, cbesh); - if (cbesh.sh != dummysh) { - tsdissolve(oldcbe); - cbve.loc = 1; - tsbond(cbve, cbesh); - } - } - // Is there a subface needs to be split together? - if (abcsh.sh != dummysh) { - // Split this subface 'abc' into three i.e, abv, bcv, cav. - splitsubface(newpoint, &abcsh, (queue *) NULL); - } - } else if (checksubsegs) { - // abvd.loc = abvd.ver = 0; - bcvd.loc = bcvd.ver = 0; - cavd.loc = cavd.ver = 0; - if (mirrorflag) { - // bave.loc = bave.ver = 0; - cbve.loc = cbve.ver = 0; - acve.loc = acve.ver = 0; - } - enext(abvd, worktet); - tsspivot1(worktet, bcseg); - if (bcseg.sh != dummysh) { - tssdissolve1(worktet); - tssbond1(bcvd, bcseg); - if (mirrorflag) { - enext2(bave, worktet); - tssdissolve1(worktet); - tssbond1(cbve, bcseg); - } - } - enext2(abvd, worktet); - tsspivot1(worktet, caseg); - if (caseg.sh != dummysh) { - tssdissolve1(worktet); - tssbond1(cavd, caseg); - if (mirrorflag) { - enext(bave, worktet); - tssdissolve1(worktet); - tssbond1(acve, caseg); - } - } - fnext(abvd, worktet); - enext2self(worktet); - tsspivot1(worktet, adseg); - if (adseg.sh != dummysh) { - fnext(cavd, worktet); - enextself(worktet); - tssbond1(worktet, adseg); - } - fnext(abvd, worktet); - enextself(worktet); - tsspivot1(worktet, bdseg); - if (bdseg.sh != dummysh) { - fnext(bcvd, worktet); - enext2self(worktet); - tssbond1(worktet, bdseg); - } - enextfnext(abvd, worktet); - enextself(worktet); - tsspivot1(worktet, cdseg); - if (cdseg.sh != dummysh) { - tssdissolve1(worktet); - fnext(bcvd, worktet); - enextself(worktet); - tssbond1(worktet, cdseg); - fnext(cavd, worktet); - enext2self(worktet); - tssbond1(worktet, cdseg); - } - if (mirrorflag) { - fnext(bave, worktet); - enextself(worktet); - tsspivot1(worktet, aeseg); - if (aeseg.sh != dummysh) { - fnext(acve, worktet); - enext2self(worktet); - tssbond1(worktet, aeseg); - } - fnext(bave, worktet); - enext2self(worktet); - tsspivot1(worktet, beseg); - if (beseg.sh != dummysh) { - fnext(cbve, worktet); - enextself(worktet); - tssbond1(worktet, beseg); - } - enextfnext(bave, worktet); - enextself(worktet); - tsspivot1(worktet, ceseg); - if (ceseg.sh != dummysh) { - tssdissolve1(worktet); - fnext(cbve, worktet); - enext2self(worktet); - tssbond1(worktet, ceseg); - fnext(acve, worktet); - enextself(worktet); - tssbond1(worktet, ceseg); - } - } - } - - // Save a handle for quick point location. - recenttet = abvd; - // Set the return handle be abvd. - *splittet = abvd; - - bcvd.loc = 0; - cavd.loc = 0; - if (mirrorflag) { - cbve.loc = 0; - acve.loc = 0; - } - if (b->verbose > 3) { - printf(" Updating abvd "); - printtet(&abvd); - printf(" Creating bcvd "); - printtet(&bcvd); - printf(" Creating cavd "); - printtet(&cavd); - if (mirrorflag) { - printf(" Updating bave "); - printtet(&bave); - printf(" Creating cbve "); - printtet(&cbve); - printf(" Creating acve "); - printtet(&acve); - } - } - - if (flipqueue != (queue *) NULL) { - fnextself(abvd); - enqueueflipface(abvd, flipqueue); - fnextself(bcvd); - enqueueflipface(bcvd, flipqueue); - fnextself(cavd); - enqueueflipface(cavd, flipqueue); - if (mirrorflag) { - fnextself(bave); - enqueueflipface(bave, flipqueue); - fnextself(cbve); - enqueueflipface(cbve, flipqueue); - fnextself(acve); - enqueueflipface(acve, flipqueue); - } - } -} + point swapvert; + int axis, d; + REAL split; + int i, j; -/////////////////////////////////////////////////////////////////////////////// -// // -// splitsubface() Insert a point on a subface, split it into three. // -// // -// The subface is 'splitface'. Let it is abc. The inserting point 'newpoint'// -// v should lie inside abc. If the neighbor tetrahedra of abc exist, i.e., // -// abcd and bace, they should have been split by routine splittetface() // -// before calling this routine, so the connection between the new tetrahedra // -// and new subfaces can be correctly set. // -// // -// To split subface abc by point v is to shrink abc to abv, create two new // -// subfaces bcv and cav. Set the connection between updated and new created // -// subfaces. If there is a subsegment at edge bc or ca, connection of new // -// subface (bcv or cav) to its casing subfaces is a face link, 'casingin' is // -// the predecessor and 'casingout' is the successor. It is important to keep // -// the orientations of the edge rings of the updated and created subfaces be // -// the same as abc's. So they have the same orientation as other subfaces of // -// this facet with respect to the lift point of this facet. // -// // -// On completion, 'splitface' returns abv. If 'flipqueue' is not NULL, it // -// returns all possibly non-Delaunay edges. // -// // -/////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::splitsubface(point newpoint, face* splitface, - queue* flipqueue) -{ - triface abvd, bcvd, cavd, bave, cbve, acve; - face abc, oldbc, oldca, bc, ca, spinsh; - face bccasin, bccasout, cacasin, cacasout; - face abv, bcv, cav; - point pa, pb, pc; - - abc = *splitface; - // The newly created subfaces will have the same edge ring as abc. - adjustedgering(abc, CCW); - pa = sorg(abc); - pb = sdest(abc); - pc = sapex(abc); + // Find the current splitting axis. 'axis' is a value 0, or 1, or 2, which + // correspoding to x-, or y- or z-axis. + axis = (gc0 ^ gc1) >> 1; - if (b->verbose > 1) { - printf(" Inserting point %d on subface (%d, %d, %d).\n", - pointmark(newpoint), pointmark(pa), pointmark(pb), pointmark(pc)); - } - - // Save the old configuration at edge bc and ca. Subsegments may appear - // at both sides, save the face links and dissolve them. - senext(abc, oldbc); - senext2(abc, oldca); - spivot(oldbc, bccasout); - sspivot(oldbc, bc); - if (bc.sh != dummysh) { - if (bccasout.sh != dummysh) { - // 'oldbc' is not self-bonded. - spinsh = bccasout; - do { - bccasin = spinsh; - spivotself(spinsh); - } while (spinsh.sh != oldbc.sh); - } - ssdissolve(oldbc); - } - spivot(oldca, cacasout); - sspivot(oldca, ca); - if (ca.sh != dummysh) { - if (cacasout.sh != dummysh) { - // 'oldca' is not self-bonded. - spinsh = cacasout; - do { - cacasin = spinsh; - spivotself(spinsh); - } while (spinsh.sh != oldca.sh); - } - ssdissolve(oldca); - } - // Create two new subfaces. - makeshellface(subfaces, &bcv); - makeshellface(subfaces, &cav); - - // Set the vertices of changed and new subfaces. - abv = abc; // Update 'abc' to 'abv'. - setsapex(abv, newpoint); - setsorg(bcv, pb); // Set 'bcv'. - setsdest(bcv, pc); - setsapex(bcv, newpoint); - setsorg(cav, pc); // Set 'cav'. - setsdest(cav, pa); - setsapex(cav, newpoint); - if (b->quality && varconstraint) { - // Copy yhr area bound into the new subfaces. - setareabound(bcv, areabound(abv)); - setareabound(cav, areabound(abv)); - } - // Copy the boundary mark into the new subfaces. - setshellmark(bcv, shellmark(abv)); - setshellmark(cav, shellmark(abv)); - // Copy the subface type into the new subfaces. - setshelltype(bcv, shelltype(abv)); - setshelltype(cav, shelltype(abv)); - if (checkpbcs) { - // Copy the pbcgroup into the new subfaces. - setshellpbcgroup(bcv, shellpbcgroup(abv)); - setshellpbcgroup(cav, shellpbcgroup(abv)); - } - // Bond the new subfaces to the surrounding subfaces. - if (bc.sh != dummysh) { - if (bccasout.sh != dummysh) { - sbond1(bccasin, bcv); - sbond1(bcv, bccasout); - } else { - // Bond 'bcv' to itsself. - sdissolve(bcv); // sbond(bcv, bcv); - } - ssbond(bcv, bc); - } else { - sbond(bcv, bccasout); + // Calulate the split position along the axis. + if (axis == 0) { + split = 0.5 * (bxmin + bxmax); + } else if (axis == 1) { + split = 0.5 * (bymin + bymax); + } else { // == 2 + split = 0.5 * (bzmin + bzmax); } - if (ca.sh != dummysh) { - if (cacasout.sh != dummysh) { - sbond1(cacasin, cav); - sbond1(cav, cacasout); - } else { - // Bond 'cav' to itself. - sdissolve(cav); // sbond(cav, cav); - } - ssbond(cav, ca); - } else { - sbond(cav, cacasout); - } - senext2self(bcv); - sbond(bcv, oldbc); - senextself(cav); - sbond(cav, oldca); - senext2self(bcv); - senextself(cav); - sbond(bcv, cav); - - // Bond the new subfaces to the new tetrahedra if they exist. - stpivot(abv, abvd); - if (abvd.tet != dummytet) { - // Get two new tetrahedra and their syms. - findedge(&abvd, sorg(abv), sdest(abv)); - enextfnext(abvd, bcvd); -#ifdef SELF_CHECK - assert(bcvd.tet != dummytet); -#endif - fnextself(bcvd); - enext2fnext(abvd, cavd); -#ifdef SELF_CHECK - assert(cavd.tet != dummytet); -#endif - fnextself(cavd); - // Bond two new subfaces to the two new tetrahedra. - tsbond(bcvd, bcv); - tsbond(cavd, cav); - } - // Set the connection at the other sides if the tetrahedra exist. - sesymself(abv); // bav - stpivot(abv, bave); - if (bave.tet != dummytet) { - sesymself(bcv); // cbv - sesymself(cav); // acv - // Get two new tetrahedra and their syms. - findedge(&bave, sorg(abv), sdest(abv)); - enextfnext(bave, acve); -#ifdef SELF_CHECK - assert(acve.tet != dummytet); -#endif - fnextself(acve); - enext2fnext(bave, cbve); -#ifdef SELF_CHECK - assert(cbve.tet != dummytet); -#endif - fnextself(cbve); - // Bond two new subfaces to the two new tetrahedra. - tsbond(acve, cav); - tsbond(cbve, bcv); - } - - bcv.shver = 0; - cav.shver = 0; - if (b->verbose > 3) { - printf(" Updating abv "); - printsh(&abv); - printf(" Creating bcv "); - printsh(&bcv); - printf(" Creating cav "); - printsh(&cav); - } - - if (flipqueue != (queue *) NULL) { - enqueueflipedge(abv, flipqueue); - enqueueflipedge(bcv, flipqueue); - enqueueflipedge(cav, flipqueue); - } - - // Set the return handle be abv. - *splitface = abv; -} -/////////////////////////////////////////////////////////////////////////////// -// // -// splittetedge() Insert a point on an edge of the mesh. // -// // -// The edge is given by 'splittet'. Assume its four corners are a, b, n1 and // -// n2, where ab is the edge will be split. Around ab may exist any number of // -// tetrahedra. For convenience, they're ordered in a sequence following the // -// right-hand rule with your thumb points from a to b. Let the vertex set of // -// these tetrahedra be {a, b, n1, n2, ..., n(i)}. NOTE the tetrahedra around // -// ab may not connect to each other (can only happen when ab is a subsegment,// -// hence some faces abn(i) are subfaces). If ab is a subsegment, abn1 must // -// be a subface. // -// // -// To split edge ab by a point v is to split all tetrahedra containing ab by // -// v. More specifically, for each such tetrahedron, an1n2b, it is shrunk to // -// an1n2v, and a new tetrahedra bn2n1v is created. If ab is a subsegment, or // -// some faces of the splitting tetrahedra are subfaces, they must be split // -// either by calling routine 'splitsubedge()'. // -// // -// On completion, 'splittet' returns avn1n2. If 'flipqueue' is not NULL, it // -// returns all faces which may become non-Delaunay after this operation. // -// // -/////////////////////////////////////////////////////////////////////////////// + // Find the direction (+1 or -1) of the axis. If 'd' is +1, the direction + // of the axis is to the positive of the axis, otherwise, it is -1. + d = ((gc0 & (1<verbose > 1) { - printf(" Inserting point %d on edge (%d, %d).\n", - pointmark(newpoint), pointmark(pa), pointmark(pb)); - } - - // Collect the tetrahedra containing the splitting edge (ab). - n1 = apex(spintet); - hitbdry = 0; - wrapcount = 1; - if (checksubfaces && abseg.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 ++; - 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; - } + // Partition the vertices into left- and right-arrays. + if (d > 0) { + do { + for (; i < arraysize; i++) { + if (vertexarray[i][axis] >= split) break; + } + for (; j >= 0; j--) { + if (vertexarray[j][axis] < split) break; + } + // Is the partition finished? + if (i == (j + 1)) break; + // Swap i-th and j-th vertices. + swapvert = vertexarray[i]; + vertexarray[i] = vertexarray[j]; + vertexarray[j] = swapvert; + // Continue patitioning the array; + } while (true); } else { - // 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); - break; - } - volume = orient3d(pb, n2, n1, newpoint); - if (volume >= 0.0) { - //printf("Internal error in splittetedge(): volume = %.12g.\n", volume); - break; - } -//#endif + do { + for (; i < arraysize; i++) { + if (vertexarray[i][axis] <= split) break; + } + for (; j >= 0; j--) { + if (vertexarray[j][axis] > split) break; + } + // Is the partition finished? + if (i == (j + 1)) break; + // Swap i-th and j-th vertices. + swapvert = vertexarray[i]; + vertexarray[i] = vertexarray[j]; + vertexarray[j] = swapvert; + // Continue patitioning the array; + } while (true); } - if (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); + return i; +} + +void tetgenmesh::hilbert_sort3(point* vertexarray, int arraysize, int e, int d, + REAL bxmin, REAL bxmax, REAL bymin, REAL bymax, + REAL bzmin, REAL bzmax, int depth) +{ + REAL x1, x2, y1, y2, z1, z2; + int p[9], w, e_w, d_w, k, ei, di; + int n = 3, mask = 7; + + p[0] = 0; + p[8] = arraysize; + + // Sort the points according to the 1st order Hilbert curve in 3d. + p[4] = hilbert_split(vertexarray, p[8], transgc[e][d][3], transgc[e][d][4], + bxmin, bxmax, bymin, bymax, bzmin, bzmax); + p[2] = hilbert_split(vertexarray, p[4], transgc[e][d][1], transgc[e][d][2], + bxmin, bxmax, bymin, bymax, bzmin, bzmax); + p[1] = hilbert_split(vertexarray, p[2], transgc[e][d][0], transgc[e][d][1], + bxmin, bxmax, bymin, bymax, bzmin, bzmax); + p[3] = hilbert_split(&(vertexarray[p[2]]), p[4] - p[2], + transgc[e][d][2], transgc[e][d][3], + bxmin, bxmax, bymin, bymax, bzmin, bzmax) + p[2]; + p[6] = hilbert_split(&(vertexarray[p[4]]), p[8] - p[4], + transgc[e][d][5], transgc[e][d][6], + bxmin, bxmax, bymin, bymax, bzmin, bzmax) + p[4]; + p[5] = hilbert_split(&(vertexarray[p[4]]), p[6] - p[4], + transgc[e][d][4], transgc[e][d][5], + bxmin, bxmax, bymin, bymax, bzmin, bzmax) + p[4]; + p[7] = hilbert_split(&(vertexarray[p[6]]), p[8] - p[6], + transgc[e][d][6], transgc[e][d][7], + bxmin, bxmax, bymin, bymax, bzmin, bzmax) + p[6]; + + if (b->hilbert_order > 0) { + // A maximum order is prescribed. + if ((depth + 1) == b->hilbert_order) { + // The maximum prescribed order is reached. + return; } - delete [] newtops; - delete [] bots; - return false; } - // 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); + // Recursively sort the points in sub-boxes. + for (w = 0; w < 8; w++) { + // w is the local Hilbert index (NOT Gray code). + // Sort into the sub-box either there are more than 2 points in it, or + // the prescribed order of the curve is not reached yet. + //if ((p[w+1] - p[w] > b->hilbert_limit) || (b->hilbert_order > 0)) { + if ((p[w+1] - p[w]) > b->hilbert_limit) { + // Calculcate the start point (ei) of the curve in this sub-box. + // update e = e ^ (e(w) left_rotate (d+1)). + if (w == 0) { + e_w = 0; + } else { + // calculate e(w) = gc(2 * floor((w - 1) / 2)). + k = 2 * ((w - 1) / 2); + e_w = k ^ (k >> 1); // = gc(k). + } + k = e_w; + e_w = ((k << (d+1)) & mask) | ((k >> (n-d-1)) & mask); + ei = e ^ e_w; + // Calulcate the direction (di) of the curve in this sub-box. + // update d = (d + d(w) + 1) % n + if (w == 0) { + d_w = 0; + } else { + d_w = ((w % 2) == 0) ? tsb1mod3[w - 1] : tsb1mod3[w]; } - } - 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); + di = (d + d_w + 1) % n; + // Calculate the bounding box of the sub-box. + if (transgc[e][d][w] & 1) { // x-axis + x1 = 0.5 * (bxmin + bxmax); + x2 = bxmax; + } else { + x1 = bxmin; + x2 = 0.5 * (bxmin + bxmax); } - enextself(worktet); // edge n2->v ==> n2->b - tsspivot1(worktet, n2vseg); - if (n2vseg.sh != dummysh) { - tssdissolve1(worktet); - tssbond1(newtops[i], n2vseg); + if (transgc[e][d][w] & 2) { // y-axis + y1 = 0.5 * (bymin + bymax); + y2 = bymax; + } else { + y1 = bymin; + y2 = 0.5 * (bymin + bymax); } - enextself(worktet); // edge v->n1 ==> b->n1 - tsspivot1(worktet, n1vseg); - if (n1vseg.sh != dummysh) { - tssdissolve1(worktet); - enext2(newtops[i], tmpbond0); - tssbond1(tmpbond0, n1vseg); + if (transgc[e][d][w] & 4) { // z-axis + z1 = 0.5 * (bzmin + bzmax); + z2 = bzmax; + } else { + z1 = bzmin; + z2 = 0.5 * (bzmin + bzmax); } - } - } + hilbert_sort3(&(vertexarray[p[w]]), p[w+1] - p[w], ei, di, + x1, x2, y1, y2, z1, z2, depth+1); + } // if (p[w+1] - p[w] > 1) + } // w +} - // 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); - } - } +/////////////////////////////////////////////////////////////////////////////// +// // +// brio_multiscale_sort() Sort the points using BRIO and Hilbert curve. // +// // +/////////////////////////////////////////////////////////////////////////////// - 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])); - } - } +void tetgenmesh::brio_multiscale_sort(point* vertexarray, int arraysize, + int threshold, REAL ratio, int *depth) +{ + int middle; - if (flipqueue != (queue *) NULL) { - for (i = 0; i < wrapcount; i++) { - enqueueflipface(bots[i], flipqueue); - enqueueflipface(newtops[i], flipqueue); - } + middle = 0; + if (arraysize >= threshold) { + (*depth)++; + middle = arraysize * ratio; + brio_multiscale_sort(vertexarray, middle, threshold, ratio, depth); } - - // 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; + // Sort the right-array (rnd-th round) using the Hilbert curve. + hilbert_sort3(&(vertexarray[middle]), arraysize - middle, 0, 0, // e, d + xmin, xmax, ymin, ymax, zmin, zmax, 0); // depth. } /////////////////////////////////////////////////////////////////////////////// // // -// splitsubedge() Insert a point on an edge of the surface mesh. // -// // -// The splitting edge is given by 'splitsh'. Assume its three corners are a, // -// b, c, where ab is the edge will be split. ab may be a subsegment. // -// // -// To split edge ab is to split all subfaces conatining ab. If ab is not a // -// subsegment, there are only two subfaces need be split, otherwise, there // -// may have any number of subfaces need be split. Each splitting subface abc // -// is shrunk to avc, a new subface vbc is created. It is important to keep // -// the orientations of edge rings of avc and vbc be the same as abc's. If ab // -// is a subsegment, it is shrunk to av and a new subsegment vb is created. // -// // -// If there are tetrahedra adjoining to the splitting subfaces, they should // -// be split before calling this routine, so the connection between the new // -// tetrahedra and the new subfaces can be correctly set. // -// // -// On completion, 'splitsh' returns avc. If 'flipqueue' is not NULL, it // -// returns all edges which may be non-Delaunay. // +// randomnation() Generate a random number between 0 and 'choices' - 1. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::splitsubedge(point newpoint, face* splitsh, queue* flipqueue) +unsigned long tetgenmesh::randomnation(unsigned int choices) { - triface abcd, bace, vbcd, bvce; - face startabc, spinabc, spinsh; - face oldbc, bccasin, bccasout; - face ab, bc; - face avc, vbc, vbc1; - face av, vb; - point pa, pb; + unsigned long newrandom; - startabc = *splitsh; - // Is there a subsegment? - sspivot(startabc, ab); - if (ab.sh != dummysh) { - ab.shver = 0; - if (sorg(startabc) != sorg(ab)) { - sesymself(startabc); - } - } - pa = sorg(startabc); - pb = sdest(startabc); - - if (b->verbose > 1) { - printf(" Inserting point %d on subedge (%d, %d) %s.\n", - pointmark(newpoint), pointmark(pa), pointmark(pb), - (ab.sh != dummysh ? "(seg)" : " ")); - } - - // Spin arround ab, split every subface containing ab. - spinabc = startabc; - do { - // Adjust spinabc be edge ab. - 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) { - // 'spinabc' is not self-bonded. - spinsh = bccasout; - do { - bccasin = spinsh; - spivotself(spinsh); - } while (spinsh.sh != oldbc.sh); - } else { - bccasout.sh = dummysh; - } - ssdissolve(oldbc); - } - // Create a new subface. - makeshellface(subfaces, &vbc); - // Split abc. - avc = spinabc; // Update 'abc' to 'avc'. - setsdest(avc, newpoint); - // Make 'vbc' be in the same edge ring as 'avc'. - vbc.shver = avc.shver; - setsorg(vbc, newpoint); // Set 'vbc'. - setsdest(vbc, pb); - setsapex(vbc, sapex(avc)); - if (b->quality && varconstraint) { - // Copy the area bound into the new subface. - setareabound(vbc, areabound(avc)); - } - // Copy the shell marker and shell type into the new subface. - setshellmark(vbc, shellmark(avc)); - setshelltype(vbc, shelltype(avc)); - if (checkpbcs) { - // Copy the pbcgroup into the new subface. - setshellpbcgroup(vbc, shellpbcgroup(avc)); - } - // Set the connection between updated and new subfaces. - senext2self(vbc); - sbond(vbc, oldbc); - // Set the connection between new subface and casings. - senext2self(vbc); - if (bc.sh != dummysh) { - if (bccasout.sh != dummysh) { - // Insert 'vbc' into face link. - sbond1(bccasin, vbc); - sbond1(vbc, bccasout); - } else { - // Bond 'vbc' to itself. - sdissolve(vbc); // sbond(vbc, vbc); - } - ssbond(vbc, bc); - } else { - sbond(vbc, bccasout); - } - // Go to next subface at edge ab. - spivotself(spinabc); - if (spinabc.sh == dummysh) { - break; // 'ab' is a hull edge. - } - } while (spinabc.sh != startabc.sh); - - // Get the new subface vbc above the updated subface avc (= startabc). - senext(startabc, oldbc); - spivot(oldbc, vbc); - if (sorg(vbc) == newpoint) { - sesymself(vbc); - } -#ifdef SELF_CHECK - assert(sorg(vbc) == sdest(oldbc) && sdest(vbc) == sorg(oldbc)); -#endif - senextself(vbc); - // Set the face link for the new created subfaces around edge vb. - spinabc = startabc; - do { - // Go to the next subface at edge av. - spivotself(spinabc); - if (spinabc.sh == dummysh) { - break; // 'ab' is a hull edge. - } - if (sorg(spinabc) != pa) { - sesymself(spinabc); - } - // Get the new subface vbc1 above the updated subface avc (= spinabc). - senext(spinabc, oldbc); - spivot(oldbc, vbc1); - if (sorg(vbc1) == newpoint) { - sesymself(vbc1); - } -#ifdef SELF_CHECK - assert(sorg(vbc1) == sdest(oldbc) && sdest(vbc1) == sorg(oldbc)); -#endif - senextself(vbc1); - // Set the connection: vbc->vbc1. - sbond1(vbc, vbc1); - // For the next connection. - vbc = vbc1; - } while (spinabc.sh != startabc.sh); - - // 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); - // Create a new subsegment vb. - makeshellface(subsegs, &vb); - setsorg(vb, newpoint); - setsdest(vb, pb); - // vb gets the same mark and segment type as av. - setshellmark(vb, shellmark(av)); - setshelltype(vb, shelltype(av)); - if (b->quality && varconstraint) { - // Copy the area bound into the new subsegment. - setareabound(vb, areabound(av)); - } - // Save the old connection at ab (re-use the handles oldbc, bccasout). - senext(av, oldbc); - spivot(oldbc, bccasout); - // Bond av and vb (bonded at their "fake" edges). - senext2(vb, bccasin); - sbond(bccasin, oldbc); - if (bccasout.sh != dummysh) { - // There is a subsegment connecting with ab at b. It will connect - // to vb at b after splitting. - bccasout.shver = 0; - if (sorg(bccasout) != pb) sesymself(bccasout); -#ifdef SELF_CHECK - assert(sorg(bccasout) == pb); -#endif - senext2self(bccasout); - senext(vb, bccasin); - sbond(bccasin, bccasout); - } - // Bond all new subfaces (vbc) to vb. - spinabc = startabc; - do { - // Adjust spinabc be edge av. - if (sorg(spinabc) != pa) { - sesymself(spinabc); - } - // Get new subface vbc above the updated subface avc (= spinabc). - senext(spinabc, oldbc); - spivot(oldbc, vbc); - if (sorg(vbc) == newpoint) { - sesymself(vbc); - } - senextself(vbc); - // Bond the new subface and the new subsegment. - ssbond(vbc, vb); - // Go to the next. - spivotself(spinabc); - if (spinabc.sh == dummysh) { - break; // There's only one facet at the segment.rr - } - } while (spinabc.sh != startabc.sh); - } - - // Bond the new subfaces to new tetrahedra if they exist. New tetrahedra - // should have been created before calling this routine. - spinabc = startabc; - do { - // Adjust spinabc be edge av. - if (sorg(spinabc) != pa) { - sesymself(spinabc); - } - // Get new subface vbc above the updated subface avc (= spinabc). - senext(spinabc, oldbc); - spivot(oldbc, vbc); - if (sorg(vbc) == newpoint) { - sesymself(vbc); - } - senextself(vbc); - // Get the adjacent tetrahedra at 'spinabc'. - stpivot(spinabc, abcd); - if (abcd.tet != dummytet) { - findedge(&abcd, sorg(spinabc), sdest(spinabc)); - enextfnext(abcd, vbcd); - fnextself(vbcd); -#ifdef SELF_CHECK - assert(vbcd.tet != dummytet); -#endif - tsbond(vbcd, vbc); - sym(vbcd, bvce); - sesymself(vbc); - tsbond(bvce, vbc); + 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 { - // One side is empty, check the other side. - sesymself(spinabc); - stpivot(spinabc, bace); - if (bace.tet != dummytet) { - findedge(&bace, sorg(spinabc), sdest(spinabc)); - enext2fnext(bace, bvce); - fnextself(bvce); -#ifdef SELF_CHECK - assert(bvce.tet != dummytet); -#endif - sesymself(vbc); - tsbond(bvce, vbc); - } - } - // Go to the next. - spivotself(spinabc); - if (spinabc.sh == dummysh) { - break; // 'ab' is a hull edge. - } - } while (spinabc.sh != startabc.sh); - - if (b->verbose > 3) { - spinabc = startabc; - do { - // Adjust spinabc be edge av. - if (sorg(spinabc) != pa) { - sesymself(spinabc); - } - printf(" Updating abc:\n"); - printsh(&spinabc); - // Get new subface vbc above the updated subface avc (= spinabc). - senext(spinabc, oldbc); - spivot(oldbc, vbc); - if (sorg(vbc) == newpoint) { - sesymself(vbc); - } - senextself(vbc); - printf(" Creating vbc:\n"); - printsh(&vbc); - // Go to the next. - spivotself(spinabc); - if (spinabc.sh == dummysh) { - break; // 'ab' is a hull edge. - } - } while (spinabc.sh != startabc.sh); - } - - if (flipqueue != (queue *) NULL) { - spinabc = startabc; - do { - // Adjust spinabc be edge av. - if (sorg(spinabc) != pa) { - sesymself(spinabc); - } - senext2(spinabc, oldbc); // Re-use oldbc. - enqueueflipedge(oldbc, flipqueue); - // Get new subface vbc above the updated subface avc (= spinabc). - senext(spinabc, oldbc); - spivot(oldbc, vbc); - if (sorg(vbc) == newpoint) { - sesymself(vbc); - } - senextself(vbc); - senext(vbc, oldbc); // Re-use oldbc. - enqueueflipedge(oldbc, flipqueue); - // Go to the next. - spivotself(spinabc); - if (spinabc.sh == dummysh) { - break; // 'ab' is a hull edge. - } - } while (spinabc.sh != startabc.sh); + return newrandom; + } + } else { + randomseed = (randomseed * 1366l + 150889l) % 714025l; + return randomseed % choices; } } /////////////////////////////////////////////////////////////////////////////// // // -// 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'. // +// randomsample() Randomly sample the tetrahedra for point loation. // // // -// '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. // +// 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 searching for. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::formstarpolyhedron(point pt, list* tetlist, list* verlist, - bool complete) +void tetgenmesh::randomsample(point searchpt,triface *searchtet) { - triface starttet, neightet; - face checksh; - point ver[3]; - int idx, i, j; + tetrahedron *firsttet, *tetptr; + point torg; + void **sampleblock; + uintptr_t alignptr; + long sampleblocks, samplesperblock, samplenum; + long tetblocks, i, j; + REAL searchdist, dist; - // 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])); - } + if (b->verbose > 2) { + printf(" Random sampling tetrahedra for searching point %d.\n", + pointmark(searchpt)); } - // 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])); - } - } - } + if (!nonconvex) { + if (searchtet->tet == NULL) { + // A null tet. Choose the recenttet as the starting tet. + *searchtet = recenttet; + // Recenttet should not be dead. + assert(recenttet.tet[4] != NULL); + } + + // 'searchtet' should be a valid tetrahedron. Choose the base face + // whose vertices must not be 'dummypoint'. + searchtet->ver = 3; + // Record the distance from its origin to the searching point. + torg = org(*searchtet); + searchdist = (searchpt[0] - torg[0]) * (searchpt[0] - torg[0]) + + (searchpt[1] - torg[1]) * (searchpt[1] - torg[1]) + + (searchpt[2] - torg[2]) * (searchpt[2] - torg[2]); + + // If a recently encountered tetrahedron has been recorded and has not + // been deallocated, test it as a good starting point. + if (recenttet.tet != searchtet->tet) { + recenttet.ver = 3; + torg = org(recenttet); + dist = (searchpt[0] - torg[0]) * (searchpt[0] - torg[0]) + + (searchpt[1] - torg[1]) * (searchpt[1] - torg[1]) + + (searchpt[2] - torg[2]) * (searchpt[2] - torg[2]); + if (dist < searchdist) { + *searchtet = recenttet; + searchdist = dist; } - enextself(starttet); } + } else { + // The mesh is non-convex. Do not use 'recenttet'. + assert(samples >= 1l); // Make sure at least 1 sample. + searchdist = longest; } - // Uninfect tets. - for (i = 0; i < tetlist->len(); i++) { - starttet = * (triface *)(* tetlist)[i]; - uninfect(starttet); + // 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. + while (samples * samples * samples * samples < tetrahedrons->items) { + samples++; } - 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)); + // Find how much blocks in current tet pool. + tetblocks = (tetrahedrons->maxitems + b->tetrahedraperblock - 1) + / b->tetrahedraperblock; + // Find the average samples 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 * b->tetrahedraperblock))); + } else { + samplenum = randomnation(b->tetrahedraperblock); + } + tetptr = (tetrahedron *) + (firsttet + (samplenum * tetrahedrons->itemwords)); + torg = (point) tetptr[4]; + if (torg != (point) NULL) { + dist = (searchpt[0] - torg[0]) * (searchpt[0] - torg[0]) + + (searchpt[1] - torg[1]) * (searchpt[1] - torg[1]) + + (searchpt[2] - torg[2]) * (searchpt[2] - torg[2]); + if (dist < searchdist) { + searchtet->tet = tetptr; + searchtet->ver = 11; // torg = org(t); + searchdist = dist; + } + } else { + // A dead tet. Re-sample it. + if (i != tetblocks - 1) j--; + } } + sampleblock = (void **) *sampleblock; } } /////////////////////////////////////////////////////////////////////////////// // // -// Terminology: BC(p) and CBC(p), B(p) and C(p). // -// // -// Given an arbitrary point p, the Bowyer-Watson cavity BC(p) is formed by // -// tets whose circumspheres containing p. The outer faces of BC(p) form a // -// polyhedron B(p). // -// // -// If p is on a facet F, the constrained Bowyer-Watson cavity CBC(p) on F is // -// formed by subfaces of F whose circumspheres containing p. The outer edges // -// of CBC(p) form a polygon C(p). B(p) is separated into two parts by C(p), // -// denoted as B_1(p) and B_2(p), one of them may be empty (F is on the hull).// -// // -// If p is on a segment S which is shared by n facets. There exist n C(p)s, // -// each one is a non-closed polygon (without S). B(p) is split into n parts, // -// each of them is denoted as B_i(p), some B_i(p) may be empty. // -// // -/////////////////////////////////////////////////////////////////////////////// - -/////////////////////////////////////////////////////////////////////////////// +// locate() Find a tetrahedron containing a given point. // // // -// formbowatcavitysub() Form CBC(p) and C(p) on a facet F. // +// 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. // // // -// Parameters: bp = p, bpseg = S, sublist = CBC(p), subceillist = C(p). // +// 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 face which is visible by the search point. // // // -// CBC(p) contains at least one subface on input; S may be NULL which means // -// that p is inside a facet. On output, all subfaces of CBC(p) are infected, // -// and the edge rings are oriented to the same halfspace. // +// WARNING: This routine is designed for convex triangulations, and will not // +// generally work after the holes and concavities have been carved. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::formbowatcavitysub(point bp, face* bpseg, list* sublist, - list* subceillist) +enum tetgenmesh::locateresult tetgenmesh::locate(point searchpt, + triface* searchtet) { - triface adjtet; - face startsh, neighsh; - face checkseg; - point pa, pb, pc, pd; - REAL sign; - int i, j; + point torg, tdest, tapex, toppo; + enum {ORGMOVE, DESTMOVE, APEXMOVE} nextmove; + REAL ori, oriorg, oridest, oriapex; + enum locateresult loc = OUTSIDE; + int t1ver; + int s; - // Form CBC(p) and C(p) by a broadth-first searching. - for (i = 0; i < sublist->len(); i++) { - startsh = * (face *)(* sublist)[i]; // startsh = f. - // Look for three neighbors of f. - for (j = 0; j < 3; j++) { - sspivot(startsh, checkseg); - if (checkseg.sh == dummysh) { - // Get its neighbor n. - spivot(startsh, neighsh); - // Is n already in CBC(p)? - if (!sinfected(neighsh)) { - stpivot(neighsh, adjtet); - if (adjtet.tet == dummytet) { - sesymself(neighsh); - stpivot(neighsh, adjtet); - } - // For positive orientation that insphere() test requires. - adjustedgering(adjtet, CW); - pa = org(adjtet); - pb = dest(adjtet); - pc = apex(adjtet); - pd = oppo(adjtet); - sign = insphere(pa, pb, pc, pd, bp); - if (sign >= 0.0) { - // Orient edge ring of n according to that of f. - if (sorg(neighsh) != sdest(startsh)) sesymself(neighsh); - // Collect it into CBC(p). - sinfect(neighsh); - sublist->append(&neighsh); - } else { - subceillist->append(&startsh); // Found an edge of C(p). - } - } - } else { - // Do not cross a segment. - if (bpseg != (face *) NULL) { - if (checkseg.sh != bpseg->sh) { - subceillist->append(&startsh); // Found an edge of C(p). - } - } else { - subceillist->append(&startsh); // Found an edge of C(p). - } - } - senextself(startsh); - } + if (searchtet->tet == NULL) { + // A null tet. Choose the recenttet as the starting tet. + searchtet->tet = recenttet.tet; } - if (b->verbose > 2) { - printf(" Collect CBC(%d): %d subfaces, %d edges.\n", pointmark(bp), - sublist->len(), subceillist->len()); + // Check if we are in the outside of the convex hull. + if (ishulltet(*searchtet)) { + // Get its adjacent tet (inside the hull). + searchtet->ver = 3; + fsymself(*searchtet); } -} -/////////////////////////////////////////////////////////////////////////////// -// // -// formbowatcavityquad() Form BC_i(p) and B_i(p) in a quadrant. // -// // -// Parameters: bp = p, tetlist = BC_i(p), ceillist = B_i(p). // -// // -// BC_i(p) contains at least one tet on input. On finish, all tets collected // -// in BC_i(p) are infected. B_i(p) may not closed when p is on segment or in // -// facet. C(p) must be formed before this routine. Check the infect flag of // -// a subface to identify the unclosed side of B_i(p). These sides will be // -// closed by new subfaces of C(p)s. // -// // -/////////////////////////////////////////////////////////////////////////////// + // Let searchtet be the face such that 'searchpt' lies above to it. + for (searchtet->ver = 0; searchtet->ver < 4; searchtet->ver++) { + torg = org(*searchtet); + tdest = dest(*searchtet); + tapex = apex(*searchtet); + ori = orient3d(torg, tdest, tapex, searchpt); + if (ori < 0.0) break; + } + assert(searchtet->ver != 4); -void tetgenmesh::formbowatcavityquad(point bp, list* tetlist, list* ceillist) -{ - triface starttet, neightet; - face checksh; - point pa, pb, pc, pd; - REAL sign; - int i; + // Walk through tetrahedra to locate the point. + while (true) { - // Form BC_i(p) and B_i(p) by a broadth-first searching. - for (i = 0; i < tetlist->len(); i++) { - starttet = * (triface *)(* tetlist)[i]; - for (starttet.loc = 0; starttet.loc < 4; starttet.loc++) { - // Try to collect the neighbor of the face (f). - tspivot(starttet, checksh); - if (checksh.sh == dummysh) { - // Get its neighbor n. - sym(starttet, neightet); - // Is n already in BC_i(p)? - if (!infected(neightet)) { - // For positive orientation that insphere() test requires. - adjustedgering(neightet, CW); - pa = org(neightet); - pb = dest(neightet); - pc = apex(neightet); - pd = oppo(neightet); - sign = insphere(pa, pb, pc, pd, bp); - if (sign >= 0.0) { - // Collect it into BC_i(p). - infect(neightet); - tetlist->append(&neightet); + toppo = oppo(*searchtet); + + // Check if the vertex is we seek. + if (toppo == searchpt) { + // Adjust the origin of searchtet to be searchpt. + esymself(*searchtet); + eprevself(*searchtet); + loc = ONVERTEX; // return ONVERTEX; + break; + } + + // We enter from one of serarchtet's faces, which face do we exit? + oriorg = orient3d(tdest, tapex, toppo, searchpt); + oridest = orient3d(tapex, torg, toppo, searchpt); + oriapex = orient3d(torg, tdest, toppo, searchpt); + + // Now decide which face to move. It is possible there are more than one + // faces are viable moves. If so, randomly choose one. + if (oriorg < 0) { + if (oridest < 0) { + if (oriapex < 0) { + // All three faces are possible. + s = randomnation(3); // 's' is in {0,1,2}. + if (s == 0) { + nextmove = ORGMOVE; + } else if (s == 1) { + nextmove = DESTMOVE; + } else { + nextmove = APEXMOVE; + } + } else { + // Two faces, opposite to origin and destination, are viable. + //s = randomnation(2); // 's' is in {0,1}. + if (randomnation(2)) { + nextmove = ORGMOVE; + } else { + nextmove = DESTMOVE; + } + } + } else { + if (oriapex < 0) { + // Two faces, opposite to origin and apex, are viable. + //s = randomnation(2); // 's' is in {0,1}. + if (randomnation(2)) { + nextmove = ORGMOVE; + } else { + nextmove = APEXMOVE; + } + } 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. + //s = randomnation(2); // 's' is in {0,1}. + if (randomnation(2)) { + nextmove = DESTMOVE; } else { - ceillist->append(&starttet); // Found a face of B_i(p). + nextmove = APEXMOVE; } + } else { + // Only the face opposite to destination is viable. + nextmove = DESTMOVE; } } else { - // Do not cross a boundary face. - if (!sinfected(checksh)) { - ceillist->append(&starttet); // Found a face of B_i(p). + 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. + enextesymself(*searchtet); + if (oridest == 0) { + eprevself(*searchtet); // edge oppo->apex + if (oriapex == 0) { + // oppo is duplicated with p. + loc = ONVERTEX; // return ONVERTEX; + break; + } + loc = ONEDGE; // return ONEDGE; + break; + } + if (oriapex == 0) { + enextself(*searchtet); // edge dest->oppo + loc = ONEDGE; // return ONEDGE; + break; + } + loc = ONFACE; // return ONFACE; + break; + } + if (oridest == 0) { + // Go to the face opposite to destination. + eprevesymself(*searchtet); + if (oriapex == 0) { + eprevself(*searchtet); // edge oppo->org + loc = ONEDGE; // return ONEDGE; + break; + } + loc = ONFACE; // return ONFACE; + break; + } + if (oriapex == 0) { + // Go to the face opposite to apex + esymself(*searchtet); + loc = ONFACE; // return ONFACE; + break; + } + loc = INTETRAHEDRON; // return INTETRAHEDRON; + break; } } } - } + + // Move to the selected face. + if (nextmove == ORGMOVE) { + enextesymself(*searchtet); + } else if (nextmove == DESTMOVE) { + eprevesymself(*searchtet); + } else { + esymself(*searchtet); + } + // Move to the adjacent tetrahedron (maybe a hull tetrahedron). + fsymself(*searchtet); + if (oppo(*searchtet) == dummypoint) { + loc = OUTSIDE; // return OUTSIDE; + break; + } - if (b->verbose > 2) { - printf(" Collect BC_i(%d): %d tets, %d faces.\n", pointmark(bp), - tetlist->len(), ceillist->len()); + // Retreat the three vertices of the base face. + torg = org(*searchtet); + tdest = dest(*searchtet); + tapex = apex(*searchtet); + + } // while (true) + + return loc; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// flippush() Push a face (possibly will be flipped) into flipstack. // +// // +// The face is marked. The flag is used to check the validity of the face on // +// its popup. Some other flips may change it already. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::flippush(badface*& fstack, triface* flipface) +{ + if (!facemarked(*flipface)) { + badface *newflipface = (badface *) flippool->alloc(); + newflipface->tt = *flipface; + markface(newflipface->tt); + // Push this face into stack. + newflipface->nextitem = fstack; + fstack = newflipface; } } /////////////////////////////////////////////////////////////////////////////// // // -// formbowatcavitysegquad() Form BC_i(p) and B_i(p) in a segment quadrant.// +// incrementalflip() Incrementally flipping to construct DT. // // // -// Parameters: bp = p, tetlist = BC_i(p), ceillist = B_i(p). // +// Faces need to be checked for flipping are already queued in 'flipstack'. // +// Return the total number of performed flips. // // // -// BC_i(p) contains at least one tet on input. On finish, all tets collected // -// in BC_i(p) are infected. B_i(p) is not closed. C(p) must be formed before // -// this routine. Check the infect flag of a subface to identify the unclosed // -// sides of B_i(p). These sides will be closed by new subfaces of C(p)s. // +// Comment: This routine should be only used in the incremental Delaunay // +// construction. In other cases, lawsonflip3d() should be used. // // // -// During the repair of encroaching subsegments, there may exist locally non-// -// Delaunay faces. These faces are collected in BC_i(p) either. B_i(p) has // -// to be formed later than BC_i(p). // +// If the new point lies outside of the convex hull ('hullflag' is set). The // +// incremental flip algorithm still works as usual. However, we must ensure // +// that every flip (2-to-3 or 3-to-2) does not create a duplicated (existing)// +// edge or face. Otherwise, the underlying space of the triangulation becomes// +// non-manifold and it is not possible to flip further. // +// Thanks to Joerg Rambau and Frank Lutz for helping in this issue. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::formbowatcavitysegquad(point bp, list* tetlist,list* ceillist) +int tetgenmesh::incrementalflip(point newpt, int hullflag, flipconstraints *fc) { - triface starttet, neightet, cavtet; - face checksh; - point pa, pb, pc, pd, pe; - REAL sign; + badface *popface; + triface fliptets[5], *parytet; + point *pts, *parypt, pe; + REAL sign, ori; + int flipcount = 0; + int t1ver; int i; - // Form BC_i(p) by a broadth-first searching. - for (i = 0; i < tetlist->len(); i++) { - starttet = * (triface *)(* tetlist)[i]; - for (starttet.loc = 0; starttet.loc < 4; starttet.loc++) { - // Try to collect the neighbor of the face f. - tspivot(starttet, checksh); - if (checksh.sh == dummysh) { - // Get its neighbor n. - sym(starttet, neightet); - // Is n already in BC_i(p)? - if (!infected(neightet)) { - // For positive orientation that insphere() test requires. - adjustedgering(neightet, CW); - pa = org(neightet); - pb = dest(neightet); - pc = apex(neightet); - pd = oppo(neightet); - sign = insphere(pa, pb, pc, pd, bp); - if (sign >= 0.0) { - // Collect it into BC_i(p). - infect(neightet); - tetlist->append(&neightet); + if (b->verbose > 2) { + printf(" Lawson flip (%ld faces).\n", flippool->items); + } + + if (hullflag) { + // 'newpt' lies in the outside of the convex hull. + // Mark all hull vertices which are connecting to it. + popface = flipstack; + while (popface != NULL) { + pts = (point *) popface->tt.tet; + for (i = 4; i < 8; i++) { + if ((pts[i] != newpt) && (pts[i] != dummypoint)) { + if (!pinfected(pts[i])) { + pinfect(pts[i]); + cavetetvertlist->newindex((void **) &parypt); + *parypt = pts[i]; + } + } + } + popface = popface->nextitem; + } + } + + // Loop until the queue is empty. + while (flipstack != NULL) { + + // Pop a face from the stack. + popface = flipstack; + fliptets[0] = popface->tt; + flipstack = flipstack->nextitem; // The next top item in stack. + flippool->dealloc((void *) popface); + + // Skip it if it is a dead tet (destroyed by previous flips). + if (isdeadtet(fliptets[0])) continue; + // Skip it if it is not the same tet as we saved. + if (!facemarked(fliptets[0])) continue; + + unmarkface(fliptets[0]); + + if ((point) fliptets[0].tet[7] == dummypoint) { + // It must be a hull edge. + fliptets[0].ver = epivot[fliptets[0].ver]; + // A hull edge. The current convex hull may be enlarged. + fsym(fliptets[0], fliptets[1]); + pts = (point *) fliptets[1].tet; + ori = orient3d(pts[4], pts[5], pts[6], newpt); + if (ori < 0) { + // Visible. The convex hull will be enlarged. + // Decide which flip (2-to-3, 3-to-2, or 4-to-1) to use. + // Check if the tet [a,c,e,d] or [c,b,e,d] exists. + enext(fliptets[1], fliptets[2]); + eprev(fliptets[1], fliptets[3]); + fnextself(fliptets[2]); // [a,c,e,*] + fnextself(fliptets[3]); // [c,b,e,*] + if (oppo(fliptets[2]) == newpt) { + if (oppo(fliptets[3]) == newpt) { + // Both tets exist! A 4-to-1 flip is found. + terminatetetgen(this, 2); // Report a bug. } else { - // Check if the face is locally non-Delaunay. - pe = oppo(starttet); - sign = insphere(pa, pb, pc, pd, pe); - if (sign >= 0.0) { - // Collect it into BC_i(p). - infect(neightet); - tetlist->append(&neightet); + esym(fliptets[2], fliptets[0]); + fnext(fliptets[0], fliptets[1]); + fnext(fliptets[1], fliptets[2]); + // Perform a 3-to-2 flip. Replace edge [c,a] by face [d,e,b]. + // This corresponds to my standard labels, where edge [e,d] is + // repalced by face [a,b,c], and a is the new vertex. + // [0] [c,a,d,e] (d = newpt) + // [1] [c,a,e,b] (c = dummypoint) + // [2] [c,a,b,d] + flip32(fliptets, 1, fc); + } + } else { + if (oppo(fliptets[3]) == newpt) { + fnext(fliptets[3], fliptets[0]); + fnext(fliptets[0], fliptets[1]); + fnext(fliptets[1], fliptets[2]); + // Perform a 3-to-2 flip. Replace edge [c,b] by face [d,a,e]. + // [0] [c,b,d,a] (d = newpt) + // [1] [c,b,a,e] (c = dummypoint) + // [2] [c,b,e,d] + flip32(fliptets, 1, fc); + } else { + if (hullflag) { + // Reject this flip if pe is already marked. + pe = oppo(fliptets[1]); + if (!pinfected(pe)) { + pinfect(pe); + cavetetvertlist->newindex((void **) &parypt); + *parypt = pe; + // Perform a 2-to-3 flip. + flip23(fliptets, 1, fc); + } else { + // Reject this flip. + flipcount--; + } + } else { + // Perform a 2-to-3 flip. Replace face [a,b,c] by edge [e,d]. + // [0] [a,b,c,d], d = newpt. + // [1] [b,a,c,e], c = dummypoint. + flip23(fliptets, 1, fc); } } } - } + flipcount++; + } + continue; + } // if (dummypoint) + + fsym(fliptets[0], fliptets[1]); + if ((point) fliptets[1].tet[7] == dummypoint) { + // A hull face is locally Delaunay. + continue; + } + // Check if the adjacent tet has already been tested. + if (marktested(fliptets[1])) { + // It has been tested and it is Delaunay. + continue; } - } - // Generate B_i(p). - for (i = 0; i < tetlist->len(); i++) { - cavtet = * (triface *)(* tetlist)[i]; - for (cavtet.loc = 0; cavtet.loc < 4; cavtet.loc++) { - tspivot(cavtet, checksh); - if (checksh.sh == dummysh) { - sym(cavtet, neightet); - if (!infected(neightet)) { - ceillist->append(&cavtet); // Found a face of B(p). + // Test whether the face is locally Delaunay or not. + pts = (point *) fliptets[1].tet; + if (b->weighted) { + sign = orient4d_s(pts[4], pts[5], pts[6], pts[7], newpt, + pts[4][3], pts[5][3], pts[6][3], pts[7][3], + newpt[3]); + } else { + sign = insphere_s(pts[4], pts[5], pts[6], pts[7], newpt); + } + + + if (sign < 0) { + point pd = newpt; + point pe = oppo(fliptets[1]); + // Check the convexity of its three edges. Stop checking either a + // locally non-convex edge (ori < 0) or a flat edge (ori = 0) is + // encountered, and 'fliptet' represents that edge. + for (i = 0; i < 3; i++) { + ori = orient3d(org(fliptets[0]), dest(fliptets[0]), pd, pe); + if (ori <= 0) break; + enextself(fliptets[0]); + } + if (ori > 0) { + // A 2-to-3 flip is found. + // [0] [a,b,c,d], + // [1] [b,a,c,e]. no dummypoint. + flip23(fliptets, 0, fc); + flipcount++; + } else { // ori <= 0 + // The edge ('fliptets[0]' = [a',b',c',d]) is non-convex or flat, + // where the edge [a',b'] is one of [a,b], [b,c], and [c,a]. + // Check if there are three or four tets sharing at this edge. + esymself(fliptets[0]); // [b,a,d,c] + for (i = 0; i < 3; i++) { + fnext(fliptets[i], fliptets[i+1]); } - } else { - // Do not cross a boundary face. - if (!sinfected(checksh)) { - ceillist->append(&cavtet); // Found a face of B(p). + if (fliptets[3].tet == fliptets[0].tet) { + // A 3-to-2 flip is found. (No hull tet.) + flip32(fliptets, 0, fc); + flipcount++; + } else { + // There are more than 3 tets at this edge. + fnext(fliptets[3], fliptets[4]); + if (fliptets[4].tet == fliptets[0].tet) { + if (ori == 0) { + // A 4-to-4 flip is found. (Two hull tets may be involved.) + // Current tets in 'fliptets': + // [0] [b,a,d,c] (d may be newpt) + // [1] [b,a,c,e] + // [2] [b,a,e,f] (f may be dummypoint) + // [3] [b,a,f,d] + esymself(fliptets[0]); // [a,b,c,d] + // A 2-to-3 flip replaces face [a,b,c] by edge [e,d]. + // This creates a degenerate tet [e,d,a,b] (tmpfliptets[0]). + // It will be removed by the followed 3-to-2 flip. + flip23(fliptets, 0, fc); // No hull tet. + fnext(fliptets[3], fliptets[1]); + fnext(fliptets[1], fliptets[2]); + // Current tets in 'fliptets': + // [0] [...] + // [1] [b,a,d,e] (degenerated, d may be new point). + // [2] [b,a,e,f] (f may be dummypoint) + // [3] [b,a,f,d] + // A 3-to-2 flip replaces edge [b,a] by face [d,e,f]. + // Hull tets may be involved (f may be dummypoint). + flip32(&(fliptets[1]), (apex(fliptets[3]) == dummypoint), fc); + flipcount++; + } + } } - } + } // ori + } else { + // The adjacent tet is Delaunay. Mark it to avoid testing it again. + marktest(fliptets[1]); + // Save it for unmarking it later. + cavebdrylist->newindex((void **) &parytet); + *parytet = fliptets[1]; } + + } // while (flipstack) + + // Unmark saved tetrahedra. + for (i = 0; i < cavebdrylist->objects; i++) { + parytet = (triface *) fastlookup(cavebdrylist, i); + unmarktest(*parytet); } + cavebdrylist->restart(); - if (b->verbose > 2) { - printf(" Collect BC_i(%d): %d tets, %d faces.\n", pointmark(bp), - tetlist->len(), ceillist->len()); + if (hullflag) { + // Unmark infected vertices. + for (i = 0; i < cavetetvertlist->objects; i++) { + parypt = (point *) fastlookup(cavetetvertlist, i); + puninfect(*parypt); + } + cavetetvertlist->restart(); } + + + return flipcount; } /////////////////////////////////////////////////////////////////////////////// // // -// formbowatcavity() Form BC(p), B(p), CBC(p)s, and C(p)s. // +// initialdelaunay() Create an initial Delaunay tetrahedralization. // // // -// If 'bpseg'(S) != NULL, p is on segment S, else, p is on facet containing // -// 'bpsh' (F). 'n' returns the number of quadrants in BC(p). 'nmax' is the // -// maximum pre-allocated array length for the lists. // +// The tetrahedralization contains only one tetrahedron abcd, and four hull // +// tetrahedra. The points pa, pb, pc, and pd must be linearly independent. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::formbowatcavity(point bp, face* bpseg, face* bpsh, int* n, - int* nmax, list** sublists, list** subceillists, list** tetlists, - list** ceillists) +void tetgenmesh::initialdelaunay(point pa, point pb, point pc, point pd) { - list *sublist; - triface adjtet; - face startsh, spinsh; - point pa, pb; - int i, j; + triface firsttet, tetopa, tetopb, tetopc, tetopd; + triface worktet, worktet1; - *n = 0; - if (bpseg != (face *) NULL) { - // p is on segment S. - bpseg->shver = 0; - pa = sorg(*bpseg); - pb = sdest(*bpseg); - // Count the number of facets sharing at S. - spivot(*bpseg, startsh); - spinsh = startsh; - do { - (*n)++; // spinshlist->append(&spinsh); - spivotself(spinsh); - } while (spinsh.sh != startsh.sh); - // *n is the number of quadrants around S. - if (*n > *nmax) { - // Reallocate arrays. Should not happen very often. - delete [] tetlists; - delete [] ceillists; - delete [] sublists; - delete [] subceillists; - tetlists = new list*[*n]; - ceillists = new list*[*n]; - sublists = new list*[*n]; - subceillists = new list*[*n]; - *nmax = *n; - } - // Form CBC(p)s and C(p)s. - spinsh = startsh; - for (i = 0; i < *n; i++) { - sublists[i] = new list(sizeof(face), NULL, 256); - subceillists[i] = new list(sizeof(face), NULL, 256); - // Set a subface f to start search. - startsh = spinsh; - // Let f face to the quadrant of interest (used in forming BC(p)). - findedge(&startsh, pa, pb); - sinfect(startsh); - sublists[i]->append(&startsh); - formbowatcavitysub(bp, bpseg, sublists[i], subceillists[i]); - // Go to the next facet. - spivotself(spinsh); - } - } else if (sublists != (list **) NULL) { - // p is on a facet. - *n = 2; - // Form CBC(p) and C(p). - sublists[0] = new list(sizeof(face), NULL, 256); - subceillists[0] = new list(sizeof(face), NULL, 256); - sinfect(*bpsh); - sublists[0]->append(bpsh); - formbowatcavitysub(bp, NULL, sublists[0], subceillists[0]); - } else { - // p is inside a tet. - *n = 1; - } - - // Form BC_i(p) and B_i(p). - for (i = 0; i < *n; i++) { - tetlists[i] = new list(sizeof(triface), NULL, 256); - ceillists[i] = new list(sizeof(triface), NULL, 256); - if (sublists != (list **) NULL) { - // There are C(p)s. - sublist = ((bpseg == (face *) NULL) ? sublists[0] : sublists[i]); - // Add all adjacent tets of C_i(p) into BC_i(p). - for (j = 0; j < sublist->len(); j++) { - startsh = * (face *)(* sublist)[j]; - // Adjust the side facing to the right quadrant for C(p). - if ((bpseg == (face *) NULL) && (i == 1)) sesymself(startsh); - stpivot(startsh, adjtet); - if (adjtet.tet != dummytet) { - if (!infected(adjtet)) { - infect(adjtet); - tetlists[i]->append(&adjtet); - } - } - } - if (bpseg != (face *) NULL) { - // The quadrant is bounded by another facet. - sublist = ((i < *n - 1) ? sublists[i + 1] : sublists[0]); - for (j = 0; j < sublist->len(); j++) { - startsh = * (face *)(* sublist)[j]; - // Adjust the side facing to the right quadrant for C(p). - sesymself(startsh); - stpivot(startsh, adjtet); - if (adjtet.tet != dummytet) { - if (!infected(adjtet)) { - infect(adjtet); - tetlists[i]->append(&adjtet); - } - } - } - } - } - // It is possible that BC_i(p) is empty. - if (tetlists[i]->len() == 0) continue; - // Collect the rest of tets of BC_i(p) and form B_i(p). - // if (b->conformdel) { - // formbowatcavitysegquad(bp, tetlists[i], ceillists[i]); - // } else { - formbowatcavityquad(bp, tetlists[i], ceillists[i]); - // } + if (b->verbose > 2) { + printf(" Create init tet (%d, %d, %d, %d)\n", pointmark(pa), + pointmark(pb), pointmark(pc), pointmark(pd)); } -} -/////////////////////////////////////////////////////////////////////////////// -// // -// releasebowatcavity() Undo and free the memory allocated in routine // -// formbowatcavity(). // -// // -/////////////////////////////////////////////////////////////////////////////// + // Create the first tetrahedron. + maketetrahedron(&firsttet); + setvertices(firsttet, pa, pb, pc, pd); + // Create four hull tetrahedra. + maketetrahedron(&tetopa); + setvertices(tetopa, pb, pc, pd, dummypoint); + maketetrahedron(&tetopb); + setvertices(tetopb, pc, pa, pd, dummypoint); + maketetrahedron(&tetopc); + setvertices(tetopc, pa, pb, pd, dummypoint); + maketetrahedron(&tetopd); + setvertices(tetopd, pb, pa, pc, dummypoint); + hullsize += 4; -void tetgenmesh::releasebowatcavity(face* bpseg, int n, list** sublists, - list** subceillist, list** tetlists, list** ceillists) -{ - triface oldtet; - face oldsh; - int i, j; + // Connect hull tetrahedra to firsttet (at four faces of firsttet). + bond(firsttet, tetopd); + esym(firsttet, worktet); + bond(worktet, tetopc); // ab + enextesym(firsttet, worktet); + bond(worktet, tetopa); // bc + eprevesym(firsttet, worktet); + bond(worktet, tetopb); // ca - if (sublists != (list **) NULL) { - // Release CBC(p)s. - for (i = 0; i < n; i++) { - // Uninfect subfaces of CBC(p). - for (j = 0; j < sublists[i]->len(); j++) { - oldsh = * (face *)(* (sublists[i]))[j]; -#ifdef SELF_CHECK - assert(sinfected(oldsh)); -#endif - suninfect(oldsh); - } - delete sublists[i]; - delete subceillist[i]; - sublists[i] = (list *) NULL; - subceillist[i] = (list *) NULL; - if (bpseg == (face *) NULL) break; - } - } - // Release BC(p). - for (i = 0; i < n; i++) { - // Uninfect tets of BC_i(p). - for (j = 0; j < tetlists[i]->len(); j++) { - oldtet = * (triface *)(* (tetlists[i]))[j]; -#ifdef SELF_CHECK - assert(infected(oldtet)); -#endif - uninfect(oldtet); - } - delete tetlists[i]; - delete ceillists[i]; - tetlists[i] = (list *) NULL; - ceillists[i] = (list *) NULL; + // Connect hull tetrahedra together (at six edges of firsttet). + esym(tetopc, worktet); + esym(tetopd, worktet1); + bond(worktet, worktet1); // ab + esym(tetopa, worktet); + eprevesym(tetopd, worktet1); + bond(worktet, worktet1); // bc + esym(tetopb, worktet); + enextesym(tetopd, worktet1); + bond(worktet, worktet1); // ca + eprevesym(tetopc, worktet); + enextesym(tetopb, worktet1); + bond(worktet, worktet1); // da + eprevesym(tetopa, worktet); + enextesym(tetopc, worktet1); + bond(worktet, worktet1); // db + eprevesym(tetopb, worktet); + enextesym(tetopa, worktet1); + bond(worktet, worktet1); // dc + + // Set the vertex type. + if (pointtype(pa) == UNUSEDVERTEX) { + setpointtype(pa, VOLVERTEX); } + if (pointtype(pb) == UNUSEDVERTEX) { + setpointtype(pb, VOLVERTEX); + } + if (pointtype(pc) == UNUSEDVERTEX) { + setpointtype(pc, VOLVERTEX); + } + if (pointtype(pd) == UNUSEDVERTEX) { + setpointtype(pd, VOLVERTEX); + } + + setpoint2tet(pa, encode(firsttet)); + setpoint2tet(pb, encode(firsttet)); + setpoint2tet(pc, encode(firsttet)); + setpoint2tet(pd, encode(firsttet)); + + // Remember the first tetrahedron. + recenttet = firsttet; } /////////////////////////////////////////////////////////////////////////////// // // -// validatebowatcavityquad() Valid B_i(p). // -// // -// B_i(p) is valid if all faces of B_i(p) are visible by p, else B_i(p) is // -// invalid. Each tet of BC_i(p) which has such a face is marked (uninfect). // -// They will be removed in updatebowatcavityquad(). // -// // -// Return TRUE if B(p) is valid, else, return FALSE. // +// incrementaldelaunay() Create a Delaunay tetrahedralization by // +// the incremental approach. // // // /////////////////////////////////////////////////////////////////////////////// -bool tetgenmesh::validatebowatcavityquad(point bp,list* ceillist,REAL maxcosd) + +void tetgenmesh::incrementaldelaunay(clock_t& tv) { - triface ceiltet; - point pa, pb, pc; - REAL ori, cosd; - int remcount, i; + triface searchtet; + point *permutarray, swapvertex; + REAL v1[3], v2[3], n[3]; + REAL bboxsize, bboxsize2, bboxsize3, ori; + int randindex; + int ngroup = 0; + int i, j; - // Check the validate of B(p), cut tets having invisible faces. - remcount = 0; - for (i = 0; i < ceillist->len(); i++) { - ceiltet = * (triface *)(* ceillist)[i]; - if (infected(ceiltet)) { - adjustedgering(ceiltet, CCW); - pa = org(ceiltet); - pb = dest(ceiltet); - pc = apex(ceiltet); - ori = orient3d(pa, pb, pc, bp); - if (ori >= 0.0) { - // Found an invisible face. - uninfect(ceiltet); - remcount++; - continue; - } - // If a non-trival 'maxcosd' is given. - if (maxcosd > -1.0) { - // Get the maximal dihedral angle of tet abcp. - tetalldihedral(pa, pb, pc, bp, NULL, &cosd, NULL); - // Do not form the tet if the maximal dihedral angle is not reduced. - if (cosd < maxcosd) { - uninfect(ceiltet); - remcount++; - } + if (!b->quiet) { + printf("Delaunizing vertices...\n"); + } + + // Form a random permuation (uniformly at random) of the set of vertices. + permutarray = new point[in->numberofpoints]; + points->traversalinit(); + + if (b->no_sort) { + if (b->verbose) { + printf(" Using the input order.\n"); + } + for (i = 0; i < in->numberofpoints; i++) { + permutarray[i] = (point) points->traverse(); + } + } else { + if (b->verbose) { + printf(" Permuting vertices.\n"); + } + srand(in->numberofpoints); + for (i = 0; i < in->numberofpoints; i++) { + randindex = rand() % (i + 1); // randomnation(i + 1); + permutarray[i] = permutarray[randindex]; + permutarray[randindex] = (point) points->traverse(); + } + if (b->brio_hilbert) { // -b option + if (b->verbose) { + printf(" Sorting vertices.\n"); } + hilbert_init(in->mesh_dim); + brio_multiscale_sort(permutarray, in->numberofpoints, b->brio_threshold, + b->brio_ratio, &ngroup); } } - return remcount == 0; -} -/////////////////////////////////////////////////////////////////////////////// -// // -// updatebowatcavityquad() Update BC_i(p) and reform B_i(p). // -// // -// B_i(p) is invalid and some tets in BC_i(p) have been marked to be removed // -// in validatebowatcavityquad(). This routine actually remove the cut tets // -// of BC_i(p) and re-form the B_i(p). // -// // -/////////////////////////////////////////////////////////////////////////////// + tv = clock(); // Remember the time for sorting points. -void tetgenmesh::updatebowatcavityquad(list* tetlist, list* ceillist) -{ - triface cavtet, neightet; - face checksh; - int remcount, i; + // Calculate the diagonal size of its bounding box. + bboxsize = sqrt(norm2(xmax - xmin, ymax - ymin, zmax - zmin)); + bboxsize2 = bboxsize * bboxsize; + bboxsize3 = bboxsize2 * bboxsize; - remcount = 0; - for (i = 0; i < tetlist->len(); i++) { - cavtet = * (triface *)(* tetlist)[i]; - if (!infected(cavtet)) { - tetlist->del(i, 1); - remcount++; - i--; + // Make sure the second vertex is not identical with the first one. + i = 1; + while ((distance(permutarray[0],permutarray[i])/bboxsize)epsilon) { + i++; + if (i == in->numberofpoints - 1) { + printf("Exception: All vertices are (nearly) identical (Tol = %g).\n", + b->epsilon); + terminatetetgen(this, 10); } } + if (i > 1) { + // Swap to move the non-identical vertex from index i to index 1. + swapvertex = permutarray[i]; + permutarray[i] = permutarray[1]; + permutarray[1] = swapvertex; + } - // Are there tets have been cut in BC_i(p)? - if (remcount > 0) { - // Re-form B_i(p). - ceillist->clear(); - for (i = 0; i < tetlist->len(); i++) { - cavtet = * (triface *)(* tetlist)[i]; - for (cavtet.loc = 0; cavtet.loc < 4; cavtet.loc++) { - tspivot(cavtet, checksh); - if (checksh.sh == dummysh) { - sym(cavtet, neightet); - if (!infected(neightet)) { - ceillist->append(&cavtet); // Found a face of B_i(p). - } - } else { - // Do not cross a boundary face. - if (!sinfected(checksh)) { - ceillist->append(&cavtet); // Found a face of B_i(p). - } - } - } + // Make sure the third vertex is not collinear with the first two. + // Acknowledgement: Thanks Jan Pomplun for his correction by using + // epsilon^2 and epsilon^3 (instead of epsilon). 2013-08-15. + i = 2; + for (j = 0; j < 3; j++) { + v1[j] = permutarray[1][j] - permutarray[0][j]; + v2[j] = permutarray[i][j] - permutarray[0][j]; + } + cross(v1, v2, n); + while ((sqrt(norm2(n[0], n[1], n[2])) / bboxsize2) < + (b->epsilon * b->epsilon)) { + i++; + if (i == in->numberofpoints - 1) { + printf("Exception: All vertices are (nearly) collinear (Tol = %g).\n", + b->epsilon); + terminatetetgen(this, 10); } - if (b->verbose > 2) { - printf(" Update BC_i(p): %d tets, %d faces.\n", tetlist->len(), - ceillist->len()); + for (j = 0; j < 3; j++) { + v2[j] = permutarray[i][j] - permutarray[0][j]; } + cross(v1, v2, n); + } + if (i > 2) { + // Swap to move the non-identical vertex from index i to index 1. + swapvertex = permutarray[i]; + permutarray[i] = permutarray[2]; + permutarray[2] = swapvertex; } -} -/////////////////////////////////////////////////////////////////////////////// -// // -// updatebowatcavitysub() Check and update CBC(p) and C(p). // -// // -// A CBC(p) is valid if all its subfaces are inside or on the hull of BC(p). // -// A subface s of CBC(p) is invalid if it is in one of the two cases: // -// (1) s is completely outside BC(p); // -// (2) s has two adjacent tets but only one of them is in BC(p); // -// s is removed from CBC(p) if it is invalid. If there is an adjacent tet of // -// s which is in BC(p), it gets removed from BC(p) too. If CBC(p) is updated,// -// C(p) is re-formed. // -// // -// A C(p) is valid if all its edges are on the hull of BC(p). An edge e of // -// C(p) may be inside BC(p) if e is a segment and belongs to only one facet. // -// To correct C(p), a tet of BC(p) which shields e gets removed. // -// // -// If BC(p) is formed with locally non-Delaunay check (b->conformdel > 0). // -// A boundary-consistent check is needed for non-segment edges of C(p). Let // -// e be such an edge, the subface f contains e and outside C(p) may belong // -// to B(p) due to the non-coplanarity of the facet definition. The tet of // -// BC(p) containing f gets removed to avoid creating a degenerate new tet. // -// // -// 'cutcount' accumulates the total number of cuttets(not only by this call).// -// // -/////////////////////////////////////////////////////////////////////////////// + // Make sure the fourth vertex is not coplanar with the first three. + i = 3; + ori = orient3dfast(permutarray[0], permutarray[1], permutarray[2], + permutarray[i]); + while ((fabs(ori) / bboxsize3) < (b->epsilon * b->epsilon * b->epsilon)) { + i++; + if (i == in->numberofpoints) { + printf("Exception: All vertices are coplanar (Tol = %g).\n", + b->epsilon); + terminatetetgen(this, 10); + } + ori = orient3dfast(permutarray[0], permutarray[1], permutarray[2], + permutarray[i]); + } + if (i > 3) { + // Swap to move the non-identical vertex from index i to index 1. + swapvertex = permutarray[i]; + permutarray[i] = permutarray[3]; + permutarray[3] = swapvertex; + } -void tetgenmesh::updatebowatcavitysub(list* sublist, list* subceillist, - int* cutcount) -{ - triface adjtet, rotface; - face checksh, neighsh; - face checkseg; - point pa, pb, pc; - REAL ori1, ori2; - int remcount; - int i, j; + // Orient the first four vertices in permutarray so that they follow the + // right-hand rule. + if (ori > 0.0) { + // Swap the first two vertices. + swapvertex = permutarray[0]; + permutarray[0] = permutarray[1]; + permutarray[1] = swapvertex; + } - remcount = 0; - // Check the validity of CBC(p). - for (i = 0; i < sublist->len(); i++) { - checksh = * (face *)(* sublist)[i]; - // Check two adjacent tets of s. - for (j = 0; j < 2; j++) { - stpivot(checksh, adjtet); - if (adjtet.tet != dummytet) { - if (!infected(adjtet)) { - // Could be either case (1) or (2). - suninfect(checksh); // s survives. - // If the sym. adjtet exists, it should remove from BC(p) too. - sesymself(checksh); - stpivot(checksh, adjtet); - if (adjtet.tet != dummytet) { - if (infected(adjtet)) { - // Found an adj. tet in BC(p), remove it. - uninfect(adjtet); - (*cutcount)++; - } - } - // Remove s from C(p). - sublist->del(i, 1); - i--; - remcount++; - break; - } - } - sesymself(checksh); - } + // Create the initial Delaunay tetrahedralization. + initialdelaunay(permutarray[0], permutarray[1], permutarray[2], + permutarray[3]); + + if (b->verbose) { + printf(" Incrementally inserting vertices.\n"); } - 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); + insertvertexflags ivf; + flipconstraints fc; + + // Choose algorithm: Bowyer-Watson (default) or Incremental Flip + if (b->incrflip) { + ivf.bowywat = 0; + ivf.lawson = 1; + fc.enqflag = 1; + } else { + ivf.bowywat = 1; + ivf.lawson = 0; + } + + + for (i = 4; i < in->numberofpoints; i++) { + if (pointtype(permutarray[i]) == UNUSEDVERTEX) { + setpointtype(permutarray[i], VOLVERTEX); } - // Re-generate C(p). - subceillist->clear(); - for (i = 0; i < sublist->len(); i++) { - checksh = * (face *)(* sublist)[i]; - for (j = 0; j < 3; j++) { - spivot(checksh, neighsh); - if (!sinfected(neighsh)) { - subceillist->append(&checksh); - } - senextself(checksh); - } + if (b->brio_hilbert || b->no_sort) { // -b or -b/1 + // Start the last updated tet. + searchtet.tet = recenttet.tet; + } else { // -b0 + // Randomly choose the starting tet for point location. + searchtet.tet = NULL; } - if (b->verbose > 2) { - printf(" Update CBC(p): %d subs, %d edges.\n", sublist->len(), - subceillist->len()); - } - } - - // Check the validity of C(p). - for (i = 0; i < subceillist->len(); i++) { - checksh = * (face *)(* subceillist)[i]; - sspivot(checksh, checkseg); - if (checkseg.sh != dummysh) { - // A segment. Check if it is inside BC(p). - stpivot(checksh, adjtet); - if (adjtet.tet == dummytet) { - sesym(checksh, neighsh); - stpivot(neighsh, adjtet); - } - findedge(&adjtet, sorg(checkseg), sdest(checkseg)); - adjustedgering(adjtet, CCW); - fnext(adjtet, rotface); // It's the same tet. - // Rotate rotface (f), stop on either of the following cases: - // (a) meet a subface, or - // (b) enter an uninfected tet, or - // (c) rewind back to adjtet. - do { - if (!infected(rotface)) break; // case (b) - tspivot(rotface, neighsh); - if (neighsh.sh != dummysh) break; // case (a) - // Go to the next tet of the facing ring. - fnextself(rotface); - } while (apex(rotface) != apex(adjtet)); - // Is it case (c)? - if (apex(rotface) == apex(adjtet)) { - // The segment is enclosed by BC(p), invalid cavity. - pa = org(adjtet); - pb = dest(adjtet); - pc = apex(adjtet); - // Find the shield tet and cut it. Notice that the shield tet may - // not be unique when there are four coplanar points, ie., - // ori1 * ori2 == 0.0. In such case, choose either of them. - fnext(adjtet, rotface); - do { - fnextself(rotface); - assert(infected(rotface)); - ori1 = orient3d(pa, pb, pc, apex(rotface)); - ori2 = orient3d(pa, pb, pc, oppo(rotface)); - } while (ori1 * ori2 > 0.0); - // Cut this tet from BC(p). - uninfect(rotface); - (*cutcount)++; + ivf.iloc = (int) OUTSIDE; + // Insert the vertex. + if (insertpoint(permutarray[i], &searchtet, NULL, NULL, &ivf)) { + if (flipstack != NULL) { + // Perform flip to recover Delaunayness. + incrementalflip(permutarray[i], (ivf.iloc == (int) OUTSIDE), &fc); } } else { - /*// An edge. Check if boundary-consistency should be enforced. - if (b->conformdel > 0) { - // Get the adj-sub n at e, it must be outside C(p). - spivot(checksh, neighsh); - assert(!sinfected(neighsh)); - // Check if n is on B(p). - for (j = 0; j < 2; j++) { - stpivot(neighsh, adjtet); - if (adjtet.tet != dummytet) { - if (infected(adjtet)) { - uninfect(adjtet); - (*cutcount)++; - } + if (ivf.iloc == (int) ONVERTEX) { + // The point already exists. Mark it and do nothing on it. + swapvertex = org(searchtet); + assert(swapvertex != permutarray[i]); // SELF_CHECK + if (b->object != tetgenbehavior::STL) { + if (!b->quiet) { + printf("Warning: Point #%d is coincident with #%d. Ignored!\n", + pointmark(permutarray[i]), pointmark(swapvertex)); } - sesymself(neighsh); } - } */ + setpoint2ppt(permutarray[i], swapvertex); + setpointtype(permutarray[i], DUPLICATEDVERTEX); + dupverts++; + } else if (ivf.iloc == (int) NEARVERTEX) { + swapvertex = point2ppt(permutarray[i]); + if (!b->quiet) { + printf("Warning: Point %d is replaced by point %d.\n", + pointmark(permutarray[i]), pointmark(swapvertex)); + printf(" Avoid creating a very short edge (len = %g) (< %g).\n", + permutarray[i][3], b->minedgelength); + printf(" You may try a smaller tolerance (-T) (current is %g)\n", + b->epsilon); + printf(" or use the option -M0/1 to avoid such replacement.\n"); + } + // Remember it is a duplicated point. + setpointtype(permutarray[i], DUPLICATEDVERTEX); + // Count the number of duplicated points. + dupverts++; + } } } + + + + delete [] permutarray; } +//// //// +//// //// +//// delaunay_cxx ///////////////////////////////////////////////////////////// + +//// surface_cxx ////////////////////////////////////////////////////////////// +//// //// +//// //// + /////////////////////////////////////////////////////////////////////////////// // // -// trimbowatcavity() Validate B(p), CBC(p)s and C(p)s, update BC(p). // +// flipshpush() Push a facet edge into flip stack. // // // -// A B(p) is valid if all its faces are visible by p. If a face f of B(p) is // -// found invisible by p, the tet of BC(p) containing f gets removed and B(p) // -// is refromed. The new B(p) may still contain invisible faces by p. Iterat- // -// ively do the above procedure until B(p) is satisfied. // -// // -// A CBC(p) is valid if each subface of CBC(p) is either on the hull of BC(p)// -// or completely inside BC(p). If a subface s of CBC(p) is not valid, it is // -// removed from CBC(p) and C(p) is reformed. If there exists a tet t of BC(p)// -// containg s, t is removed from BC(p). The process for validating BC(p) and // -// B(p) is re-excuted. // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::flipshpush(face* flipedge) +{ + badface *newflipface; + + newflipface = (badface *) flippool->alloc(); + newflipface->ss = *flipedge; + newflipface->forg = sorg(*flipedge); + newflipface->fdest = sdest(*flipedge); + newflipface->nextitem = flipstack; + flipstack = newflipface; +} + +/////////////////////////////////////////////////////////////////////////////// // // -// A C(p) is valid if each edge of C(p) is on the hull of BC(p). If an edge // -// e of C(p) is invalid (e should be a subsegment which only belong to one // -// facet), a tet of BC(p) which contains e and has two other faces shielding // -// e is removed. The process for validating BC(p) and B(p) is re-excuted. // +// flip22() Perform a 2-to-2 flip in surface mesh. // // // -// If either BC(p) or CBC(p) becomes empty. No valid BC(p) is found, return // -// FALSE. else, return TRUE. // +// 'flipfaces' is an array of two subfaces. On input, they are [a,b,c] and // +// [b,a,d]. On output, they are [c,d,b] and [d,c,a]. As a result, edge [a,b] // +// is replaced by edge [c,d]. // // // /////////////////////////////////////////////////////////////////////////////// -bool tetgenmesh::trimbowatcavity(point bp, face* bpseg, int n, list** sublists, - list** subceillists, list** tetlists, list** ceillists, REAL maxcosd) +void tetgenmesh::flip22(face* flipfaces, int flipflag, int chkencflag) { - bool valflag; - int oldnum, cutnum, cutcount; + face bdedges[4], outfaces[4], infaces[4]; + face bdsegs[4]; + face checkface; + point pa, pb, pc, pd; int i; - cutnum = 0; // Count the total number of cut-off tets of BC(p). - valflag = true; + pa = sorg(flipfaces[0]); + pb = sdest(flipfaces[0]); + pc = sapex(flipfaces[0]); + pd = sapex(flipfaces[1]); - do { - // Validate BC(p), B(p). - for (i = 0; i < n && valflag; i++) { - oldnum = tetlists[i]->len(); - // Iteratively validate BC_i(p) and B_i(p). - while (!validatebowatcavityquad(bp, ceillists[i], maxcosd)) { - // Update BC_i(p) and B_i(p). - updatebowatcavityquad(tetlists[i], ceillists[i]); - valflag = tetlists[i]->len() > 0; - } - cutnum += (oldnum - tetlists[i]->len()); - } - if (valflag && (sublists != (list **) NULL)) { - // Validate CBC(p), C(p). - cutcount = 0; - for (i = 0; i < n; i++) { - updatebowatcavitysub(sublists[i], subceillists[i], &cutcount); - // Only do once if p is on a facet. - if (bpseg == (face *) NULL) break; - } - // Are there cut tets? - if (cutcount > 0) { - // Squeeze all cut tets in BC(p), keep valflag once it gets FLASE. - for (i = 0; i < n; i++) { - if (tetlists[i]->len() > 0) { - updatebowatcavityquad(tetlists[i], ceillists[i]); - if (valflag) { - valflag = tetlists[i]->len() > 0; - } - } + if (sorg(flipfaces[1]) != pb) { + sesymself(flipfaces[1]); + } + + flip22count++; + + // Collect the four boundary edges. + senext(flipfaces[0], bdedges[0]); + senext2(flipfaces[0], bdedges[1]); + senext(flipfaces[1], bdedges[2]); + senext2(flipfaces[1], bdedges[3]); + + // Collect outer boundary faces. + for (i = 0; i < 4; i++) { + spivot(bdedges[i], outfaces[i]); + infaces[i] = outfaces[i]; + sspivot(bdedges[i], bdsegs[i]); + if (outfaces[i].sh != NULL) { + if (isshsubseg(bdedges[i])) { + spivot(infaces[i], checkface); + while (checkface.sh != bdedges[i].sh) { + infaces[i] = checkface; + spivot(infaces[i], checkface); } - cutnum += cutcount; - // Go back to valid the updated BC(p). - continue; } } - break; // Leave the while-loop. - } while (true); + } - // Check if any CBC(p) becomes non-empty. - if (valflag && (sublists != (list **) NULL)) { - for (i = 0; i < n && valflag; i++) { - valflag = (sublists[i]->len() > 0); - if (bpseg == (face *) NULL) break; - } + // The flags set in these two subfaces do not change. + // Shellmark does not change. + // area constraint does not change. + + // Transform [a,b,c] -> [c,d,b]. + setshvertices(flipfaces[0], pc, pd, pb); + // Transform [b,a,d] -> [d,c,a]. + setshvertices(flipfaces[1], pd, pc, pa); + + // Update the point-to-subface map. + if (pointtype(pa) == FREEFACETVERTEX) { + setpoint2sh(pa, sencode(flipfaces[1])); + } + if (pointtype(pb) == FREEFACETVERTEX) { + setpoint2sh(pb, sencode(flipfaces[0])); + } + if (pointtype(pc) == FREEFACETVERTEX) { + setpoint2sh(pc, sencode(flipfaces[0])); + } + if (pointtype(pd) == FREEFACETVERTEX) { + setpoint2sh(pd, sencode(flipfaces[0])); } - if (valflag && (cutnum > 0)) { - // Accumulate counters. - if (bpseg != (face *) NULL) { - updsegcount++; - } else if (sublists != (list **) NULL) { - updsubcount++; + // Reconnect boundary edges to outer boundary faces. + for (i = 0; i < 4; i++) { + if (outfaces[(3 + i) % 4].sh != NULL) { + // Make sure that the subface has the ori as the segment. + if (bdsegs[(3 + i) % 4].sh != NULL) { + bdsegs[(3 + i) % 4].shver = 0; + if (sorg(bdedges[i]) != sorg(bdsegs[(3 + i) % 4])) { + sesymself(bdedges[i]); + } + } + sbond1(bdedges[i], outfaces[(3 + i) % 4]); + sbond1(infaces[(3 + i) % 4], bdedges[i]); + } else { + sdissolve(bdedges[i]); + } + if (bdsegs[(3 + i) % 4].sh != NULL) { + ssbond(bdedges[i], bdsegs[(3 + i) % 4]); + if (chkencflag & 1) { + // Queue this segment for encroaching check. + enqueuesubface(badsubsegs, &(bdsegs[(3 + i) % 4])); + } } else { - updvolcount++; + ssdissolve(bdedges[i]); } } - if (!valflag) { - // Accumulate counters. - if (bpseg != (face *) NULL) { - failsegcount++; - } else if (sublists != (list **) NULL) { - failsubcount++; - } else { - failvolcount++; + if (chkencflag & 2) { + // Queue the flipped subfaces for quality/encroaching checks. + for (i = 0; i < 2; i++) { + enqueuesubface(badsubfacs, &(flipfaces[i])); } } - return valflag; + recentsh = flipfaces[0]; + + if (flipflag) { + // Put the boundary edges into flip stack. + for (i = 0; i < 4; i++) { + flipshpush(&(bdedges[i])); + } + } } /////////////////////////////////////////////////////////////////////////////// // // -// bowatinsertsite() Insert a point using the Bowyer-Watson method. // -// // -// Parameters: 'bp' = p, 'splitseg' = S, 'n' = the number of quadrants, // -// 'sublists', an array of CBC_i(p)s, 'subceillists', an array of C_i(p)s, // -// 'tetlists', an array of BC_i(p)s, 'ceillists', an array of B_i(p)s. // +// flip31() Remove a vertex by transforming 3-to-1 subfaces. // // // -// If p is inside the mesh domain, then S = NULL, n = 1, CBC(p) and C(p) are // -// NULLs. 'tetlists[0]' = BC(p), 'ceillists[0]' = B(p). // -// If p is on a facet F, then S = NULL, n = 2, and 'subceillists[0]' = C(p), // -// 'subceillists[1]' is not needed (set it to NULL). B_1(p) and B_2(p) are // -// in 'ceillists[0]' and 'ceillists[1]'. // -// If p is on a segment S, then F(S) is a list of subfaces around S, and n = // -// len(F(S)), there are n C_i(p)s and B_i(p)s supplied in 'subceillists[i]'// -// and 'ceillists[i]'. // +// 'flipfaces' is an array of subfaces. Its length is at least 4. On input, // +// the first three faces are: [p,a,b], [p,b,c], and [p,c,a]. This routine // +// replaces them by one face [a,b,c], it is returned in flipfaces[3]. // // // -// If 'verlist' != NULL, it returns a list of vertices which connect to p. // -// This vertices are used for interpolating size of p. // -// // -// If 'flipque' != NULL, it returns a list of internal faces of new tets in // -// BC(p), faces on C(p)s are excluded. These faces may be locally non- // -// Delaunay and will be flipped if they are flippable. Such non-Delaunay // -// faces may exist when p is inserted to split an encroaching segment. // -// // -// 'chkencseg', 'chkencsub', and 'chkbadtet' are flags that indicate whether // -// or not there should be checks for the creation of encroached subsegments, // -// subfaces, or bad quality tets. If 'chkencseg' = TRUE, the encroached sub- // -// segments are added to the list of subsegments to be split. // -// // -// On return, 'ceillists' returns Star(p). // +// NOTE: The three old subfaces are not deleted within this routine. They // +// still hold pointers to their adjacent subfaces. These informations are // +// needed by the routine 'sremovevertex()' for recovering a segment. // +// The caller of this routine must delete the old subfaces after their uses. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::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) +void tetgenmesh::flip31(face* flipfaces, int flipflag) { - list *ceillist, *subceillist; - triface oldtet, newtet, newface, rotface, neightet; - face oldsh, newsh, newedge, checksh; - face spinsh, casingin, casingout; - face *apsegshs, *pbsegshs; - face apseg, pbseg, checkseg; + face bdedges[3], outfaces[3], infaces[3]; + face bdsegs[3]; + face checkface; point pa, pb, pc; - REAL attrib, volume; - int idx, i, j, k; + int i; - if (b->verbose > 1) { - printf(" Insert point %d (%.12g, %.12g, %.12g)", pointmark(bp), bp[0], - bp[1], bp[2]); - } - if (splitseg != (face *) NULL) { - if (b->verbose > 1) { - printf(" on segment.\n"); - } - bowatsegcount++; - } else { - if (subceillists != (list **) NULL) { - if (b->verbose > 1) { - printf(" on facet.\n"); - } - bowatsubcount++; - } else { - if (b->verbose > 1) { - printf(" in volume.\n"); + pa = sdest(flipfaces[0]); + pb = sdest(flipfaces[1]); + pc = sdest(flipfaces[2]); + + flip31count++; + + // Collect all infos at the three boundary edges. + for (i = 0; i < 3; i++) { + senext(flipfaces[i], bdedges[i]); + spivot(bdedges[i], outfaces[i]); + infaces[i] = outfaces[i]; + sspivot(bdedges[i], bdsegs[i]); + if (outfaces[i].sh != NULL) { + if (isshsubseg(bdedges[i])) { + spivot(infaces[i], checkface); + while (checkface.sh != bdedges[i].sh) { + infaces[i] = checkface; + spivot(infaces[i], checkface); + } } - bowatvolcount++; } + } // i + + // Create a new subface. + makeshellface(subfaces, &(flipfaces[3])); + setshvertices(flipfaces[3], pa, pb,pc); + setshellmark(flipfaces[3], shellmark(flipfaces[0])); + if (checkconstraints) { + //area = areabound(flipfaces[0]); + setareabound(flipfaces[3], areabound(flipfaces[0])); + } + if (useinsertradius) { + setfacetindex(flipfaces[3], getfacetindex(flipfaces[0])); } - // Create new tets to fill B(p). - for (k = 0; k < n; k++) { - // Create new tets from each B_i(p). - ceillist = ceillists[k]; - for (i = 0; i < ceillist->len(); i++) { - oldtet = * (triface *)(* ceillist)[i]; - adjustedgering(oldtet, CCW); - pa = org(oldtet); - pb = dest(oldtet); - pc = apex(oldtet); - maketetrahedron(&newtet); - setorg(newtet, pa); - setdest(newtet, pb); - setapex(newtet, pc); - setoppo(newtet, bp); - for (j = 0; j < in->numberoftetrahedronattributes; j++) { - attrib = elemattribute(oldtet.tet, j); - setelemattribute(newtet.tet, j, attrib); - } - if (b->varvolume) { - volume = volumebound(oldtet.tet); - if (volume > 0.0) { - if (!b->fixedvolume && b->refine) { - // '-r -a' switches and a .vol file case. Enlarge the maximum - // volume constraint for the new tets. Hence the new points - // only spread near the original constrained tet. - volume *= 1.2; - } - } - setvolumebound(newtet.tet, volume); - } - sym(oldtet, neightet); - tspivot(oldtet, checksh); - if (neightet.tet != dummytet) { - bond(newtet, neightet); - } - if (checksh.sh != dummysh) { - tsbond(newtet, checksh); - } - if (verlist != (list *) NULL) { - // Collect vertices connecting to p. - idx = pointmark(pa); - if (idx >= 0) { - setpointmark(pa, -idx - 1); - verlist->append(&pa); - } - idx = pointmark(pb); - if (idx >= 0) { - setpointmark(pb, -idx - 1); - verlist->append(&pb); - } - idx = pointmark(pc); - if (idx >= 0) { - setpointmark(pc, -idx - 1); - verlist->append(&pc); - } - } - // Replace the tet by the newtet for checking the quality. - * (triface *)(* ceillist)[i] = newtet; - } - } - if (verlist != (list *) NULL) { - // Uninfect collected vertices. - for (i = 0; i < verlist->len(); i++) { - pa = * (point *)(* verlist)[i]; - idx = pointmark(pa); - setpointmark(pa, -(idx + 1)); - } - } - - // Connect new tets of B(p). Not all faces of new tets can be connected, - // e.g., if there are empty B_i(p)s. - for (k = 0; k < n; k++) { - ceillist = ceillists[k]; - for (i = 0; i < ceillist->len(); i++) { - newtet = * (triface *)(* ceillist)[i]; - newtet.ver = 0; - for (j = 0; j < 3; j++) { - fnext(newtet, newface); - sym(newface, neightet); - if (neightet.tet == dummytet) { - // Find the neighbor face by rotating the faces at edge ab. - esym(newtet, rotface); - pa = org(rotface); - pb = dest(rotface); - while (fnextself(rotface)); - // Do we meet a boundary face? - tspivot(rotface, checksh); - if (checksh.sh != dummysh) { - // Walk through the boundary and continue to rotate faces. - do { - findedge(&checksh, pa, pb); - sfnextself(checksh); - assert((sorg(checksh) == pa) && (sdest(checksh) == pb)); - stpivot(checksh, rotface); - if (infected(rotface)) { - // Meet an old tet of B_i(p). This side is on the hull and - // will be connected to a new subface created in C(p). - break; - } - findedge(&rotface, pa, pb); - while (fnextself(rotface)); - tspivot(rotface, checksh); - } while (checksh.sh != dummysh); - } - // The rotface has edge ab, but it may not have newpt. - if (apex(rotface) == apex(newface)) { - // Bond the two tets together. - bond(newface, rotface); - // Queue (uniquely) this face if 'flipque' is given. - if (flipque != (queue *) NULL) { - enqueueflipface(newface, flipque); - } - } + // Update the point-to-subface map. + if (pointtype(pa) == FREEFACETVERTEX) { + setpoint2sh(pa, sencode(flipfaces[3])); + } + if (pointtype(pb) == FREEFACETVERTEX) { + setpoint2sh(pb, sencode(flipfaces[3])); + } + if (pointtype(pc) == FREEFACETVERTEX) { + setpoint2sh(pc, sencode(flipfaces[3])); + } + + // Update the three new boundary edges. + bdedges[0] = flipfaces[3]; // [a,b] + senext(flipfaces[3], bdedges[1]); // [b,c] + senext2(flipfaces[3], bdedges[2]); // [c,a] + + // Reconnect boundary edges to outer boundary faces. + for (i = 0; i < 3; i++) { + if (outfaces[i].sh != NULL) { + // Make sure that the subface has the ori as the segment. + if (bdsegs[i].sh != NULL) { + bdsegs[i].shver = 0; + if (sorg(bdedges[i]) != sorg(bdsegs[i])) { + sesymself(bdedges[i]); } - enextself(newtet); } + sbond1(bdedges[i], outfaces[i]); + sbond1(infaces[i], bdedges[i]); + } + if (bdsegs[i].sh != NULL) { + ssbond(bdedges[i], bdsegs[i]); } } - if (subceillists != (list **) NULL) { - // There are C(p)s. - if (splitseg != (face *) NULL) { - // S (ab) is split by p. - splitseg->shver = 0; - pa = sorg(*splitseg); - pb = sdest(*splitseg); - // Allcate two arrays for saving the subface rings of the two new - // segments a->p and p->b. - apsegshs = new face[n]; - pbsegshs = new face[n]; + recentsh = flipfaces[3]; + + if (flipflag) { + // Put the boundary edges into flip stack. + for (i = 0; i < 3; i++) { + flipshpush(&(bdedges[i])); } + } +} - // For each C_k(p), do the following: - // (1) Create new subfaces to fill C_k(p), insert them into B(p); - // (2) Connect new subfaces to each other; - for (k = 0; k < n; k++) { - subceillist = subceillists[k]; +/////////////////////////////////////////////////////////////////////////////// +// // +// lawsonflip() Flip non-locally Delaunay edges. // +// // +/////////////////////////////////////////////////////////////////////////////// - // Check if 'hullsize' should be updated. - oldsh = * (face *)(* subceillist)[0]; - stpivot(oldsh, neightet); - if (neightet.tet != dummytet) { - sesymself(oldsh); - stpivot(oldsh, neightet); - } - if (neightet.tet == dummytet) { - // The hull size changes. - hullsize += (subceillist->len() - sublists[k]->len()); - } +long tetgenmesh::lawsonflip() +{ + badface *popface; + face flipfaces[2]; + point pa, pb, pc, pd; + REAL sign; + long flipcount = 0; - // (1) Create new subfaces to fill C_k(p), insert them into B(p). - for (i = 0; i < subceillist->len(); i++) { - oldsh = * (face *)(* subceillist)[i]; - makeshellface(subfaces, &newsh); - setsorg(newsh, sorg(oldsh)); - setsdest(newsh, sdest(oldsh)); - setsapex(newsh, bp); - if (b->quality && varconstraint) { - setareabound(newsh, areabound(oldsh)); - } - setshellmark(newsh, shellmark(oldsh)); - setshelltype(newsh, shelltype(oldsh)); - if (checkpbcs) { - setshellpbcgroup(newsh, shellpbcgroup(oldsh)); - } - // Replace oldsh by newsh at the edge. - 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 (casingout.sh != dummysh) { // 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. - sdissolve(newsh); // sbond(newsh, newsh); - } - // Bond the segment. - ssbond(newsh, checkseg); - } else { - // Bond s <-> s_out (and dissolve s_out -> s_old). - sbond(newsh, casingout); - } - - // Insert newsh into B(p). Use the coonections of oldsh. - stpivot(oldsh, neightet); - if (neightet.tet == dummytet) { - sesymself(oldsh); - sesymself(newsh); // Keep the same orientation as oldsh. - stpivot(oldsh, neightet); - } - assert(infected(neightet)); - // Set on the rotating edge. - findedge(&neightet, sorg(oldsh), sdest(oldsh)); - // Choose the rotating direction (to the inside of B(p)). - adjustedgering(neightet, CCW); - rotface = neightet; - // Rotate face. Stop at a non-infected tet t (not in B(p)) or a - // hull face f (on B(p)). Get the neighbor n of t or f. n is - // a new tet that has just been created to fill B(p). - do { - fnextself(rotface); - sym(rotface, neightet); - if (neightet.tet == dummytet) { - tspivot(rotface, checksh); - assert(checksh.sh != dummysh); - stpivot(checksh, newtet); - break; - } else if (!infected(neightet)) { - sym(neightet, newtet); - break; - } - } while (true); - assert(newtet.tet != rotface.tet); - // Set the rotating edge of n. - findedge(&newtet, sorg(oldsh), sdest(oldsh)); - // Choose the rotating direction (to the inside of B(p)). - adjustedgering(newtet, CCW); - fnext(newtet, newface); - assert(apex(newface) == bp); - // newsh has already been oriented toward n. - tsbond(newface, newsh); - sym(newface, neightet); // 'neightet' maybe outside. - sesymself(newsh); - tsbond(neightet, newsh); // Bond them anyway. + if (b->verbose > 2) { + printf(" Lawson flip %ld edges.\n", flippool->items); + } - // Replace oldsh by newsh in list. - * (face *)(* subceillist)[i] = newsh; - } + while (flipstack != (badface *) NULL) { - // (2) Connect new subfaces to each other. - for (i = 0; i < subceillist->len(); i++) { - // Get a face cdp. - newsh = * (face *)(* subceillist)[i]; - // Get a new tet containing cdp. - stpivot(newsh, newtet); - if (newtet.tet == dummytet) { - sesymself(newsh); - stpivot(newsh, newtet); - } - for (j = 0; j < 2; j++) { - if (j == 0) { - senext(newsh, newedge); // edge dp. - } else { - senext2(newsh, newedge); // edge pc. - sesymself(newedge); // edge cp. - } - if (splitseg != (face *) NULL) { - // Don not operate on newedge if it is ap or pb. - if (sorg(newedge) == pa) { - apsegshs[k] = newedge; - continue; - } else if (sorg(newedge) == pb) { - pbsegshs[k] = newedge; - continue; - } - } - // There should no segment inside the cavity. Check it. - sspivot(newedge, checkseg); - assert(checkseg.sh == dummysh); - spivot(newedge, casingout); - if (casingout.sh == dummysh) { - rotface = newtet; - findedge(&rotface, sorg(newedge), sdest(newedge)); - // Rotate newtet until meeting a new subface which contains - // newedge. It must exist since newedge is not a seg. - adjustedgering(rotface, CCW); - do { - fnextself(rotface); - tspivot(rotface, checksh); - if (checksh.sh != dummysh) break; - } while (true); - findedge(&checksh, sorg(newedge), sdest(newedge)); - sbond(newedge, checksh); - } - } - } - // Only do once if p is on a facet. - if (splitseg == (face *) NULL) break; - } // for (k = 0; k < n; k++) - - if (splitseg != (face *) NULL) { - // Update a->b to be a->p. - apseg = *splitseg; - setsdest(apseg, bp); - // Create a new subsegment p->b. - makeshellface(subsegs, &pbseg); - setsorg(pbseg, bp); - setsdest(pbseg, pb); - // p->b gets the same mark and segment type as a->p. - setshellmark(pbseg, shellmark(apseg)); - setshelltype(pbseg, shelltype(apseg)); - if (b->quality && varconstraint) { - // Copy the area bound into the new subsegment. - setareabound(pbseg, areabound(apseg)); - } - senext(apseg, checkseg); - // Get the old connection at b of a->b. - spivot(checkseg, casingout); - // Bond a->p and p->b together. - senext2(pbseg, casingin); - sbond(casingin, checkseg); - if (casingout.sh != dummysh) { - // There is a subsegment connect at b of p->b. - casingout.shver = 0; -#ifdef SELF_CHECK - assert(sorg(casingout) == pb); -#endif - senext2self(casingout); - senext(pbseg, casingin); - sbond(casingin, casingout); - } - - // Bond all new subfaces to a->p and p->b. - for (i = 0; i < n; i++) { - spinsh = apsegshs[i]; - findedge(&spinsh, pa, bp); - ssbond(spinsh, apseg); - spinsh = pbsegshs[i]; - findedge(&spinsh, bp, pb); - ssbond(spinsh, pbseg); - } - // Bond all subfaces share at a->p together. - for (i = 0; i < n; i++) { - spinsh = apsegshs[i]; - if (i < (n - 1)) { - casingout = apsegshs[i + 1]; - } else { - casingout = apsegshs[0]; - } - sbond1(spinsh, casingout); - } - // Bond all subfaces share at p->b together. - for (i = 0; i < n; i++) { - spinsh = pbsegshs[i]; - if (i < (n - 1)) { - casingout = pbsegshs[i + 1]; - } else { - casingout = pbsegshs[0]; - } - sbond1(spinsh, casingout); - } - delete [] apsegshs; - delete [] pbsegshs; + // Pop an edge from the stack. + popface = flipstack; + flipfaces[0] = popface->ss; + pa = popface->forg; + pb = popface->fdest; + flipstack = popface->nextitem; // The next top item in stack. + flippool->dealloc((void *) popface); - // Check for newly encroached subsegments if the flag is set. - if (chkencseg) { - // Check if a->p and p->b are encroached by other vertices. - checkseg4encroach(&apseg, NULL, NULL, true); - checkseg4encroach(&pbseg, NULL, NULL, true); - // Check if the adjacent segments are encroached by p. - tallencsegs(bp, n, ceillists); - } - } // if (splitseg != (face *) NULL) + // Skip it if it is dead. + if (flipfaces[0].sh[3] == NULL) continue; + // Skip it if it is not the same edge as we saved. + if ((sorg(flipfaces[0]) != pa) || (sdest(flipfaces[0]) != pb)) continue; + // Skip it if it is a subsegment. + if (isshsubseg(flipfaces[0])) continue; - // 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; - } + // Get the adjacent face. + spivot(flipfaces[0], flipfaces[1]); + if (flipfaces[1].sh == NULL) continue; // Skip a hull edge. + pc = sapex(flipfaces[0]); + pd = sapex(flipfaces[1]); - // 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) + sign = incircle3d(pa, pb, pc, pd); - // 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); + if (sign < 0) { + // It is non-locally Delaunay. Flip it. + flip22(flipfaces, 1, 0); + flipcount++; } - // 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 (b->verbose > 2) { + printf(" Performed %ld flips.\n", flipcount); } - 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); - } + return flipcount; } -//// //// -//// //// -//// flip_cxx ///////////////////////////////////////////////////////////////// - -//// delaunay_cxx ///////////////////////////////////////////////////////////// -//// //// -//// //// - /////////////////////////////////////////////////////////////////////////////// // // -// btree_sort() Sort vertices using a binary space partition (bsp) tree. // +// sinsertvertex() Insert a vertex into a triangulation of a facet. // +// // +// This function uses three global arrays: 'caveshlist', 'caveshbdlist', and // +// 'caveshseglist'. On return, 'caveshlist' contains old subfaces in C(p), // +// 'caveshbdlist' contains new subfaces in C(p). If the new point lies on a // +// segment, 'cavesegshlist' returns the two new subsegments. // +// // +// 'iloc' suggests the location of the point. If it is OUTSIDE, this routine // +// will first locate the point. It starts searching from 'searchsh' or 'rec- // +// entsh' if 'searchsh' is NULL. // +// // +// If 'bowywat' is set (1), the Bowyer-Watson algorithm is used to insert // +// the vertex. Otherwise, only insert the vertex in the initial cavity. // +// // +// If 'iloc' is 'INSTAR', this means the cavity of this vertex was already // +// provided in the list 'caveshlist'. // +// // +// If 'splitseg' is not NULL, the new vertex lies on the segment and it will // +// be split. 'iloc' must be either 'ONEDGE' or 'INSTAR'. // +// // +// 'rflag' (rounding) is a parameter passed to slocate() function. If it is // +// set, after the location of the point is found, either ONEDGE or ONFACE, // +// round the result using an epsilon. // +// // +// NOTE: the old subfaces in C(p) are not deleted. They're needed in case we // +// want to remove the new point immediately. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::btree_sort(point* vertexarray, int arraysize, int axis, - REAL bxmin, REAL bxmax, REAL bymin, REAL bymax, REAL bzmin, REAL bzmax, - int depth) +int tetgenmesh::sinsertvertex(point insertpt, face *searchsh, face *splitseg, + int iloc, int bowywat, int rflag) { - point *leftarray, *rightarray; - point **pptary, swapvert; - REAL split; - bool lflag, rflag; - int i, j, k; + face cavesh, neighsh, *parysh; + face newsh, casout, casin; + face checkseg; + point pa, pb; + enum locateresult loc = OUTSIDE; + REAL sign, ori; + int i, j; 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")); + printf(" Insert facet point %d.\n", pointmark(insertpt)); } - if (depth > max_btree_depth) { - max_btree_depth = depth; + if (bowywat == 3) { + loc = INSTAR; } - 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 ((splitseg != NULL) && (splitseg->sh != NULL)) { + // A segment is going to be split, no point location. + spivot(*splitseg, *searchsh); + if (loc != INSTAR) loc = ONEDGE; } else { - // Split along z-axis. - split = 0.5 * (bzmin + bzmax); + if (loc != INSTAR) loc = (enum locateresult) iloc; + if (loc == OUTSIDE) { + // Do point location in surface mesh. + if (searchsh->sh == NULL) { + *searchsh = recentsh; + } + // Search the vertex. An above point must be provided ('aflag' = 1). + loc = slocate(insertpt, searchsh, 1, 1, rflag); + } } - i = 0; - j = arraysize - 1; - // Partition the vertices into left- and right-arraies. - do { - for (; i < arraysize; i++) { - if (vertexarray[i][axis] >= split) { - break; - } + // Form the initial sC(p). + if (loc == ONFACE) { + // Add the face into list (in B-W cavity). + smarktest(*searchsh); + caveshlist->newindex((void **) &parysh); + *parysh = *searchsh; + } else if (loc == ONEDGE) { + if ((splitseg != NULL) && (splitseg->sh != NULL)) { + splitseg->shver = 0; + pa = sorg(*splitseg); + } else { + pa = sorg(*searchsh); } - for (; j >= 0; j--) { - if (vertexarray[j][axis] < split) { - break; + if (searchsh->sh != NULL) { + // Collect all subfaces share at this edge. + neighsh = *searchsh; + while (1) { + // Adjust the origin of its edge to be 'pa'. + if (sorg(neighsh) != pa) sesymself(neighsh); + // Add this face into list (in B-W cavity). + smarktest(neighsh); + caveshlist->newindex((void **) &parysh); + *parysh = neighsh; + // Add this face into face-at-splitedge list. + cavesegshlist->newindex((void **) &parysh); + *parysh = neighsh; + // Go to the next face at the edge. + spivotself(neighsh); + // Stop if all faces at the edge have been visited. + if (neighsh.sh == searchsh->sh) break; + if (neighsh.sh == NULL) break; + } + } // If (not a non-dangling segment). + } else if (loc == ONVERTEX) { + return (int) loc; + } else if (loc == OUTSIDE) { + // Comment: This should only happen during the surface meshing step. + // Enlarge the convex hull of the triangulation by including p. + // An above point of the facet is set in 'dummypoint' to replace + // orient2d tests by orient3d tests. + // Imagine that the current edge a->b (in 'searchsh') is horizontal in a + // plane, and a->b is directed from left to right, p lies above a->b. + // Find the right-most edge of the triangulation which is visible by p. + neighsh = *searchsh; + while (1) { + senext2self(neighsh); + spivot(neighsh, casout); + if (casout.sh == NULL) { + // A convex hull edge. Is it visible by p. + ori = orient3d(sorg(neighsh), sdest(neighsh), dummypoint, insertpt); + if (ori < 0) { + *searchsh = neighsh; // Visible, update 'searchsh'. + } else { + break; // 'searchsh' is the right-most visible edge. + } + } else { + if (sorg(casout) != sdest(neighsh)) sesymself(casout); + neighsh = casout; } } - // Is the partition finished? - if (i == (j + 1)) { - break; - } - // Swap i-th and j-th vertices. - swapvert = vertexarray[i]; - vertexarray[i] = vertexarray[j]; - vertexarray[j] = swapvert; - // Continue patitioning the array; - } while (true); - - if (b->verbose > 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); + // Create new triangles for all visible edges of p (from right to left). + casin.sh = NULL; // No adjacent face at right. + pa = sorg(*searchsh); + pb = sdest(*searchsh); + while (1) { + // Create a new subface on top of the (visible) edge. + makeshellface(subfaces, &newsh); + setshvertices(newsh, pb, pa, insertpt); + setshellmark(newsh, shellmark(*searchsh)); + if (checkconstraints) { + //area = areabound(*searchsh); + setareabound(newsh, areabound(*searchsh)); } - } 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 (useinsertradius) { + setfacetindex(newsh, getfacetindex(*searchsh)); } - } else { - rflag = true; - } - // } else { - // // Both left and right are done. - // lflag = rflag = true; - // } - - if (lflag && (i > 0)) { - // Remember the maximal length of the partitions. - if (i > max_btreenode_size) { - max_btreenode_size = i; - } - // Allocate space for the left array (use the first entry to save - // the length of this array). - leftarray = new point[i + 1]; - leftarray[0] = (point) i; // The array lenth. - // Put all points in this array. - for (k = 0; k < i; k++) { - leftarray[k + 1] = vertexarray[k]; - setpoint2ppt(leftarray[k + 1], (point) leftarray); - } - // Save this array in list. - btreenode_list->newindex((void **) &pptary); - *pptary = leftarray; - } - - // Get the length of the right array. - j = arraysize - i; - if (rflag && (j > 0)) { - if (j > max_btreenode_size) { - max_btreenode_size = j; - } - // Allocate space for the right array (use the first entry to save - // the length of this array). - rightarray = new point[j + 1]; - rightarray[0] = (point) j; // The array lenth. - // 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; + // Connect the new subface to the bottom subfaces. + sbond1(newsh, *searchsh); + sbond1(*searchsh, newsh); + // Connect the new subface to its right-adjacent subface. + if (casin.sh != NULL) { + 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 (inside the B-W cavity). + smarktest(newsh); + caveshlist->newindex((void **) &parysh); + *parysh = newsh; + // Move to the convex hull edge at the left of 'searchsh'. + neighsh = *searchsh; + while (1) { + senextself(neighsh); + spivot(neighsh, casout); + if (casout.sh == NULL) { + *searchsh = neighsh; + break; + } + if (sorg(casout) != sdest(neighsh)) sesymself(casout); + neighsh = casout; + } + // A convex hull edge. Is it visible by p. + pa = sorg(*searchsh); + pb = sdest(*searchsh); + ori = orient3d(pa, pb, dummypoint, insertpt); + // Finish the process if p is not visible by the hull edge. + if (ori >= 0) break; + } + } else if (loc == INSTAR) { + // Under this case, the sub-cavity sC(p) has already been formed in + // insertvertex(). } -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// 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); -} + // Form the Bowyer-Watson cavity sC(p). + for (i = 0; i < caveshlist->objects; i++) { + cavesh = * (face *) fastlookup(caveshlist, i); + for (j = 0; j < 3; j++) { + if (!isshsubseg(cavesh)) { + spivot(cavesh, neighsh); + if (neighsh.sh != NULL) { + // The adjacent face exists. + if (!smarktested(neighsh)) { + if (bowywat) { + if (loc == INSTAR) { // if (bowywat > 2) { + // It must be a boundary edge. + sign = 1; + } else { + // Check if this subface is connected to adjacent tet(s). + if (!isshtet(neighsh)) { + // Check if the subface is non-Delaunay wrt. the new pt. + sign = incircle3d(sorg(neighsh), sdest(neighsh), + sapex(neighsh), insertpt); + } else { + // It is connected to an adjacent tet. A boundary edge. + sign = 1; + } + } + if (sign < 0) { + // Add the adjacent face in list (in B-W cavity). + smarktest(neighsh); + caveshlist->newindex((void **) &parysh); + *parysh = neighsh; + } + } else { + sign = 1; // A boundary edge. + } + } else { + sign = -1; // Not a boundary edge. + } + } else { + // No adjacent face. It is a hull edge. + if (loc == OUTSIDE) { + // It is a boundary edge if it does not contain p. + if ((sorg(cavesh) == insertpt) || (sdest(cavesh) == insertpt)) { + sign = -1; // Not a boundary edge. + } else { + sign = 1; // A boundary edge. + } + } else { + sign = 1; // A boundary edge. + } + } + } else { + // Do not across a segment. It is a boundary edge. + sign = 1; + } + if (sign >= 0) { + // Add a boundary edge. + caveshbdlist->newindex((void **) &parysh); + *parysh = cavesh; + } + senextself(cavesh); + } // j + } // i -/////////////////////////////////////////////////////////////////////////////// -// // -// btree_search() Search a near point for an inserting point. // -// // -/////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::btree_search(point insertpt, triface* searchtet) -{ - point *ptary; - point nearpt, candpt; - REAL dist2, mindist2; - int ptsamples, ptidx; - long arylen; - int i; + // Creating new subfaces. + for (i = 0; i < 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); + setshellmark(newsh, shellmark(*parysh)); + if (checkconstraints) { + //area = areabound(*parysh); + setareabound(newsh, areabound(*parysh)); + } + if (useinsertradius) { + setfacetindex(newsh, getfacetindex(*parysh)); + } + // Update the point-to-subface map. + if (pointtype(pa) == FREEFACETVERTEX) { + setpoint2sh(pa, sencode(newsh)); + } + if (pointtype(pb) == FREEFACETVERTEX) { + setpoint2sh(pb, sencode(newsh)); + } + // Connect newsh to outer subfaces. + spivot(*parysh, casout); + if (casout.sh != NULL) { + casin = casout; + if (checkseg.sh != NULL) { + // Make sure that newsh has the right ori at this segment. + checkseg.shver = 0; + if (sorg(newsh) != sorg(checkseg)) { + sesymself(newsh); + sesymself(*parysh); // This side should also be inverse. + } + spivot(casin, neighsh); + while (neighsh.sh != parysh->sh) { + casin = neighsh; + spivot(casin, neighsh); + } + } + sbond1(newsh, casout); + sbond1(casin, newsh); + } + if (checkseg.sh != NULL) { + ssbond(newsh, checkseg); + } + // Connect oldsh <== newsh (for connecting adjacent new subfaces). + // *parysh and newsh point to the same edge and the same ori. + sbond1(*parysh, newsh); + } - // Get the tree node (save in this point). - ptary = (point *) point2ppt(insertpt); - // Get the current array length. - arylen = (long) ptary[0]; + if (newsh.sh != NULL) { + // Set a handle for searching. + recentsh = newsh; + } - if (arylen == 0) { - searchtet->tet = NULL; - return; + // Update the point-to-subface map. + if (pointtype(insertpt) == FREEFACETVERTEX) { + setpoint2sh(insertpt, sencode(newsh)); } - 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++; + // Connect adjacent new subfaces together. + for (i = 0; i < 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]. + senextself(newsh); // At edge [b, p]. + spivot(newsh, neighsh); + if (neighsh.sh == NULL) { + // Find the adjacent new subface at edge [b, p]. + pb = sdest(*parysh); + neighsh = *parysh; + while (1) { + senextself(neighsh); + spivotself(neighsh); + if (neighsh.sh == NULL) break; + if (!smarktested(neighsh)) break; + if (sdest(neighsh) != pb) sesymself(neighsh); + } + if (neighsh.sh != NULL) { + // Now 'neighsh' is a new subface at edge [b, #]. + if (sorg(neighsh) != pb) sesymself(neighsh); + senext2self(neighsh); // Go to the open edge [p, b]. + sbond(newsh, neighsh); + } else { + // There is no adjacent new face at this side. + assert(loc == OUTSIDE); // SELF_CHECK + } + } + spivot(*parysh, newsh); // The new subface [a, b, p]. + senext2self(newsh); // At edge [p, a]. + spivot(newsh, neighsh); + if (neighsh.sh == NULL) { + // Find the adjacent new subface at edge [p, a]. + pa = sorg(*parysh); + neighsh = *parysh; + while (1) { + senext2self(neighsh); + spivotself(neighsh); + if (neighsh.sh == NULL) break; + if (!smarktested(neighsh)) break; + if (sorg(neighsh) != pa) sesymself(neighsh); + } + if (neighsh.sh != NULL) { + // Now 'neighsh' is a new subface at edge [#, a]. + if (sdest(neighsh) != pa) sesymself(neighsh); + senextself(neighsh); // Go to the open edge [a, p]. + sbond(newsh, neighsh); + } else { + // There is no adjacent new face at this side. + assert(loc == OUTSIDE); // SELF_CHECK + } } } - // Select "good" candidate using k random samples, taking the closest one. - mindist2 = 1.79769E+308; // The largest double value (8 byte). - nearpt = NULL; - - for (i = 0; i < ptsamples; i++) { - ptidx = randomnation((unsigned long) arylen); - candpt = ptary[ptidx + 1]; - dist2 = (candpt[0] - insertpt[0]) * (candpt[0] - insertpt[0]) - + (candpt[1] - insertpt[1]) * (candpt[1] - insertpt[1]) - + (candpt[2] - insertpt[2]) * (candpt[2] - insertpt[2]); - if (dist2 < mindist2) { - mindist2 = dist2; - nearpt = candpt; + if ((loc == ONEDGE) || ((splitseg != NULL) && (splitseg->sh != NULL)) + || (cavesegshlist->objects > 0l)) { + // An edge is being split. We distinguish two cases: + // (1) the edge is not on the boundary of the cavity; + // (2) the edge is on the boundary of the cavity. + // In case (2), the edge is either a segment or a hull edge. There are + // degenerated new faces in the cavity. They must be removed. + face aseg, bseg, aoutseg, boutseg; + + for (i = 0; i < cavesegshlist->objects; i++) { + // Get the saved old subface. + parysh = (face *) fastlookup(cavesegshlist, i); + // Get a possible new degenerated subface. + spivot(*parysh, cavesh); + if (sapex(cavesh) == insertpt) { + // Found a degenerated new subface, i.e., case (2). + if (cavesegshlist->objects > 1) { + // There are more than one subface share at this edge. + j = (i + 1) % (int) cavesegshlist->objects; + parysh = (face *) fastlookup(cavesegshlist, j); + spivot(*parysh, neighsh); + // Adjust cavesh and neighsh both at edge a->b, and has p as apex. + if (sorg(neighsh) != sorg(cavesh)) { + sesymself(neighsh); + assert(sorg(neighsh) == sorg(cavesh)); // SELF_CHECK + } + assert(sapex(neighsh) == insertpt); // SELF_CHECK + // Connect adjacent faces at two other edges of cavesh and neighsh. + // As a result, the two degenerated new faces are squeezed from the + // new triangulation of the cavity. Note that the squeezed faces + // still hold the adjacent informations which will be used in + // re-connecting subsegments (if they exist). + for (j = 0; j < 2; j++) { + senextself(cavesh); + senextself(neighsh); + spivot(cavesh, newsh); + spivot(neighsh, casout); + sbond1(newsh, casout); // newsh <- casout. + } + } else { + // There is only one subface containing this edge [a,b]. Squeeze the + // degenerated new face [a,b,c] by disconnecting it from its two + // adjacent subfaces at edges [b,c] and [c,a]. Note that the face + // [a,b,c] still hold the connection to them. + for (j = 0; j < 2; j++) { + senextself(cavesh); + spivot(cavesh, newsh); + sdissolve(newsh); + } + } + //recentsh = newsh; + // Update the point-to-subface map. + if (pointtype(insertpt) == FREEFACETVERTEX) { + setpoint2sh(insertpt, sencode(newsh)); + } + } } - } - if (b->verbose > 1) { - printf(" Get point %d (cell size %ld).\n", pointmark(nearpt), arylen); - } + if ((splitseg != NULL) && (splitseg->sh != NULL)) { + if (loc != INSTAR) { // if (bowywat < 3) { + smarktest(*splitseg); // Mark it as being processed. + } + + aseg = *splitseg; + pa = sorg(*splitseg); + pb = sdest(*splitseg); - decode(point2tet(nearpt), *searchtet); -} + // Insert the new point p. + makeshellface(subsegs, &aseg); + makeshellface(subsegs, &bseg); + + setshvertices(aseg, pa, insertpt, NULL); + setshvertices(bseg, insertpt, pb, NULL); + setshellmark(aseg, shellmark(*splitseg)); + setshellmark(bseg, shellmark(*splitseg)); + if (checkconstraints) { + setareabound(aseg, areabound(*splitseg)); + setareabound(bseg, areabound(*splitseg)); + } + if (useinsertradius) { + setfacetindex(aseg, getfacetindex(*splitseg)); + setfacetindex(bseg, getfacetindex(*splitseg)); + } + + // Connect [#, a]<->[a, p]. + senext2(*splitseg, boutseg); // Temporarily use boutseg. + spivotself(boutseg); + if (boutseg.sh != NULL) { + senext2(aseg, aoutseg); + sbond(boutseg, aoutseg); + } + // Connect [p, b]<->[b, #]. + senext(*splitseg, aoutseg); + spivotself(aoutseg); + if (aoutseg.sh != NULL) { + 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 adjacent new subfaces. + // Although the degenerated new faces have been squeezed. They still + // hold the connections to the actual new faces. + for (i = 0; i < cavesegshlist->objects; i++) { + parysh = (face *) fastlookup(cavesegshlist, i); + spivot(*parysh, neighsh); + // neighsh is a degenerated new face. + if (sorg(neighsh) != pa) { + sesymself(neighsh); + } + senext2(neighsh, newsh); + spivotself(newsh); // The edge [p, a] in newsh + ssbond(newsh, aseg); + senext(neighsh, newsh); + spivotself(newsh); // The edge [b, p] in newsh + ssbond(newsh, bseg); + } -/////////////////////////////////////////////////////////////////////////////// -// // -// ordervertices() Order the vertices for incremental inserting. // -// // -// We assume the vertices have been sorted by a binary tree. // -// // -/////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::ordervertices(point* vertexarray, int arraysize) -{ - point **ipptary, **jpptary, *swappptary; - point *ptary; - long arylen; - int index, i, j; - - // First pick one vertex from each tree node. - for (i = 0; i < (int) btreenode_list->objects; i++) { - ipptary = (point **) fastlookup(btreenode_list, i); - ptary = *ipptary; - vertexarray[i] = ptary[1]; // Skip the first entry. - } - - index = i; - // Then put all other points in the array node by node. - for (i = (int) btreenode_list->objects - 1; i >= 0; i--) { - // Randomly pick a tree node. - j = randomnation(i + 1); - // Save the i-th node. - ipptary = (point **) fastlookup(btreenode_list, i); - // Get the j-th node. - jpptary = (point **) fastlookup(btreenode_list, j); - // Order the points in the node. - ptary = *jpptary; - arylen = (long) ptary[0]; - for (j = 2; j <= arylen; j++) { // Skip the first point. - vertexarray[index] = ptary[j]; - index++; + // Let the point remember the segment it lies on. + if (pointtype(insertpt) == FREESEGVERTEX) { + setpoint2sh(insertpt, sencode(aseg)); + } + // Update the point-to-seg map. + if (pointtype(pa) == FREESEGVERTEX) { + setpoint2sh(pa, sencode(aseg)); + } + if (pointtype(pb) == FREESEGVERTEX) { + setpoint2sh(pb, sencode(bseg)); + } + } // if ((splitseg != NULL) && (splitseg->sh != NULL)) + + // Delete all degenerated new faces. + for (i = 0; i < cavesegshlist->objects; i++) { + parysh = (face *) fastlookup(cavesegshlist, i); + spivotself(*parysh); + if (sapex(*parysh) == insertpt) { + shellfacedealloc(subfaces, parysh->sh); + } } - // 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] - } + cavesegshlist->restart(); + + if ((splitseg != NULL) && (splitseg->sh != NULL)) { + // Return the two new subsegments (for further process). + // Re-use 'cavesegshlist'. + cavesegshlist->newindex((void **) &parysh); + *parysh = aseg; + cavesegshlist->newindex((void **) &parysh); + *parysh = bseg; + } + } // if (loc == ONEDGE) + - // Make sure we've done correctly. - assert(index == arraysize); + return (int) loc; } /////////////////////////////////////////////////////////////////////////////// // // -// insertvertexbw() Insert a vertex using the Boywer-Watson algorithm. // +// sremovevertex() Remove a vertex from the surface mesh. // // // -// 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. // +// 'delpt' (p) is the vertex to be removed. If 'parentseg' is not NULL, p is // +// a segment vertex, and the origin of 'parentseg' is p. Otherwise, p is a // +// facet vertex, and the origin of 'parentsh' is p. // // // -// 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). // +// Within each facet, we first use a sequence of 2-to-2 flips to flip any // +// edge at p, finally use a 3-to-1 flip to remove p. // // // -// If 'visflag' is TRUE, force to check the visibility of the boundary faces // -// of cavity. This is needed when T is not Delaunay. // +// All new created subfaces are returned in the global array 'caveshbdlist'. // +// The new segment (when p is on segment) is returned in 'parentseg'. // // // -// 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. // +// If 'lawson' > 0, the Lawson flip algorithm is used to recover Delaunay- // +// ness after p is removed. // // // /////////////////////////////////////////////////////////////////////////////// -enum tetgenmesh::locateresult tetgenmesh::insertvertexbw(point insertpt, - triface *searchtet, bool bwflag, bool visflag, bool noencsegflag, - bool noencsubflag) +int tetgenmesh::sremovevertex(point delpt, face* parentsh, face* parentseg, + int lawson) { - 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; + face flipfaces[4], spinsh, *parysh; + point pa, pb, pc, pd; + REAL ori1, ori2; + int it, i, j; + + if (parentseg != NULL) { + // 'delpt' (p) should be a Steiner point inserted in a segment [a,b], + // where 'parentseg' should be [p,b]. Find the segment [a,p]. + face startsh, neighsh, nextsh; + face abseg, prevseg, checkseg; + face adjseg1, adjseg2; + face fakesh; + senext2(*parentseg, prevseg); + spivotself(prevseg); + prevseg.shver = 0; + assert(sdest(prevseg) == delpt); + // Restore the original segment [a,b]. + pa = sorg(prevseg); + pb = sdest(*parentseg); + if (b->verbose > 2) { + printf(" Remove vertex %d from segment [%d, %d].\n", + pointmark(delpt), pointmark(pa), pointmark(pb)); + } + makeshellface(subsegs, &abseg); + setshvertices(abseg, pa, pb, NULL); + setshellmark(abseg, shellmark(*parentseg)); + if (checkconstraints) { + setareabound(abseg, areabound(*parentseg)); + } + if (useinsertradius) { + setfacetindex(abseg, getfacetindex(*parentseg)); + } + // Connect [#, a]<->[a, b]. + senext2(prevseg, adjseg1); + spivotself(adjseg1); + if (adjseg1.sh != NULL) { + adjseg1.shver = 0; + assert(sdest(adjseg1) == pa); + senextself(adjseg1); + senext2(abseg, adjseg2); + sbond(adjseg1, adjseg2); + } + // Connect [a, b]<->[b, #]. + senext(*parentseg, adjseg1); + spivotself(adjseg1); + if (adjseg1.sh != NULL) { + adjseg1.shver = 0; + assert(sorg(adjseg1) == pb); + senext2self(adjseg1); + senext(abseg, adjseg2); + sbond(adjseg1, adjseg2); + } + // Update the point-to-segment map. + setpoint2sh(pa, sencode(abseg)); + setpoint2sh(pb, sencode(abseg)); + + // Get the faces in face ring at segment [p, b]. + // Re-use array 'caveshlist'. + spivot(*parentseg, *parentsh); + if (parentsh->sh != NULL) { + spinsh = *parentsh; + while (1) { + // Save this face in list. + caveshlist->newindex((void **) &parysh); + *parysh = spinsh; + // Go to the next face in the ring. + spivotself(spinsh); + if (spinsh.sh == parentsh->sh) break; + } + } - // 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); + // Create the face ring of the new segment [a,b]. Each face in the ring + // is [a,b,p] (degenerated!). It will be removed (automatically). + for (i = 0; i < caveshlist->objects; i++) { + parysh = (face *) fastlookup(caveshlist, i); + startsh = *parysh; + if (sorg(startsh) != delpt) { + sesymself(startsh); + assert(sorg(startsh) == delpt); + } + // startsh is [p, b, #1], find the subface [a, p, #2]. + neighsh = startsh; + while (1) { + senext2self(neighsh); + sspivot(neighsh, checkseg); + if (checkseg.sh != NULL) { + // It must be the segment [a, p]. + assert(checkseg.sh == prevseg.sh); + break; + } + spivotself(neighsh); + assert(neighsh.sh != NULL); + if (sorg(neighsh) != delpt) sesymself(neighsh); + } + // Now neighsh is [a, p, #2]. + if (neighsh.sh != startsh.sh) { + // Detach the two subsegments [a,p] and [p,b] from subfaces. + ssdissolve(startsh); + ssdissolve(neighsh); + // Create a degenerated subface [a,b,p]. It is used to: (1) hold the + // new segment [a,b]; (2) connect to the two adjacent subfaces + // [p,b,#] and [a,p,#]. + makeshellface(subfaces, &fakesh); + setshvertices(fakesh, pa, pb, delpt); + setshellmark(fakesh, shellmark(startsh)); + // Connect fakesh to the segment [a,b]. + ssbond(fakesh, abseg); + // Connect fakesh to adjacent subfaces: [p,b,#1] and [a,p,#2]. + senext(fakesh, nextsh); + sbond(nextsh, startsh); + senext2(fakesh, nextsh); + sbond(nextsh, neighsh); + smarktest(fakesh); // Mark it as faked. + } else { + // Special case. There exists already a degenerated face [a,b,p]! + // There is no need to create a faked subface here. + senext2self(neighsh); // [a,b,p] + assert(sapex(neighsh) == delpt); + // Since we will re-connect the face ring using the faked subfaces. + // We put the adjacent face of [a,b,p] to the list. + spivot(neighsh, startsh); // The original adjacent subface. + if (sorg(startsh) != pa) sesymself(startsh); + sdissolve(startsh); + // Connect fakesh to the segment [a,b]. + ssbond(startsh, abseg); + fakesh = startsh; // Do not mark it! + // Delete the degenerated subface. + shellfacedealloc(subfaces, neighsh.sh); + } + // Save the fakesh in list (for re-creating the face ring). + cavesegshlist->newindex((void **) &parysh); + *parysh = fakesh; + } // i + caveshlist->restart(); + + // Re-create the face ring. + if (cavesegshlist->objects > 1) { + for (i = 0; i < cavesegshlist->objects; i++) { + parysh = (face *) fastlookup(cavesegshlist, i); + fakesh = *parysh; + // Get the next face in the ring. + j = (i + 1) % cavesegshlist->objects; + parysh = (face *) fastlookup(cavesegshlist, j); + nextsh = *parysh; + sbond1(fakesh, nextsh); + } + } + + // Delete the two subsegments containing p. + shellfacedealloc(subsegs, parentseg->sh); + shellfacedealloc(subsegs, prevseg.sh); + // Return the new segment. + *parentseg = abseg; } else { - // Start from 'searchtet'. - loc = locate2(insertpt, searchtet, NULL); + // p is inside the surface. + if (b->verbose > 2) { + printf(" Remove vertex %d from surface.\n", pointmark(delpt)); + } + assert(sorg(*parentsh) == delpt); + // Let 'delpt' be its apex. + senextself(*parentsh); + // For unifying the code, we add parentsh to list. + cavesegshlist->newindex((void **) &parysh); + *parysh = *parentsh; + } + + // Remove the point (p). + + for (it = 0; it < cavesegshlist->objects; it++) { + parentsh = (face *) fastlookup(cavesegshlist, it); // [a,b,p] + senextself(*parentsh); // [b,p,a]. + spivotself(*parentsh); + if (sorg(*parentsh) != delpt) sesymself(*parentsh); + // now parentsh is [p,b,#]. + if (sorg(*parentsh) != delpt) { + // The vertex has already been removed in above special case. + assert(!smarktested(*parentsh)); + continue; + } + + while (1) { + // Initialize the flip edge list. Re-use 'caveshlist'. + spinsh = *parentsh; // [p, b, #] + while (1) { + caveshlist->newindex((void **) &parysh); + *parysh = spinsh; + senext2self(spinsh); + spivotself(spinsh); + assert(spinsh.sh != NULL); + if (spinsh.sh == parentsh->sh) break; + if (sorg(spinsh) != delpt) sesymself(spinsh); + assert(sorg(spinsh) == delpt); + } // while (1) + + if (caveshlist->objects == 3) { + // Delete the point by a 3-to-1 flip. + for (i = 0; i < 3; i++) { + parysh = (face *) fastlookup(caveshlist, i); + flipfaces[i] = *parysh; + } + flip31(flipfaces, lawson); + for (i = 0; i < 3; i++) { + shellfacedealloc(subfaces, flipfaces[i].sh); + } + caveshlist->restart(); + // Save the new subface. + caveshbdlist->newindex((void **) &parysh); + *parysh = flipfaces[3]; + // The vertex is removed. + break; + } + + // Search an edge to flip. + for (i = 0; i < caveshlist->objects; i++) { + parysh = (face *) fastlookup(caveshlist, i); + flipfaces[0] = *parysh; + spivot(flipfaces[0], flipfaces[1]); + if (sorg(flipfaces[0]) != sdest(flipfaces[1])) + sesymself(flipfaces[1]); + // Skip this edge if it belongs to a faked subface. + if (!smarktested(flipfaces[0]) && !smarktested(flipfaces[1])) { + pa = sorg(flipfaces[0]); + pb = sdest(flipfaces[0]); + pc = sapex(flipfaces[0]); + pd = sapex(flipfaces[1]); + calculateabovepoint4(pa, pb, pc, pd); + // Check if a 2-to-2 flip is possible. + ori1 = orient3d(pc, pd, dummypoint, pa); + ori2 = orient3d(pc, pd, dummypoint, pb); + if (ori1 * ori2 < 0) { + // A 2-to-2 flip is found. + flip22(flipfaces, lawson, 0); + // The i-th edge is flipped. The i-th and (i-1)-th subfaces are + // changed. The 'flipfaces[1]' contains p as its apex. + senext2(flipfaces[1], *parentsh); + // Save the new subface. + caveshbdlist->newindex((void **) &parysh); + *parysh = flipfaces[0]; + break; + } + } // + } // i + + if (i == caveshlist->objects) { + // This can happen only if there are 4 edges at p, and they are + // orthogonal to each other, see Fig. 2010-11-01. + assert(caveshlist->objects == 4); + // Do a flip22 and a flip31 to remove p. + parysh = (face *) fastlookup(caveshlist, 0); + flipfaces[0] = *parysh; + spivot(flipfaces[0], flipfaces[1]); + if (sorg(flipfaces[0]) != sdest(flipfaces[1])) { + sesymself(flipfaces[1]); + } + flip22(flipfaces, lawson, 0); + senext2(flipfaces[1], *parentsh); + // Save the new subface. + caveshbdlist->newindex((void **) &parysh); + *parysh = flipfaces[0]; + } + + // The edge list at p are changed. + caveshlist->restart(); + } // while (1) + + } // it + + cavesegshlist->restart(); + + if (b->verbose > 2) { + printf(" Created %ld new subfaces.\n", caveshbdlist->objects); } - if (b->verbose > 1) { - printf(" Walk distance (# tets): %ld\n", ptloc_count - tetcount); + + if (lawson) { + lawsonflip(); } - if (ptloc_max_count < (ptloc_count - tetcount)) { - ptloc_max_count = (ptloc_count - tetcount); + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// slocate() Locate a point in a surface triangulation. // +// // +// Staring the search from 'searchsh'(it should not be NULL). Perform a line // +// walk search for a subface containing the point (p). // +// // +// If 'aflag' is set, the 'dummypoint' is pre-calculated so that it lies // +// above the 'searchsh' in its current orientation. The test if c is CCW to // +// the line a->b can be done by the test if c is below the oriented plane // +// a->b->dummypoint. // +// // +// If 'cflag' is not TRUE, the triangulation may not be convex. Stop search // +// when a segment is met and return OUTSIDE. // +// // +// If 'rflag' (rounding) is set, after the location of the point is found, // +// either ONEDGE or ONFACE, round the result using an epsilon. // +// // +// The returned value indicates the following cases: // +// - ONVERTEX, p is the origin of 'searchsh'. // +// - ONEDGE, p lies on the edge of 'searchsh'. // +// - ONFACE, p lies in the interior of 'searchsh'. // +// - OUTSIDE, p lies outside of the triangulation, p is on the left-hand // +// side of the edge 'searchsh'(s), i.e., org(s), dest(s), p are CW. // +// // +/////////////////////////////////////////////////////////////////////////////// + +enum tetgenmesh::locateresult tetgenmesh::slocate(point searchpt, + face* searchsh, int aflag, int cflag, int rflag) +{ + face neighsh; + point pa, pb, pc; + enum locateresult loc; + enum {MOVE_BC, MOVE_CA} nextmove; + REAL ori, ori_bc, ori_ca; + int i; + + pa = sorg(*searchsh); + pb = sdest(*searchsh); + pc = sapex(*searchsh); + + if (!aflag) { + // No above point is given. Calculate an above point for this facet. + calculateabovepoint4(pa, pb, pc, searchpt); } - 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))); + // 'dummypoint' is given. Make sure it is above [a,b,c] + ori = orient3d(pa, pb, pc, dummypoint); + assert(ori != 0); // SELF_CHECK + if (ori > 0) { + sesymself(*searchsh); // Reverse the face orientation. } - 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; + // Find an edge of the face s.t. p lies on its right-hand side (CCW). + for (i = 0; i < 3; i++) { + pa = sorg(*searchsh); + pb = sdest(*searchsh); + ori = orient3d(pa, pb, dummypoint, searchpt); + if (ori > 0) break; + senextself(*searchsh); } + assert(i < 3); // SELF_CHECK - tetcount = 0l; // The number of deallocated tets. + pc = sapex(*searchsh); - // 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; + if (pc == searchpt) { + senext2self(*searchsh); + return ONVERTEX; + } + + while (1) { + + ori_bc = orient3d(pb, pc, dummypoint, searchpt); + ori_ca = orient3d(pc, pa, dummypoint, searchpt); + + if (ori_bc < 0) { + if (ori_ca < 0) { // (--) + // Any of the edges is a viable move. + if (randomnation(2)) { + nextmove = MOVE_CA; + } else { + nextmove = MOVE_BC; + } + } else { // (-#) + // Edge [b, c] is viable. + nextmove = MOVE_BC; } - infect(spintet); - caveoldtetlist->newindex((void **) &parytet); - *parytet = spintet; - tetcount++; } else { - // Split a hull face into three hull faces. - hullsize += 2; + if (ori_ca < 0) { // (#-) + // Edge [c, a] is viable. + nextmove = MOVE_CA; + } else { + if (ori_bc > 0) { + if (ori_ca > 0) { // (++) + loc = ONFACE; // Inside [a, b, c]. + break; + } else { // (+0) + senext2self(*searchsh); // On edge [c, a]. + loc = ONEDGE; + break; + } + } else { // ori_bc == 0 + if (ori_ca > 0) { // (0+) + senextself(*searchsh); // On edge [b, c]. + loc = ONEDGE; + break; + } else { // (00) + // p is coincident with vertex c. + senext2self(*searchsh); + return ONVERTEX; + } + } + } } - 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; + + // Move to the next face. + if (nextmove == MOVE_BC) { + senextself(*searchsh); + } else { + senext2self(*searchsh); + } + if (!cflag) { + // NON-convex case. Check if we will cross a boundary. + if (isshsubseg(*searchsh)) { + return ENCSEGMENT; } - 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"); + spivot(*searchsh, neighsh); + if (neighsh.sh == NULL) { + return OUTSIDE; // A hull edge. } - // '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); + // Adjust the edge orientation. + if (sorg(neighsh) != sdest(*searchsh)) { + sesymself(neighsh); } - if (b->varvolume) { - volume = volumebound(searchtet->tet); - setvolumebound(newtet.tet, volume); + assert(sorg(neighsh) == sdest(*searchsh)); // SELF_CHECK + + // Update the newly discovered face and its endpoints. + *searchsh = neighsh; + pa = sorg(*searchsh); + pb = sdest(*searchsh); + pc = sapex(*searchsh); + + if (pc == searchpt) { + senext2self(*searchsh); + return ONVERTEX; } - // 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++; + } // while (1) - // 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); + // assert(loc == ONFACE || loc == ONEDGE); + + + if (rflag) { + // Round the locate result before return. + REAL n[3], area_abc, area_abp, area_bcp, area_cap; + + pa = sorg(*searchsh); + pb = sdest(*searchsh); + pc = sapex(*searchsh); + + facenormal(pa, pb, pc, n, 1, NULL); + area_abc = sqrt(dot(n, n)); + + facenormal(pb, pc, searchpt, n, 1, NULL); + area_bcp = sqrt(dot(n, n)); + if ((area_bcp / area_abc) < b->epsilon) { + area_bcp = 0; // Rounding. } - // 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; + facenormal(pc, pa, searchpt, n, 1, NULL); + area_cap = sqrt(dot(n, n)); + if ((area_cap / area_abc) < b->epsilon) { + area_cap = 0; // Rounding + } + + if ((loc == ONFACE) || (loc == OUTSIDE)) { + facenormal(pa, pb, searchpt, n, 1, NULL); + area_abp = sqrt(dot(n, n)); + if ((area_abp / area_abc) < b->epsilon) { + area_abp = 0; // Rounding } + } else { // loc == ONEDGE + area_abp = 0; } - 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; + + if (area_abp == 0) { + if (area_bcp == 0) { + assert(area_cap != 0); + senextself(*searchsh); + loc = ONVERTEX; // p is close to b. + } else { + if (area_cap == 0) { + loc = ONVERTEX; // p is close to a. + } else { + loc = ONEDGE; // p is on edge [a,b]. } - 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 if (area_bcp == 0) { + if (area_cap == 0) { + senext2self(*searchsh); + loc = ONVERTEX; // p is close to c. } else { - cavebdrylist->newindex((void **) &parytet1); - *parytet1 = *parytet; + senextself(*searchsh); + loc = ONEDGE; // p is on edge [b,c]. } + } else if (area_cap == 0) { + senext2self(*searchsh); + loc = ONEDGE; // p is on edge [c,a]. + } else { + loc = ONFACE; // p is on face [a,b,c]. } - } // i + } // if (rflag) - if (b->verbose > 1) { - printf(" Cavity formed: %ld tets, %ld faces.\n", tetcount, - cavebdrylist->objects); - } + return loc; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// sscoutsegment() Look for a segment in surface triangulation. // +// // +// The segment is given by the origin of 'searchsh' and 'endpt'. Assume the // +// orientation of 'searchsh' is CCW w.r.t. the above point. // +// // +// If an edge in T is found matching this segment, the segment is "locked" // +// in T at the edge. Otherwise, flip the first edge in T that the segment // +// crosses. Continue the search from the flipped face. // +// // +/////////////////////////////////////////////////////////////////////////////// + +enum tetgenmesh::interresult tetgenmesh::sscoutsegment(face *searchsh, + point endpt) +{ + face flipshs[2], neighsh; + face newseg; + point startpt, pa, pb, pc, pd; + enum interresult dir; + enum {MOVE_AB, MOVE_CA} nextmove; + REAL ori_ab, ori_ca, len; - totaldeadtets += tetcount; - totalbowatcavsize += cavebdrylist->objects; - if (maxbowatcavsize < (long) cavebdrylist->objects) { - maxbowatcavsize = cavebdrylist->objects; + // The origin of 'searchsh' is fixed. + startpt = sorg(*searchsh); // pa = startpt; + nextmove = MOVE_AB; // Avoid compiler warning. + + if (b->verbose > 2) { + printf(" Scout segment (%d, %d).\n", pointmark(startpt), + pointmark(endpt)); } + len = distance(startpt, endpt); - 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; + // Search an edge in 'searchsh' on the path of this segment. + while (1) { + + pb = sdest(*searchsh); + if (pb == endpt) { + dir = SHAREEDGE; // Found! + break; + } + + pc = sapex(*searchsh); + if (pc == endpt) { + senext2self(*searchsh); + sesymself(*searchsh); + dir = SHAREEDGE; // Found! + break; + } + + // Round the results. + if ((sqrt(triarea(startpt, pb, endpt)) / len) < b->epsilon) { + ori_ab = 0.0; + } else { + ori_ab = orient3d(startpt, pb, dummypoint, endpt); + } + if ((sqrt(triarea(pc, startpt, endpt)) / len) < b->epsilon) { + ori_ca = 0.0; + } else { + ori_ca = orient3d(pc, startpt, dummypoint, endpt); + } + + if (ori_ab < 0) { + if (ori_ca < 0) { // (--) + // Both sides are viable moves. + if (randomnation(2)) { + nextmove = MOVE_CA; + } else { + nextmove = MOVE_AB; + } + } else { // (-#) + nextmove = MOVE_AB; + } + } else { + if (ori_ca < 0) { // (#-) + nextmove = MOVE_CA; + } else { + if (ori_ab > 0) { + if (ori_ca > 0) { // (++) + // The segment intersects with edge [b, c]. + dir = ACROSSEDGE; + break; + } else { // (+0) + // The segment collinear with edge [c, a]. + senext2self(*searchsh); + sesymself(*searchsh); + dir = ACROSSVERT; + 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; + } else { + if (ori_ca > 0) { // (0+) + // The segment collinear with edge [a, b]. + dir = ACROSSVERT; + break; + } else { // (00) + // startpt == endpt. Not possible. + assert(0); // SELF_CHECK } } } } - } - 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); + // Move 'searchsh' to the next face, keep the origin unchanged. + if (nextmove == MOVE_AB) { + spivot(*searchsh, neighsh); + if (neighsh.sh != NULL) { + if (sorg(neighsh) != pb) sesymself(neighsh); + senext(neighsh, *searchsh); + } else { + // This side (startpt->pb) is outside. It is caused by rounding error. + // Try the next side, i.e., (pc->startpt). + senext2(*searchsh, neighsh); + spivotself(neighsh); + assert(neighsh.sh != NULL); + if (sdest(neighsh) != pc) sesymself(neighsh); + *searchsh = neighsh; + } + } else { + senext2(*searchsh, neighsh); + spivotself(neighsh); + if (neighsh.sh != NULL) { + if (sdest(neighsh) != pc) sesymself(neighsh); + *searchsh = neighsh; + } else { + // The same reason as above. + // Try the next side, i.e., (startpt->pb). + spivot(*searchsh, neighsh); + assert(neighsh.sh != NULL); + if (sorg(neighsh) != pb) sesymself(neighsh); + senext(neighsh, *searchsh); } } - cavetetlist->restart(); - cavebdrylist->restart(); - caveoldtetlist->restart(); - return ENCSEGMENT; - } + assert(sorg(*searchsh) == startpt); // SELF_CHECK - 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; - } - } - } - } + } // while + + if (dir == SHAREEDGE) { + // Insert the segment into the triangulation. + makeshellface(subsegs, &newseg); + setshvertices(newseg, startpt, endpt, NULL); + // Set the default segment marker. + setshellmark(newseg, 1); + ssbond(*searchsh, newseg); + spivot(*searchsh, neighsh); + if (neighsh.sh != NULL) { + ssbond(neighsh, newseg); } + return dir; } - 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 (dir == ACROSSVERT) { + // A point is found collinear with this segment. + return dir; + } + + if (dir == ACROSSEDGE) { + // Edge [b, c] intersects with the segment. + senext(*searchsh, flipshs[0]); + if (isshsubseg(flipshs[0])) { + printf("Error: Invalid PLC.\n"); + pb = sorg(flipshs[0]); + pc = sdest(flipshs[0]); + printf(" Two segments (%d, %d) and (%d, %d) intersect.\n", + pointmark(startpt), pointmark(endpt), pointmark(pb), pointmark(pc)); + terminatetetgen(this, 3); } - if (bwflag && (futureflip != NULL)) { - flippool->restart(); - futureflip = NULL; + // Flip edge [b, c], queue unflipped edges (for Delaunay checks). + spivot(flipshs[0], flipshs[1]); + assert(flipshs[1].sh != NULL); // SELF_CHECK + if (sorg(flipshs[1]) != sdest(flipshs[0])) sesymself(flipshs[1]); + flip22(flipshs, 1, 0); + // The flip may create an inverted triangle, check it. + pa = sapex(flipshs[1]); + pb = sapex(flipshs[0]); + pc = sorg(flipshs[0]); + pd = sdest(flipshs[0]); + // Check if pa and pb are on the different sides of [pc, pd]. + // Re-use ori_ab, ori_ca for the tests. + ori_ab = orient3d(pc, pd, dummypoint, pb); + ori_ca = orient3d(pd, pc, dummypoint, pa); + //assert(ori_ab * ori_ca != 0); // SELF_CHECK + if (ori_ab < 0) { + flipshpush(&(flipshs[0])); // push it to 'flipstack' + } else if (ori_ca < 0) { + flipshpush(&(flipshs[1])); // // push it to 'flipstack' } - cavetetlist->restart(); - cavebdrylist->restart(); - caveoldtetlist->restart(); - return ENCFACE; - */ + // Set 'searchsh' s.t. its origin is 'startpt'. + *searchsh = flipshs[0]; + assert(sorg(*searchsh) == startpt); } - 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); + return sscoutsegment(searchsh, endpt); +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// scarveholes() Remove triangles not in the facet. // +// // +// This routine re-uses the two global arrays: caveshlist and caveshbdlist. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::scarveholes(int holes, REAL* holelist) +{ + face *parysh, searchsh, neighsh; + enum locateresult loc; + int i, j; + + // Get all triangles. Infect unprotected convex hull triangles. + smarktest(recentsh); + caveshlist->newindex((void **) &parysh); + *parysh = recentsh; + for (i = 0; i < caveshlist->objects; i++) { + parysh = (face *) fastlookup(caveshlist, i); + searchsh = *parysh; + searchsh.shver = 0; + for (j = 0; j < 3; j++) { + spivot(searchsh, neighsh); + // Is this side on the convex hull? + if (neighsh.sh != NULL) { + if (!smarktested(neighsh)) { + smarktest(neighsh); + caveshlist->newindex((void **) &parysh); + *parysh = neighsh; } } 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); + // A hull side. Check if it is protected by a segment. + if (!isshsubseg(searchsh)) { + // Not protected. Save this face. + if (!sinfected(searchsh)) { + sinfect(searchsh); + caveshbdlist->newindex((void **) &parysh); + *parysh = searchsh; } - } 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; - } - } - 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); - } + senextself(searchsh); } } - // 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 (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. - } + // Infect the triangles in the holes. + for (i = 0; i < 3 * holes; i += 3) { + searchsh = recentsh; + loc = slocate(&(holelist[i]), &searchsh, 1, 1, 0); + if (loc != OUTSIDE) { + sinfect(searchsh); + caveshbdlist->newindex((void **) &parysh); + *parysh = searchsh; } - } // 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. + // Find and infect all exterior triangles. + for (i = 0; i < caveshbdlist->objects; i++) { + parysh = (face *) fastlookup(caveshbdlist, i); + searchsh = *parysh; + searchsh.shver = 0; 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); + spivot(searchsh, neighsh); + if (neighsh.sh != NULL) { + if (!isshsubseg(searchsh)) { + if (!sinfected(neighsh)) { + sinfect(neighsh); + caveshbdlist->newindex((void **) &parysh); + *parysh = neighsh; + } } else { - // This side is a hull face. - neightet.tet[neightet.loc] = (tetrahedron) dummytet; - dummytet[0] = encode(neightet); + sdissolve(neighsh); // Disconnect a protected face. } } - setpoint2tet(org(newtet), encode(newtet)); - enextself(newtet); - enextself(*parytet); + senextself(searchsh); } } - // 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); + // Delete exterior triangles, unmark interior triangles. + for (i = 0; i < caveshlist->objects; i++) { + parysh = (face *) fastlookup(caveshlist, i); + if (sinfected(*parysh)) { + shellfacedealloc(subfaces, parysh->sh); + } else { + sunmarktest(*parysh); + } } - cavetetlist->restart(); - cavebdrylist->restart(); - caveoldtetlist->restart(); - - return loc; + caveshlist->restart(); + caveshbdlist->restart(); } /////////////////////////////////////////////////////////////////////////////// // // -// unifypoint() Unify two distinct points if they're very close. // +// triangulate() Create a CDT for the facet. // // // -// 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. // +// All vertices of the triangulation have type FACETVERTEX. The actual type // +// of boundary vertices are set by the routine unifysements(). // // // /////////////////////////////////////////////////////////////////////////////// -bool tetgenmesh::unifypoint(point testpt, triface *starttet, enum locateresult - loc, REAL eps) +void tetgenmesh::triangulate(int shmark, arraypool* ptlist, arraypool* conlist, + int holes, REAL* holelist) { - triface symtet, spintet; - point checkpt, tapex; - REAL tol; - bool merged; - int hitbdry; - int i; + face searchsh, newsh, *parysh; + face newseg; + point pa, pb, pc, *ppt, *cons; + int iloc; + 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++; - } - } - } - } while ((apex(spintet) != tapex) && (hitbdry < 2)); + if (b->verbose > 2) { + printf(" f%d: %ld vertices, %ld segments", shmark, ptlist->objects, + conlist->objects); + if (holes > 0) { + printf(", %d holes", holes); } + printf(".\n"); } - 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); + + if (ptlist->objects < 2l) { + // Not a segment or a facet. + return; } - return merged; -} -/////////////////////////////////////////////////////////////////////////////// -// // -// 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. // -// // -// 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. // -// // -/////////////////////////////////////////////////////////////////////////////// + if (ptlist->objects == 2l) { + pa = * (point *) fastlookup(ptlist, 0); + pb = * (point *) fastlookup(ptlist, 1); + if (distance(pa, pb) > 0) { + // It is a single segment. + makeshellface(subsegs, &newseg); + setshvertices(newseg, pa, pb, NULL); + // Set the default segment marker '1'. + setshellmark(newseg, 1); + } + if (pointtype(pa) == VOLVERTEX) { + setpointtype(pa, FACETVERTEX); + } + if (pointtype(pb) == VOLVERTEX) { + setpointtype(pb, FACETVERTEX); + } + return; + } -bool tetgenmesh::incrflipdelaunay(triface* oldtet, point* insertarray, - long arraysize, bool jump, bool merge, REAL eps, queue* flipque) -{ - triface newtet, searchtet; - point swappt, lastpt; - enum locateresult loc; - REAL det; - REAL attrib, volume; - int i, j; - // 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 (i == arraysize) { - // printf("\nAll points seem to be identical.\n"); - return false; - } else { - // 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 false; + if (ptlist->objects == 3) { + pa = * (point *) fastlookup(ptlist, 0); + pb = * (point *) fastlookup(ptlist, 1); + pc = * (point *) fastlookup(ptlist, 2); } 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) { - return false; - } 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; + // Calculate an above point of this facet. + if (!calculateabovepoint(ptlist, &pa, &pb, &pc)) { + return; // The point set is degenerate. + } } - // 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)); - } + // Create an initial triangulation. + makeshellface(subfaces, &newsh); + setshvertices(newsh, pa, pb, pc); + setshellmark(newsh, shmark); + recentsh = newsh; - 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); - } - } - // Set vertex type be FREEVOLVERTEX if it has no type yet. - if (pointtype(insertarray[0]) == UNUSEDVERTEX) { - setpointtype(insertarray[0], FREEVOLVERTEX); + if (pointtype(pa) == VOLVERTEX) { + setpointtype(pa, FACETVERTEX); } - if (pointtype(insertarray[1]) == UNUSEDVERTEX) { - setpointtype(insertarray[1], FREEVOLVERTEX); + if (pointtype(pb) == VOLVERTEX) { + setpointtype(pb, FACETVERTEX); } - if (pointtype(insertarray[2]) == UNUSEDVERTEX) { - setpointtype(insertarray[2], FREEVOLVERTEX); + if (pointtype(pc) == VOLVERTEX) { + setpointtype(pc, FACETVERTEX); } - 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); + + // Are there area constraints? + if (b->quality && (in->facetconstraintlist != (REAL *) NULL)) { + int idx, fmarker; + REAL area; + idx = in->facetmarkerlist[shmark - 1]; // The actual facet marker. + for (i = 0; i < in->numberoffacetconstraints; i++) { + fmarker = (int) in->facetconstraintlist[i * 2]; + if (fmarker == idx) { + area = in->facetconstraintlist[i * 2 + 1]; + setareabound(newsh, area); + break; + } + } } - // At init, all faces of this tet are hull faces. - hullsize = 4; - if (b->verbose > 1) { - printf(" Incrementally inserting points.\n"); + if (ptlist->objects == 3) { + // The triangulation only has one element. + for (i = 0; i < 3; i++) { + makeshellface(subsegs, &newseg); + setshvertices(newseg, sorg(newsh), sdest(newsh), NULL); + // Set the default segment marker '1'. + setshellmark(newseg, 1); + ssbond(newsh, newseg); + senextself(newsh); + } + return; } - // Insert the rest of points, one by one. - for (; i < arraysize; i++) { - if (jump) { - searchtet.tet = NULL; + // Incrementally build the triangulation. + pinfect(pa); + pinfect(pb); + pinfect(pc); + for (i = 0; i < ptlist->objects; i++) { + ppt = (point *) fastlookup(ptlist, i); + if (!pinfected(*ppt)) { + searchsh = recentsh; // Start from 'recentsh'. + iloc = (int) OUTSIDE; + // Insert the vertex. Use Bowyer-Watson algo. Round the location. + iloc = sinsertvertex(*ppt, &searchsh, NULL, iloc, 1, 1); + if (pointtype(*ppt) == VOLVERTEX) { + setpointtype(*ppt, FACETVERTEX); + } + // Delete all removed subfaces. + for (j = 0; j < caveshlist->objects; j++) { + parysh = (face *) fastlookup(caveshlist, j); + shellfacedealloc(subfaces, parysh->sh); + } + // Clear the global lists. + caveshbdlist->restart(); + caveshlist->restart(); + cavesegshlist->restart(); } else { - searchtet = recenttet; + puninfect(*ppt); // This point has inserted. } - loc = insertvertexbw(insertarray[i],&searchtet,true,false,false,false); } - return true; + // Insert the segments. + for (i = 0; i < conlist->objects; i++) { + cons = (point *) fastlookup(conlist, i); + searchsh = recentsh; + iloc = (int) slocate(cons[0], &searchsh, 1, 1, 0); + if (iloc != (enum locateresult) ONVERTEX) { + // Not found due to roundoff errors. Do a brute-force search. + subfaces->traversalinit(); + searchsh.sh = shellfacetraverse(subfaces); + while (searchsh.sh != NULL) { + // Only search the subface in the same facet. + if (shellmark(searchsh) == shmark) { + if ((point) searchsh.sh[3] == cons[0]) { + searchsh.shver = 0; break; + } else if ((point) searchsh.sh[4] == cons[0]) { + searchsh.shver = 2; break; + } else if ((point) searchsh.sh[5] == cons[0]) { + searchsh.shver = 4; break; + } + } + searchsh.sh = shellfacetraverse(subfaces); + } + assert(searchsh.sh != NULL); + } + // Recover the segment. Some edges may be flipped. + sscoutsegment(&searchsh, cons[1]); + if (flipstack != NULL) { + // Recover locally Delaunay edges. + lawsonflip(); + } + } + + // Remove exterior and hole triangles. + scarveholes(holes, holelist); } /////////////////////////////////////////////////////////////////////////////// // // -// delaunizevertices() Form a Delaunay tetrahedralization. // +// unifysubfaces() Unify two identical subfaces. // // // -// 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. // +// Two subfaces, f1 [a, b, c] and f2 [a, b, d], share the same edge [a, b]. // +// If c = d, then f1 and f2 are identical. Otherwise, these two subfaces // +// intersect, and the mesher is stopped. // +// // +// If the two subfaces are identical, we try to replace f2 by f1, i.e, all // +// neighbors of f2 are re-connected to f1. // // // /////////////////////////////////////////////////////////////////////////////// -long tetgenmesh::delaunizevertices() +void tetgenmesh::unifysubfaces(face *f1, face *f2) { - point *insertarray; - long arraysize; - bool success; - int i, j; - - if (!b->quiet) { - printf("Constructing Delaunay tetrahedralization.\n"); - } - - if (b->btree) { - btreenode_list = new arraypool(sizeof(point*), 10); - max_btreenode_size = 0; - max_btree_depth = 0; - } - - if (cavetetlist == NULL) { - cavetetlist = new arraypool(sizeof(triface), 10); - cavebdrylist = new arraypool(sizeof(triface), 10); - caveoldtetlist = new arraypool(sizeof(triface), 10); + if (b->psc) { + // In this case, it is possible that two subfaces are identical. + // While they must belong to two different surfaces. + return; } - // Prepare the array of points for inserting. - arraysize = points->items; - insertarray = new point[arraysize]; + point pa, pb, pc, pd; - 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); + pa = sorg(*f1); + pb = sdest(*f1); + pc = sapex(*f1); + pd = sapex(*f2); + + if (pc != pd) { + printf("Found two facets intersect each other.\n"); + printf(" 1st: [%d, %d, %d] #%d\n", + pointmark(pa), pointmark(pb), pointmark(pc), shellmark(*f1)); + printf(" 2nd: [%d, %d, %d] #%d\n", + pointmark(pa), pointmark(pb), pointmark(pd), shellmark(*f2)); + terminatetetgen(this, 3); } 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(); - } - } - - if (b->verbose) { - printf(" Incrementally inserting vertices.\n"); - } - - // 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; - } - delete btreenode_list; - btreenode_list = NULL; + printf("Found two duplicated facets.\n"); + printf(" 1st: [%d, %d, %d] #%d\n", + pointmark(pa), pointmark(pb), pointmark(pc), shellmark(*f1)); + printf(" 2nd: [%d, %d, %d] #%d\n", + pointmark(pa), pointmark(pb), pointmark(pd), shellmark(*f2)); + terminatetetgen(this, 3); } - delete [] insertarray; - - if (!success) { - return 0l; - } else { - return hullsize; - } } -//// //// -//// //// -//// 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. // +// unifysegments() Remove redundant segments and create face links. // // // -// If 'cflag' is not TRUE, the triangulation may be not convex. Don't insert // -// p if it is found in outside. // -// // -// Comment: This routine assumes the 'abovepoint' of this facet has been set,// -// i.e., the routine getabovepoint() has been executed before it is called. // +// After this routine, although segments are unique, but some of them may be // +// removed later by mergefacet(). All vertices still have type FACETVERTEX. // // // /////////////////////////////////////////////////////////////////////////////// -enum tetgenmesh::locateresult tetgenmesh::sinsertvertex(point insertpt, - face *splitsh, face *splitseg, bool bwflag, bool cflag) +void tetgenmesh::unifysegments() { - 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; - } - - 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; - } - } - } + badface *facelink = NULL, *newlinkitem, *f1, *f2; + face *facperverlist, sface; + face subsegloop, testseg; + point torg, tdest; + REAL ori1, ori2, ori3; + REAL n1[3], n2[3]; + int *idx2faclist; + int idx, k, m; 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]); - } + printf(" Unifying segments.\n"); } - // 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]; - } - } 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; - } - } + // Create a mapping from vertices to subfaces. + makepoint2submap(subfaces, idx2faclist, facperverlist); - // 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. + if (b->psc) { + face sface1; + face seg, seg1; + int fmarker, fmarker1; + // First only connect subfaces which belong to the same surfaces. + subsegloop.shver = 0; + subsegs->traversalinit(); + subsegloop.sh = shellfacetraverse(subsegs); + while (subsegloop.sh != (shellface *) NULL) { + torg = sorg(subsegloop); + tdest = sdest(subsegloop); + + idx = pointmark(torg) - in->firstnumber; + for (k = idx2faclist[idx]; k < idx2faclist[idx + 1]; k++) { + sface = facperverlist[k]; + // The face may be deleted if it is a duplicated face. + if (sface.sh[3] == NULL) continue; + // Search the edge torg->tdest. + assert(sorg(sface) == torg); // SELF_CHECK + if (sdest(sface) != tdest) { + senext2self(sface); + sesymself(sface); + } + if (sdest(sface) != tdest) continue; + + sspivot(sface, seg); + if (seg.sh == NULL) continue; + // assert(seg.sh != NULL); It may or may not be subsegloop. + + // Find the adjacent subface on the same facet. + fmarker = in->facetmarkerlist[shellmark(sface) - 1]; + sface1.sh = NULL; + k++; + for (; k < idx2faclist[idx + 1]; k++) { + sface1 = facperverlist[k]; + // The face may be deleted if it is a duplicated face. + if (sface1.sh[3] == NULL) continue; + // Search the edge torg->tdest. + assert(sorg(sface1) == torg); // SELF_CHECK + if (sdest(sface1) != tdest) { + senext2self(sface1); + sesymself(sface1); + } + if (sdest(sface1) != tdest) continue; + // Found a subface sharing at the same edge. + fmarker1 = in->facetmarkerlist[shellmark(sface1) - 1]; + if (fmarker1 == fmarker) { + // Found a pair of adjacent subfaces. Connect them. + // Delete a redundent segment. + sspivot(sface1, seg1); + assert(seg1.sh != NULL); // SELF_CHECK + shellfacedealloc(subsegs, seg.sh); + shellfacedealloc(subsegs, seg1.sh); + ssdissolve(sface); + ssdissolve(sface1); + // Connect them. + sbond(sface, sface1); + // Set Steiner point -to- subface map. + if (pointtype(torg) == FREEFACETVERTEX) { + setpoint2sh(torg, sencode(sface)); } - } 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. + if (pointtype(tdest) == FREEFACETVERTEX) { + setpoint2sh(tdest, sencode(sface)); } - } else { - sign = 1; // A boundary edge. + break; } } - } else { - sign = 1; // A segment! - } - if (sign >= 0) { - // Add a boundary edge. - caveshbdlist->newindex((void **) &pssub); - *pssub = *parysh; + break; } - senextself(*parysh); + subsegloop.sh = shellfacetraverse(subsegs); } - } + } // if (b->psc) - // 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); + subsegloop.shver = 0; + subsegs->traversalinit(); + subsegloop.sh = shellfacetraverse(subsegs); + while (subsegloop.sh != (shellface *) NULL) { + 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 = idx2faclist[idx]; k < idx2faclist[idx + 1]; k++) { + sface = facperverlist[k]; + // The face may be deleted if it is a duplicated face. + if (sface.sh[3] == NULL) continue; + // Search the edge torg->tdest. + assert(sorg(sface) == torg); // SELF_CHECK + if (sdest(sface) != tdest) { + senext2self(sface); + sesymself(sface); + } + if (sdest(sface) != tdest) continue; + + // Save the face f in facelink. + if (flippool->items >= 2) { + f1 = facelink; + for (m = 0; m < flippool->items - 1; m++) { + f2 = f1->nextitem; + ori1 = orient3d(torg, tdest, sapex(f1->ss), sapex(f2->ss)); + ori2 = orient3d(torg, tdest, sapex(f1->ss), sapex(sface)); + if (ori1 > 0) { + // apex(f2) is below f1. + if (ori2 > 0) { + // apex(f) is below f1 (see Fig.1). + ori3 = orient3d(torg, tdest, sapex(f2->ss), sapex(sface)); + if (ori3 > 0) { + // apex(f) is below f2, insert it. + break; + } else if (ori3 < 0) { + // apex(f) is above f2, continue. + } else { // ori3 == 0; + // f is coplanar and codirection with f2. + unifysubfaces(&(f2->ss), &sface); + break; + } + } else if (ori2 < 0) { + // apex(f) is above f1 below f2, inset it (see Fig. 2). + break; + } else { // ori2 == 0; + // apex(f) is coplanar with f1 (see Fig. 5). + ori3 = orient3d(torg, tdest, sapex(f2->ss), sapex(sface)); + if (ori3 > 0) { + // apex(f) is below f2, insert it. + break; + } else { + // f is coplanar and codirection with f1. + unifysubfaces(&(f1->ss), &sface); + break; + } + } + } else if (ori1 < 0) { + // apex(f2) is above f1. + if (ori2 > 0) { + // apex(f) is below f1, continue (see Fig. 3). + } else if (ori2 < 0) { + // apex(f) is above f1 (see Fig.4). + ori3 = orient3d(torg, tdest, sapex(f2->ss), sapex(sface)); + if (ori3 > 0) { + // apex(f) is below f2, insert it. + break; + } else if (ori3 < 0) { + // apex(f) is above f2, continue. + } else { // ori3 == 0; + // f is coplanar and codirection with f2. + unifysubfaces(&(f2->ss), &sface); + break; + } + } else { // ori2 == 0; + // f is coplanar and with f1 (see Fig. 6). + ori3 = orient3d(torg, tdest, sapex(f2->ss), sapex(sface)); + if (ori3 > 0) { + // f is also codirection with f1. + unifysubfaces(&(f1->ss), &sface); + break; + } else { + // f is above f2, continue. + } + } + } else { // ori1 == 0; + // apex(f2) is coplanar with f1. By assumption, f1 is not + // coplanar and codirection with f2. + if (ori2 > 0) { + // apex(f) is below f1, continue (see Fig. 7). + } else if (ori2 < 0) { + // apex(f) is above f1, insert it (see Fig. 7). + break; + } else { // ori2 == 0. + // apex(f) is coplanar with f1 (see Fig. 8). + // f is either codirection with f1 or is codirection with f2. + facenormal(torg, tdest, sapex(f1->ss), n1, 1, NULL); + facenormal(torg, tdest, sapex(sface), n2, 1, NULL); + if (dot(n1, n2) > 0) { + unifysubfaces(&(f1->ss), &sface); + } else { + unifysubfaces(&(f2->ss), &sface); + } + break; + } } + // Go to the next item; + f1 = f2; + } // for (m = 0; ...) + if (sface.sh[3] != NULL) { + // Insert sface between f1 and f2. + newlinkitem = (badface *) flippool->alloc(); + newlinkitem->ss = sface; + newlinkitem->nextitem = f1->nextitem; + f1->nextitem = newlinkitem; + } + } else if (flippool->items == 1) { + f1 = facelink; + // Make sure that f is not coplanar and codirection with f1. + ori1 = orient3d(torg, tdest, sapex(f1->ss), sapex(sface)); + if (ori1 == 0) { + // f is coplanar with f1 (see Fig. 8). + facenormal(torg, tdest, sapex(f1->ss), n1, 1, NULL); + facenormal(torg, tdest, sapex(sface), n2, 1, NULL); + if (dot(n1, n2) > 0) { + // The two faces are codirectional as well. + unifysubfaces(&(f1->ss), &sface); + } + } + // Add this face to link if it is not deleted. + if (sface.sh[3] != NULL) { + // Add this face into link. + newlinkitem = (badface *) flippool->alloc(); + newlinkitem->ss = sface; + newlinkitem->nextitem = NULL; + f1->nextitem = newlinkitem; } - sbond1(newsh, casout); - sbond1(casin, newsh); } else { - // This side is empty. + // The first face. + newlinkitem = (badface *) flippool->alloc(); + newlinkitem->ss = sface; + newlinkitem->nextitem = NULL; + facelink = newlinkitem; } - } 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; + } // for (k = idx2faclist[idx]; ...) - // 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 (b->psc) { + // Set Steiner point -to- segment map. + if (pointtype(torg) == FREESEGVERTEX) { + setpoint2sh(torg, sencode(subsegloop)); } - 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); + if (pointtype(tdest) == FREESEGVERTEX) { + setpoint2sh(tdest, sencode(subsegloop)); } } - } - 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); + // Set the connection between this segment and faces containing it, + // at the same time, remove redundant segments. + f1 = facelink; + for (k = 0; k < flippool->items; k++) { + sspivot(f1->ss, testseg); + // If 'testseg' is not 'subsegloop' and is not dead, it is redundant. + if ((testseg.sh != subsegloop.sh) && (testseg.sh[3] != NULL)) { + shellfacedealloc(subsegs, testseg.sh); } - } 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; + // Bonds the subface and the segment together. + ssbond(f1->ss, subsegloop); + f1 = f1->nextitem; } - delete [] abfaces; - } - 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; + // Create the face ring at the segment. + if (flippool->items > 1) { + f1 = facelink; + for (k = 1; k <= flippool->items; k++) { + k < flippool->items ? f2 = f1->nextitem : f2 = facelink; + sbond1(f1->ss, f2->ss); + f1 = f2; } } - } - // 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)); - } - } - } + // All identified segments has an init marker "0". + flippool->restart(); - // 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); + // Are there length constraints? + if (b->quality && (in->segmentconstraintlist != (REAL *) NULL)) { + int e1, e2; + REAL len; + for (k = 0; k < in->numberofsegmentconstraints; k++) { + e1 = (int) in->segmentconstraintlist[k * 3]; + e2 = (int) in->segmentconstraintlist[k * 3 + 1]; + if (((pointmark(torg) == e1) && (pointmark(tdest) == e2)) || + ((pointmark(torg) == e2) && (pointmark(tdest) == e1))) { + len = in->segmentconstraintlist[k * 3 + 2]; + setareabound(subsegloop, len); + break; } - sesymself(*parysh); } } - shellfacedealloc(subfaces, parysh->sh); - } - // Clean the working lists. - caveshlist->restart(); - caveshbdlist->restart(); + subsegloop.sh = shellfacetraverse(subsegs); + } - return loc; + delete [] idx2faclist; + delete [] facperverlist; } /////////////////////////////////////////////////////////////////////////////// // // -// 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. // -// // -// '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.// +// mergefacets() Merge adjacent facets. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::formstarpolygon(point pt, list* trilist, list* vertlist) +void tetgenmesh::mergefacets() { - face steinsh, lnextsh, rnextsh; - face checkseg; + face parentsh, neighsh, neineish; + face segloop; point pa, pb, pc, pd; - int i; + REAL ang_tol, ang; + int remsegcount; + int fidx1, fidx2; + int fmrk1, fmrk2; - // 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); - } - - // 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); - } - } 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); + if (b->verbose > 1) { + printf(" Merging adjacent facets.\n"); } -} -/////////////////////////////////////////////////////////////////////////////// -// // -// 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. // -// // -// 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. // -// // -/////////////////////////////////////////////////////////////////////////////// + // The dihedral angle bound for two different facets. + // Set by -p option. Default is 179 degree. + ang_tol = b->facet_ang_tol / 180.0 * PI; + remsegcount = 0; -/////////////////////////////////////////////////////////////////////////////// -// // -// getfacetabovepoint() Get a point above a plane pass through a facet. // -// // -// The calculcated point is saved in 'facetabovepointarray'. The 'abovepoint'// -// is set on return. // -// // -/////////////////////////////////////////////////////////////////////////////// + // Loop all segments, merge adjacent coplanar facets. + subsegs->traversalinit(); + segloop.sh = shellfacetraverse(subsegs); + while (segloop.sh != (shellface *) NULL) { + spivot(segloop, parentsh); + if (parentsh.sh != NULL) { + spivot(parentsh, neighsh); + if (neighsh.sh != NULL) { + spivot(neighsh, neineish); + if (neineish.sh == parentsh.sh) { + // Exactly two subfaces at this segment. + fidx1 = shellmark(parentsh) - 1; + fidx2 = shellmark(neighsh) - 1; + // Only merge them if they are in different facet. + if (fidx1 != fidx2) { + // The two subfaces are not in the same facet. + if (in->facetmarkerlist != NULL) { + fmrk1 = in->facetmarkerlist[fidx1]; + fmrk2 = in->facetmarkerlist[fidx2]; + } else { + fmrk1 = fmrk2 = 0; + } + // Only merge them if they have the same boundary marker. + if (fmrk1 == fmrk2) { + pa = sorg(segloop); + pb = sdest(segloop); + pc = sapex(parentsh); + pd = sapex(neighsh); + // Calculate the dihedral angle at the segment [a,b]. + ang = facedihedral(pa, pb, pc, pd); + if (ang > PI) ang = (2 * PI - ang); + if (ang > ang_tol) { + remsegcount++; + ssdissolve(parentsh); + ssdissolve(neighsh); + shellfacedealloc(subsegs, segloop.sh); + // Add the edge to flip stack. + flipshpush(&parentsh); + } // if (ang > ang_tol) + } // if (fmrk1 == fmrk2) + } // if (fidx1 != fidx2) + } // if (neineish.sh == parentsh.sh) + } + } + segloop.sh = shellfacetraverse(subsegs); + } -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 (flipstack != NULL) { + lawsonflip(); // Recover Delaunayness. + } - 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; - } - } - } - assert(smallcos < 1.0); // p1->p3 != p1->p2. - p3 = * (point *)(* verlist)[smallidx]; - verlist->clear(); - - 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); - } - } - - // 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; - } - } - } - - // 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)); - } + if (b->verbose > 1) { + printf(" %d segments are removed.\n", remsegcount); } - // Save the abovepoint in 'facetabovepointarray'. - shmark = shellmark(*facetsh); - facetabovepointarray[shmark] = abovepoint; - - delete trilist; - delete tetlist; - delete verlist; } /////////////////////////////////////////////////////////////////////////////// // // -// incrflipdelaunaysub() Create a DT from a 3D coplanar point set using // -// the incremental flip algorithm. // +// identifypscedges() Identify PSC edges. // // // -// Let T be the current Delaunay triangulation (of vertices of a facet F). // -// 'shmark', the index of F in 'in->facetlist' (starts from 1). // +// The set of PSC edges are provided in the 'in->edgelist'. Each edge should // +// also be an edge in the surface mesh. We find the corresponding edges in // +// the surface mesh and make them segments of the mesh. // +// // +// It is possible to give an edge which is not in any facet, i.e., it is a // +// dangling edge inside the volume. // // // /////////////////////////////////////////////////////////////////////////////// -bool tetgenmesh::incrflipdelaunaysub(int shmark, REAL eps, list* ptlist, - int holes, REAL* holelist, queue* flipque) +void tetgenmesh::identifypscedges(point *idx2verlist) { - 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; - - // Get the point array (saved in 'ptlist'). - insertarray = (point *) ptlist->base; - arraysize = ptlist->len(); - if (arraysize < 3) return false; - - // 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) { - // 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(", ..."); - } - printf(") looks like a line.\n"); - // terminatetetgen(1); - return false; - } - // 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)); - } - - // 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; - } - } - } + face* shperverlist; + int* idx2shlist; + face searchsh, neighsh; + face segloop, checkseg, newseg; + point checkpt, pa = NULL, pb = NULL; + int *endpts; + int edgemarker; + int idx, i, j; - // 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; - } - } - } - } + int e1, e2; + REAL len; - if (aboveflag) { - // Compute the 'abovepoint' for orient3d(). - abovepoint = facetabovepointarray[shmark]; - if (abovepoint == (point) NULL) { - getfacetabovepoint(&newsh); - } + if (!b->quiet) { + printf("Inserting edges ...\n"); } - 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]; - } - } + // All identified segments have the initial marker '1'. + // All segments inserted here should have a marker 'k >= 0'. - // 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 (b->psc) { + // First mark all segments of the mesh with a marker '-1'. + subsegs->traversalinit(); + segloop.sh = shellfacetraverse(subsegs); + while (segloop.sh != NULL) { + setshellmark(segloop, -1); + segloop.sh = shellfacetraverse(subsegs); } } - return true; -} + // Construct a map from points to subfaces. + makepoint2submap(subfaces, idx2shlist, shperverlist); -/////////////////////////////////////////////////////////////////////////////// -// // -// finddirectionsub() Find the first subface in a facet on the path from // -// one point to another. // -// // -// 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. // -// // -// The return value notes whether the destination or apex of the found face // -// is collinear with the two points in question. // -// // -/////////////////////////////////////////////////////////////////////////////// + // Process the set of PSC edges. + for (i = 0; i < in->numberofedges; i++) { + endpts = &(in->edgelist[(i << 1)]); + edgemarker = in->edgemarkerlist ? in->edgemarkerlist[i] : 0; + + // Find a face contains the edge. + newseg.sh = NULL; + searchsh.sh = NULL; + idx = endpts[0] - in->firstnumber; + for (j = idx2shlist[idx]; j < idx2shlist[idx + 1]; j++) { + checkpt = sdest(shperverlist[j]); + if (pointmark(checkpt) == endpts[1]) { + searchsh = shperverlist[j]; + break; // Found. + } else { + checkpt = sapex(shperverlist[j]); + if (pointmark(checkpt) == endpts[1]) { + senext2(shperverlist[j], searchsh); + sesymself(searchsh); + break; + } + } + } // j -enum tetgenmesh::finddirectionresult tetgenmesh::finddirectionsub( - face* searchsh, point tend) -{ - 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; - - // 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; + if (searchsh.sh != NULL) { + // Check if this edge is already a segment of the mesh. + sspivot(searchsh, checkseg); + if (checkseg.sh != NULL) { + // This segment already exist. + newseg = checkseg; + } else { + // Create a new segment at this edge. + pa = sorg(searchsh); + pb = sdest(searchsh); + makeshellface(subsegs, &newseg); + setshvertices(newseg, pa, pb, NULL); + ssbond(searchsh, newseg); + spivot(searchsh, neighsh); + if (neighsh.sh != NULL) { + ssbond(neighsh, newseg); + } + if (b->psc) { + if (pointtype(pa) == FREESEGVERTEX) { + setpoint2sh(pa, sencode(newseg)); + } + if (pointtype(pb) == FREESEGVERTEX) { + setpoint2sh(pb, sencode(newseg)); + } + } + } } else { - rightflag = 0; + // It is a dangling segment (not belong to any facets). + // Get the two endpoints of this segment. + pa = idx2verlist[endpts[0]]; + pb = idx2verlist[endpts[1]]; + // Check if segment [a,b] already exists. + // TODO: Change the brute-force search. Slow! + point *ppt; + subsegs->traversalinit(); + segloop.sh = shellfacetraverse(subsegs); + while (segloop.sh != NULL) { + ppt = (point *) &(segloop.sh[3]); + if (((ppt[0] == pa) && (ppt[1] == pb)) || + ((ppt[0] == pb) && (ppt[1] == pa))) { + // Found! + newseg = segloop; + break; + } + segloop.sh = shellfacetraverse(subsegs); + } + if (newseg.sh == NULL) { + makeshellface(subsegs, &newseg); + setshvertices(newseg, pa, pb, NULL); + if (b->psc) { + if (pointtype(pa) == FREESEGVERTEX) { + setpoint2sh(pa, sencode(newseg)); + } + if (pointtype(pb) == FREESEGVERTEX) { + setpoint2sh(pb, sencode(newseg)); + } + } + } } - } - 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 (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; - } - 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); - } - 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; - } -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// 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()'. // -// // -/////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::insertsubseg(face* tri) -{ - face oppotri; - face newsubseg; - point pa, pb; - REAL len; - int e1, e2; - int i; + setshellmark(newseg, edgemarker); - // 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]; @@ -17353,936 +14146,129 @@ void tetgenmesh::insertsubseg(face* tri) if (((pointmark(pa) == e1) && (pointmark(pb) == e2)) || ((pointmark(pa) == e2) && (pointmark(pb) == e1))) { len = in->segmentconstraintlist[i * 3 + 2]; - setareabound(newsubseg, len); + setareabound(newseg, len); break; } } } - // 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); - } */ - } -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// 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. // -// // -// Returns true if the entire segment is successfully inserted, and false if // -// the job must be finished by constrainededge(). // -// // -/////////////////////////////////////////////////////////////////////////////// - -bool tetgenmesh::scoutsegmentsub(face* searchsh, point tend) -{ - face newsubseg; - face crosssub, crosssubseg; - point leftpoint, rightpoint; - enum finddirectionresult collinear; - - 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); - } - // 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; - } -} + } // i -/////////////////////////////////////////////////////////////////////////////// -// // -// flipedgerecursive() Flip an edge. // -// // -// This is a support routine for inserting segments into a CDT. // -// // -// 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; + delete [] shperverlist; + delete [] idx2shlist; - 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); + if (b->psc) { + // Removing all segments with a marker '-1'. + subsegs->traversalinit(); + segloop.sh = shellfacetraverse(subsegs); + while (segloop.sh != NULL) { + if (shellmark(segloop) == -1) { + shellfacedealloc(subsegs, segloop.sh); } - // Flip the edge. - flipedgerecursive(&fixupsh, flipqueue); + segloop.sh = shellfacetraverse(subsegs); } - } while (!doflip); -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// constrainededge() Force a segment into a CDT. // -// // -// 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::constrainededge(face* startsh, point tend, queue* flipqueue) -{ - point tstart, tright, tleft; - REAL rori, lori; - bool collision; + + // Connecting subsegments at Steiner points. + face seg1, seg2; + // Re-use 'idx2shlist' and 'shperverlist'. + makepoint2submap(subsegs, idx2shlist, shperverlist); - 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; + points->traversalinit(); + pa = pointtraverse(); + while (pa != NULL) { + if (pointtype(pa) == FREESEGVERTEX) { + idx = pointmark(pa) - in->firstnumber; + // There must be only two segments containing this vertex. + assert((idx2shlist[idx + 1] - idx2shlist[idx]) == 2); + i = idx2shlist[idx]; + seg1 = shperverlist[i]; + seg2 = shperverlist[i+1]; + senextself(seg1); + senextself(seg2); + sbond(seg1, seg2); } - 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); + pa = pointtraverse(); } + + delete [] shperverlist; + delete [] idx2shlist; } } /////////////////////////////////////////////////////////////////////////////// // // -// recoversegment() Recover a segment in the surface triangulation. // +// meshsurface() Create a surface mesh of the input PLC. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::recoversegment(point tstart, point tend, queue* flipqueue) +void tetgenmesh::meshsurface() { - face searchsh; + arraypool *ptlist, *conlist; + point *idx2verlist; + point tstart, tend, *pnewpt, *cons; + tetgenio::facet *f; + tetgenio::polygon *p; + int end1, end2; + int shmark, i, j; - if (b->verbose > 2) { - printf(" Insert seg (%d, %d).\n", pointmark(tstart), pointmark(tend)); + if (!b->quiet) { + printf("Creating surface mesh ...\n"); } - // 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); -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// infecthullsub() Virally infect all of the triangles of the convex hull // -// that are not protected by subsegments. // -// // -/////////////////////////////////////////////////////////////////////////////// + // Create a map from indices to points. + makeindex2pointmap(idx2verlist); -void tetgenmesh::infecthullsub(memorypool* viri) -{ - face hulltri, nexttri, starttri; - face hullsubseg; - shellface **deadshellface; - - // 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); - spivot(hulltri, nexttri); - while (nexttri.sh != dummysh) { - if (sorg(nexttri) != sdest(hulltri)) { - sesymself(nexttri); - } - senext(nexttri, hulltri); - spivot(hulltri, nexttri); - } - } while (hulltri != starttri); -} + // Initialize arrays (block size: 2^8 = 256). + ptlist = new arraypool(sizeof(point *), 8); + conlist = new arraypool(2 * sizeof(point *), 8); -/////////////////////////////////////////////////////////////////////////////// -// // -// 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. // -// // -/////////////////////////////////////////////////////////////////////////////// + // Loop the facet list, triangulate each facet. + for (shmark = 1; shmark <= in->numberoffacets; shmark++) { -void tetgenmesh::plaguesub(memorypool* viri) -{ - face testtri, neighbor, ghostsh; - face neighborsubseg; - shellface **virusloop; - shellface **deadshellface; - point *ppt; - int i, j; + // Get a facet F. + f = &in->facetlist[shmark - 1]; - // 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)); + // Process the duplicated points first, they are marked with type + // DUPLICATEDVERTEX. If p and q are duplicated, and p'index > q's, + // then p is substituted by q. + if (dupverts > 0l) { + // 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]; + if (pointtype(tstart) == DUPLICATEDVERTEX) { + // Reset the index of vertex-j. + tend = point2ppt(tstart); + end2 = pointmark(tend); + p->vertexlist[j] = end2; } } } - senextself(testtri); } - 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); - } - 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(); -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// 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.// -// // -/////////////////////////////////////////////////////////////////////////////// - -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) { - // 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; + // Loop polygons of F, get the set of vertices and 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. } - } - } - - 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. // -// // -// 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). // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::triangulate(int shmark, REAL eps, list* ptlist, list* conlist, - int holes, REAL* holelist, memorypool* viri, queue* flipqueue) -{ - 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); - } - - // 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]); - } - } -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// retrievenewsubs() Retrieve newly created subfaces. // -// // -// 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::retrievenewsubs(list* newshlist, bool removeseg) -{ - 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); - - // 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); - } - } - for (i = 0; i < newshlist->len(); i++) { - startsh = * (face *)(* newshlist)[i]; - suninfect(startsh); - } -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// unifysegments() Unify identical segments and build facet connections. // -// // -// 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. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::unifysegments() -{ - 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; - - 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); - } - } - } - 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))); - } - 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; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// assignsegmentmarkers() Assign markers given in "in->edgemarkerlist". // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::assignsegmentmarkers() -{ - shellface **segsperverlist; - face sseg; - bool isseg; - int *idx2seglist; - int end1, end2, tend1, tend2; - int index, i, j; - - if (b->verbose > 0) { - printf(" Assigning segment markers.\n"); - } - - 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; - } - } - } - - delete [] idx2seglist; - delete [] segsperverlist; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// 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. // -// // -/////////////////////////////////////////////////////////////////////////////// - -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); - } - - // 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); - } - // 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); - } - } - } - } - } // neighsh.sh != dummysh - } // parentsh.sh != dummysh - segloop.sh = shellfacetraverse(subsegs); - } - - if (!flipqueue->empty()) { - // Restore the Delaunay property in the facet triangulation. - lawson(flipqueue); - } - - delete [] segspernodelist; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// meshsurface() Create the surface mesh of a PLC. // -// // -// Let X be the PLC, the surface mesh S of X consists of triangulated facets.// -// S is created mainly in the following steps: // -// // -// (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. // -// // -/////////////////////////////////////////////////////////////////////////////// - -long tetgenmesh::meshsurface() -{ - 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; - - 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; - - caveshlist = new arraypool(sizeof(face), 10); - caveshbdlist = new arraypool(sizeof(face), 10); - - // 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; - } - } - } - } - - // 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]; + tstart = idx2verlist[end1]; // Add tstart to V if it haven't been added yet. - if (worklist[end1] == 0) { - ptlist->append(&tstart); - worklist[end1] = 1; + if (!pinfected(tstart)) { + pinfect(tstart); + ptlist->newindex((void **) &pnewpt); + *pnewpt = tstart; } // Loop other vertices of this polygon. for (j = 1; j <= p->numberofvertices; j++) { @@ -18301,21 +14287,22 @@ long tetgenmesh::meshsurface() } else { if (end1 != end2) { // 'end1' and 'end2' form a segment. - tend = idx2verlist[end2 - in->firstnumber]; + tend = idx2verlist[end2]; // Add tstart to V if it haven't been added yet. - if (worklist[end2] == 0) { - ptlist->append(&tend); - worklist[end2] = 1; + if (!pinfected(tend)) { + pinfect(tend); + ptlist->newindex((void **) &pnewpt); + *pnewpt = tend; } // Save the segment in S (conlist). - cons = (point *) conlist->append(NULL); + conlist->newindex((void **) &cons); 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. + // Two identical vertices mean 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. @@ -18325,49 +14312,50 @@ long tetgenmesh::meshsurface() } } // 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; + for (i = 0; i < ptlist->objects; i++) { + pnewpt = (point *) fastlookup(ptlist, i); + puninfect(*pnewpt); } - // Create a CDT of F. - triangulate(shmark, b->epsilon * 1e+2, ptlist, conlist, f->numberofholes, - f->holelist, viri, flipqueue); + // Triangulate F into a CDT. + triangulate(shmark, ptlist, conlist, f->numberofholes, f->holelist); + // Clear working lists. - ptlist->clear(); - conlist->clear(); - viri->restart(); + ptlist->restart(); + conlist->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(); + if (!b->diagnose) { + // Remove redundant segments and build the face links. + unifysegments(); + if (!b->psc && !b->nomergefacet && !b->nobisect) { + // Merge adjacent coplanar facets. + mergefacets(); + } + if (in->numberofedges > 0) { // if (b->psc) + // There are segments specified by the user. Read and create them. + identifypscedges(idx2verlist); + } + if (!b->psc) { + // Mark all segment vertices to be RIDGEVERTEX. + face segloop; + point *ppt; + subsegs->traversalinit(); + segloop.sh = shellfacetraverse(subsegs); + while (segloop.sh != NULL) { + ppt = (point *) &(segloop.sh[3]); + setpointtype(ppt[0], RIDGEVERTEX); + setpointtype(ppt[1], RIDGEVERTEX); + segloop.sh = shellfacetraverse(subsegs); + } } - }*/ - - // Remember the number of input segments (for output). - insegments = subsegs->items; - - if (checkpbcs) { - // Create the global array 'segpbcgrouptable'. - // createsegpbcgrouptable(); } if (b->object == tetgenbehavior::STL) { @@ -18375,22 +14363,17 @@ long tetgenmesh::meshsurface() jettisonnodes(); } - if (!b->nomerge && !b->nobisect && !checkpbcs) { - // No '-M' switch - merge adjacent facets if they are coplanar. - mergefacets(flipqueue); + if (b->verbose) { + printf(" %ld (%ld) subfaces (segments).\n", subfaces->items, + subsegs->items); } - // Create the point-to-segment map. - makepoint2segmap(); + // The total number of iunput segments. + insegments = subsegs->items; delete [] idx2verlist; - delete [] worklist; delete ptlist; delete conlist; - delete flipqueue; - delete viri; - - return subsegs->items; } /////////////////////////////////////////////////////////////////////////////// @@ -18398,7 +14381,7 @@ long tetgenmesh::meshsurface() // 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 // +// a cut plane parallel to x-, or, y-, or z-axis. 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: // // // @@ -18417,10 +14400,10 @@ long tetgenmesh::meshsurface() // // /////////////////////////////////////////////////////////////////////////////// -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::interecursive(shellface** subfacearray, int arraysize, + int axis, REAL bxmin, REAL bxmax, REAL bymin, + REAL bymax, REAL bzmin, REAL bzmax, + int* internum) { shellface **leftarray, **rightarray; face sface1, sface2; @@ -18432,19 +14415,19 @@ interecursive(shellface** subfacearray, int arraysize, int axis, REAL bxmin, int leftsize, rightsize; int i, j; - if (b->verbose > 1) { - printf(" Recur %d faces. Bbox (%g, %g, %g),(%g, %g, %g). %s-axis\n", + if (b->verbose > 2) { + 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); + terminatetetgen(this, 1); } rightarray = new shellface*[arraysize]; if (rightarray == NULL) { - terminatetetgen(1); + terminatetetgen(this, 1); } leftsize = rightsize = 0; @@ -18481,9 +14464,7 @@ interecursive(shellface** subfacearray, int arraysize, int axis, REAL bxmin, toright = true; } // At least one is true; -#ifdef SELF_CHECK assert(!(toleft == false && toright == false)); -#endif if (toleft) { leftarray[leftsize] = sface1.sh; leftsize++; @@ -18532,7 +14513,7 @@ interecursive(shellface** subfacearray, int arraysize, int axis, REAL bxmin, 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); + intersect = (enum interresult) tri_tri_inter(p1, p2, p3, p4, p5, p6); if (intersect == INTERSECT || intersect == SHAREFACE) { if (!b->quiet) { if (intersect == INTERSECT) { @@ -18544,8 +14525,9 @@ interecursive(shellface** subfacearray, int arraysize, int axis, REAL bxmin, } 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)); + printf(" (%4d, %4d, %4d) and (%4d, %4d, %4d)\n", + pointmark(p1), pointmark(p2), pointmark(p3), + pointmark(p4), pointmark(p5), pointmark(p6)); } } // Increase the number of intersecting pairs. @@ -18596,7 +14578,7 @@ void tetgenmesh::detectinterfaces() int i; if (!b->quiet) { - printf("Detecting intersecting facets.\n"); + printf("Detecting self-intersecting facets...\n"); } // Construct a map from indices to subfaces; @@ -18612,7 +14594,7 @@ void tetgenmesh::detectinterfaces() 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 + // parallel to x-, or, y-, or z-axis. 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); @@ -18655,286 +14637,78 @@ void tetgenmesh::detectinterfaces() /////////////////////////////////////////////////////////////////////////////// // // -// markacutevertices() Mark acute vertices. // -// // -// A vertex v is called acute if there are two segments sharing at v forming // -// an acute angle (i.e. smaller than 90 degree). // +// makesegmentendpointsmap() Create a map from a segment to its endpoints.// // // -// This routine finds all acute vertices in the PLC and marks them as point- // -// type ACUTEVERTEX. The other vertices of segments which are non-acute will // -// be marked as NACUTEVERTEX. Vertices which are not endpoints of segments // -// (such as DUPLICATEDVERTEX, UNUSEDVERTEX, etc) are not infected. // -// // -// NOTE: This routine should be called before Steiner points are introduced. // -// That is, no point has type like FREESEGVERTEX, etc. // +// The map is saved in the array 'segmentendpointslist'. The length of this // +// array is twice the number of segments. Each segment is assigned a unique // +// index (starting from 0). // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::markacutevertices(REAL acuteangle) +void tetgenmesh::makesegmentendpointsmap() { - shellface **segsperverlist; - face segloop, nextseg; - point pointloop, edest, eapex; - REAL cosbound, anglearc; - REAL v1[3], v2[3], L, D; - bool isacute; - int *idx2seglist; - int acutecount; - int idx, i, j, k; + arraypool *segptlist; + face segloop, prevseg, nextseg; + point eorg, edest, *parypt; + int segindex = 0, idx = 0; + int i; if (b->verbose > 0) { - printf(" Marking acute vertices.\n"); + printf(" Creating the segment-endpoints map.\n"); } - anglearc = acuteangle * PI / 180.0; - cosbound = cos(anglearc); - acutecount = 0; - // Constructing a map from vertex to segments. - makesegmentmap(idx2seglist, segsperverlist); + segptlist = new arraypool(2 * sizeof(point), 10); - // Loop over the set of vertices. - points->traversalinit(); - pointloop = pointtraverse(); - while (pointloop != (point) NULL) { - idx = pointmark(pointloop) - in->firstnumber; - // Only do test if p is an endpoint of some segments. - if (idx2seglist[idx + 1] > idx2seglist[idx]) { - // Init p to be non-acute. - setpointtype(pointloop, NACUTEVERTEX); - isacute = false; - // Loop through all segments sharing at p. - for (i = idx2seglist[idx]; i < idx2seglist[idx + 1] && !isacute; i++) { - segloop.sh = segsperverlist[i]; - // segloop.shver = 0; - if (sorg(segloop) != pointloop) sesymself(segloop); - edest = sdest(segloop); - for (j = i + 1; j < idx2seglist[idx + 1] && !isacute; j++) { - nextseg.sh = segsperverlist[j]; - // nextseg.shver = 0; - if (sorg(nextseg) != pointloop) sesymself(nextseg); - eapex = sdest(nextseg); - // Check the angle formed by segs (p, edest) and (p, eapex). - for (k = 0; k < 3; k++) { - v1[k] = edest[k] - pointloop[k]; - v2[k] = eapex[k] - pointloop[k]; - } - L = sqrt(v1[0] * v1[0] + v1[1] * v1[1] + v1[2] * v1[2]); - for (k = 0; k < 3; k++) v1[k] /= L; - L = sqrt(v2[0] * v2[0] + v2[1] * v2[1] + v2[2] * v2[2]); - for (k = 0; k < 3; k++) v2[k] /= L; - D = v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2]; - // Is D acute? - isacute = (D >= cosbound); - } - } - if (isacute) { - // Mark p to be acute. - setpointtype(pointloop, ACUTEVERTEX); - acutecount++; - } - } - pointloop = pointtraverse(); + // A segment s may have been split into many subsegments. Operate the one + // which contains the origin of s. Then mark the rest of subsegments. + subsegs->traversalinit(); + segloop.sh = shellfacetraverse(subsegs); + segloop.shver = 0; + while (segloop.sh != NULL) { + senext2(segloop, prevseg); + spivotself(prevseg); + if (prevseg.sh == NULL) { + eorg = sorg(segloop); + edest = sdest(segloop); + setfacetindex(segloop, segindex); + senext(segloop, nextseg); + spivotself(nextseg); + while (nextseg.sh != NULL) { + setfacetindex(nextseg, segindex); + nextseg.shver = 0; + if (sorg(nextseg) != edest) sesymself(nextseg); + assert(sorg(nextseg) == edest); + edest = sdest(nextseg); + // Go the next connected subsegment at edest. + senextself(nextseg); + spivotself(nextseg); + } + segptlist->newindex((void **) &parypt); + parypt[0] = eorg; + parypt[1] = edest; + segindex++; + } + segloop.sh = shellfacetraverse(subsegs); } - delete [] idx2seglist; - delete [] segsperverlist; - - if ((b->verbose > 0) && (acutecount > 0)) { - printf(" %d acute vertices.\n", acutecount); + if (b->verbose) { + printf(" Found %ld segments.\n", segptlist->objects); } -} -/////////////////////////////////////////////////////////////////////////////// -// // -// finddirection() Find the first tetrahedron on the path from one point // -// to another. // -// // -// Find the tetrahedron that intersects a line segment L (from the origin of // -// 'searchtet' to the point 'tend'), and returns the result in 'searchtet'. // -// The origin of 'searchtet' does not change, even though the tetrahedron // -// returned may differ from the one passed in. This routine is used to find // -// the direction to move in to get from one point to another. // -// // -// The return value notes the location of the line segment L with respect to // -// 'searchtet': // -// - Returns RIGHTCOLLINEAR indicates L is collinear with the line segment // -// from the origin to the destination of 'searchtet'. // -// - Returns LEFTCOLLINEAR indicates L is collinear with the line segment // -// from the origin to the apex of 'searchtet'. // -// - Returns TOPCOLLINEAR indicates L is collinear with the line segment // -// from the origin to the opposite of 'searchtet'. // -// - Returns ACROSSEDGE indicates L intersects with the line segment from // -// the destination to the apex of 'searchtet'. // -// - Returns ACROSSFACE indicates L intersects with the face opposite to // -// the origin of 'searchtet'. // -// - Returns BELOWHULL indicates L crosses outside the mesh domain. This // -// can only happen when the domain is non-convex. // -// // -// NOTE: This routine only works correctly when the mesh is exactly Delaunay.// -// // -// If 'maxtetnumber' > 0, stop the searching process if the number of passed // -// tets is larger than it. Return BELOWHULL. // -// // -/////////////////////////////////////////////////////////////////////////////// + segmentendpointslist = new point[segptlist->objects * 2]; -enum tetgenmesh::finddirectionresult tetgenmesh:: -finddirection(triface *searchtet, point tend, long maxtetnumber) -{ - triface neightet; - point tstart, tdest, tapex, toppo; - REAL ori1, ori2, ori3; - long tetnumber; - - tstart = org(*searchtet); -#ifdef SELF_CHECK - assert(tstart != tend); -#endif - adjustedgering(*searchtet, CCW); - if (tstart != org(*searchtet)) { - enextself(*searchtet); // For keeping the same origin. - } - tdest = dest(*searchtet); - if (tdest == tend) { - return RIGHTCOLLINEAR; - } - tapex = apex(*searchtet); - if (tapex == tend) { - return LEFTCOLLINEAR; - } + totalworkmemory += (segptlist->objects * 2) * sizeof(point *); - ori1 = orient3d(tstart, tdest, tapex, tend); - if (ori1 > 0.0) { - // 'tend' is below the face, get the neighbor of this side. - sym(*searchtet, neightet); - if (neightet.tet != dummytet) { - findorg(&neightet, tstart); - adjustedgering(neightet, CCW); - if (org(neightet) != tstart) { - enextself(neightet); // keep the same origin. - } - // Set the changed configuratiuon. - *searchtet = neightet; - ori1 = -1.0; - tdest = dest(*searchtet); - tapex = apex(*searchtet); - } else { - // A hull face. Only possible for a nonconvex mesh. -#ifdef SELF_CHECK - assert(nonconvex); -#endif - return BELOWHULL; - } + for (i = 0; i < segptlist->objects; i++) { + parypt = (point *) fastlookup(segptlist, i); + segmentendpointslist[idx++] = parypt[0]; + segmentendpointslist[idx++] = parypt[1]; } - // Repeatedly change the 'searchtet', remain 'tstart' be its origin, until - // find a tetrahedron contains 'tend' or is crossed by the line segment - // from 'tstart' to 'tend'. - tetnumber = 0l; - while ((maxtetnumber > 0) && (tetnumber <= maxtetnumber)) { - tetnumber++; - toppo = oppo(*searchtet); - if (toppo == tend) { - return TOPCOLLINEAR; - } - ori2 = orient3d(tstart, toppo, tdest, tend); - if (ori2 > 0.0) { - // 'tend' is below the face, get the neighbor at this side. - fnext(*searchtet, neightet); - symself(neightet); - if (neightet.tet != dummytet) { - findorg(&neightet, tstart); - adjustedgering(neightet, CCW); - if (org(neightet) != tstart) { - enextself(neightet); // keep the same origin. - } - // Set the changed configuration. - *searchtet = neightet; - ori1 = -1.0; - tdest = dest(*searchtet); - tapex = apex(*searchtet); - // Continue the search from the changed 'searchtet'. - continue; - } else { - // A hull face. Only possible for a nonconvex mesh. -#ifdef SELF_CHECK - assert(nonconvex); -#endif - return BELOWHULL; - } - } - ori3 = orient3d(tapex, toppo, tstart, tend); - if (ori3 > 0.0) { - // 'tend' is below the face, get the neighbor at this side. - enext2fnext(*searchtet, neightet); - symself(neightet); - if (neightet.tet != dummytet) { - findorg(&neightet, tstart); - adjustedgering(neightet, CCW); - if (org(neightet) != tstart) { - enextself(neightet); // keep the same origin. - } - // Set the changed configuration. - *searchtet = neightet; - ori1 = -1.0; - tdest = dest(*searchtet); - tapex = apex(*searchtet); - // Continue the search from the changed 'searchtet'. - continue; - } else { - // A hull face. Only possible for a nonconvex mesh. -#ifdef SELF_CHECK - assert(nonconvex); -#endif - return BELOWHULL; - } - } - // Now 'ori1', 'ori2' and 'ori3' are possible be 0.0 or all < 0.0; - if (ori1 < 0.0) { - // Possible cases are: ACROSSFACE, ACROSSEDGE, TOPCOLLINEAR. - if (ori2 < 0.0) { - if (ori3 < 0.0) { - return ACROSSFACE; - } else { // ori3 == 0.0; - // Cross edge (apex, oppo) - enext2fnextself(*searchtet); - esymself(*searchtet); // org(*searchtet) == tstart; - return ACROSSEDGE; - } - } else { // ori2 == 0.0; - if (ori3 < 0.0) { - // Cross edge (dest, oppo) - 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; + delete segptlist; } + /////////////////////////////////////////////////////////////////////////////// // // // finddirection() Find the tet on the path from one point to another. // @@ -18950,60 +14724,67 @@ finddirection(triface *searchtet, point tend, long maxtetnumber) // // // 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) +enum tetgenmesh::interresult + tetgenmesh::finddirection(triface* searchtet, point endpt) { triface neightet; - point pa, pb, pc, pd, pn; + point pa, pb, pc, pd; 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)); + int t1ver; + int s; // The origin is fixed. pa = org(*searchtet); - if (searchtet->ver & 01) { - // Switch to the 0th edge ring. - esymself(*searchtet); - enextself(*searchtet); + if ((point) searchtet->tet[7] == dummypoint) { + // A hull tet. Choose the neighbor of its base face. + decode(searchtet->tet[3], *searchtet); + // Reset the origin to be pa. + if ((point) searchtet->tet[4] == pa) { + searchtet->ver = 11; + } else if ((point) searchtet->tet[5] == pa) { + searchtet->ver = 3; + } else if ((point) searchtet->tet[6] == pa) { + searchtet->ver = 7; + } else { + assert((point) searchtet->tet[7] == pa); + searchtet->ver = 0; + } } + pb = dest(*searchtet); + // Check whether the destination or apex is 'endpt'. if (pb == endpt) { // pa->pb is the search edge. - return INTERVERT; + return ACROSSVERT; } + pc = apex(*searchtet); if (pc == endpt) { // pa->pc is the search edge. - enext2self(*searchtet); - esymself(*searchtet); - return INTERVERT; + eprevesymself(*searchtet); + return ACROSSVERT; } - // Walk through tets at pa until the right one is found. + // Walk through tets around 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; + enextself(*searchtet); + return ACROSSVERT; + } + // Check if we have entered outside of the domain. + if (pd == dummypoint) { + // This is possible when the mesh is non-convex. + assert(nonconvex); + return ACROSSSUB; // Hit a bounday. } // Now assume that the base face abc coincides with the horizon plane, @@ -19014,93 +14795,38 @@ enum tetgenmesh::interresult tetgenmesh::finddirection2(triface* searchtet, hori = orient3d(pa, pb, pc, endpt); rori = orient3d(pb, pa, pd, endpt); lori = orient3d(pa, pc, pd, endpt); - orient3dcount += 3; // Now decide the tet to move. It is possible there are more than one - // tet are viable moves. Use the opposite points of thier neighbors - // to discriminate, i.e., we choose the tet whose opposite point has - // the shortest distance to 'endpt'. + // tets are viable moves. Is so, randomly choose one. if (hori > 0) { if (rori > 0) { if (lori > 0) { // Any of the three neighbors is a viable move. - 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) { + s = randomnation(3); + if (s == 0) { + nextmove = HMOVE; + } else if (s == 1) { 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]); + //s = randomnation(2); + if (randomnation(2)) { + nextmove = HMOVE; } 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]); + //s = randomnation(2); + if (randomnation(2)) { + nextmove = HMOVE; } else { - dist = dmin; - } - if (dist < dmin) { nextmove = LMOVE; - dmin = dist; } } else { // The tet below horizon is chosen. @@ -19111,26 +14837,11 @@ enum tetgenmesh::interresult tetgenmesh::finddirection2(triface* searchtet, 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]); + //s = randomnation(2); + if (randomnation(2)) { + nextmove = RMOVE; } else { - dist = dmin; - } - if (dist < dmin) { nextmove = LMOVE; - dmin = dist; } } else { // The tet below right is chosen. @@ -19145,339 +14856,137 @@ enum tetgenmesh::interresult tetgenmesh::finddirection2(triface* searchtet, if (hori == 0) { if (rori == 0) { // pa->'endpt' is COLLINEAR with pa->pb. - return INTERVERT; + return ACROSSVERT; } if (lori == 0) { // pa->'endpt' is COLLINEAR with pa->pc. - enext2self(*searchtet); - esymself(*searchtet); - return INTERVERT; + eprevesymself(*searchtet); // // [a,c,d] + return ACROSSVERT; } // pa->'endpt' crosses the edge pb->pc. - // enextself(*searchtet); - // return INTEREDGE; - cop = HCOPLANE; - break; + return ACROSSEDGE; } if (rori == 0) { if (lori == 0) { // pa->'endpt' is COLLINEAR with pa->pd. - fnextself(*searchtet); // face abd. - enext2self(*searchtet); - esymself(*searchtet); - return INTERVERT; + esymself(*searchtet); // face bad. + enextself(*searchtet); // face [a,d,b] + return ACROSSVERT; } // pa->'endpt' crosses the edge pb->pd. - // fnextself(*searchtet); // face abd. - // enextself(*searchtet); - // return INTEREDGE; - cop = RCOPLANE; - break; + esymself(*searchtet); // face bad. + enextself(*searchtet); // face adb + return ACROSSEDGE; } if (lori == 0) { // pa->'endpt' crosses the edge pc->pd. - // enext2fnextself(*searchtet); // face cad - // enext2self(*searchtet); - // return INTEREDGE; - cop = LCOPLANE; - break; + eprevesymself(*searchtet); // [a,c,d] + return ACROSSEDGE; } // pa->'endpt' crosses the face bcd. - // enextfnextself(*searchtet); - // return INTERFACE; - cop = NCOPLANE; - break; + return ACROSSFACE; } } } // Move to the next tet, fix pa as its origin. if (nextmove == RMOVE) { - tfnextself(*searchtet); + fnextself(*searchtet); } else if (nextmove == LMOVE) { - enext2self(*searchtet); - tfnextself(*searchtet); + eprevself(*searchtet); + fnextself(*searchtet); enextself(*searchtet); } else { // HMOVE - symedgeself(*searchtet); + fsymself(*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 + assert(org(*searchtet) == pa); 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. // +// scoutsegment() Search an edge in the tetrahedralization. // // // -// Search an edge in the tetrahedralization that matches the given segmment. // -// If such an edge exists, the segment is 'locked' at the edge. 'searchtet' // -// returns this (constrained) edge. Otherwise, the segment is missing. // +// If the edge is found, it returns SHAREEDGE, and 'searchtet' returns the // +// edge from startpt to endpt. // // // -// The returned value indicates one of the following cases: // -// - SHAREEDGE, the segment exists and is inserted in T; // -// - 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 edge is missing, it returns either ACROSSEDGE or ACROSSFACE, which // +// indicates that the edge intersects an edge or a face. If 'refpt' is NULL,// +// 'searchtet' returns the edge or face. If 'refpt' is not NULL, it returns // +// a vertex which encroaches upon this edge, and 'searchtet' returns a tet // +// which containing 'refpt'. // // // -// 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'. // +// The following cases can happen when the input PLC is not valid. // +// - ACROSSVERT, the edge intersects a vertex return by the origin of // +// 'searchtet'. // +// - ACROSSSEG, the edge intersects a segment returned by 'searchtet'. // +// - ACROSSSUB, the edge intersects a subface returned by 'searchtet'. // // // /////////////////////////////////////////////////////////////////////////////// -enum tetgenmesh::interresult tetgenmesh::scoutsegment2(face* sseg, - triface* searchtet, point* refpt) +enum tetgenmesh::interresult + tetgenmesh::scoutsegment(point startpt, point endpt, triface* searchtet, + point* refpt, arraypool* intfacelist) { - triface neightet, reftet; - face splitsh, checkseg; - point startpt, endpt; - point pa, pb, pc, pd; + point 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); + int t1ver; - if (b->verbose > 1) { - printf(" Scout seg (%d, %d).\n", pointmark(startpt), pointmark(endpt)); + if (b->verbose > 2) { + printf(" Scout seg (%d, %d).\n",pointmark(startpt),pointmark(endpt)); } - dir = finddirection2(searchtet, endpt); + point2tetorg(startpt, *searchtet); + dir = finddirection(searchtet, endpt); - if (dir == INTERVERT) { + if (dir == ACROSSVERT) { 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; + // Let the origin of the searchtet be the vertex. + enextself(*searchtet); + if (refpt) *refpt = pd; + return ACROSSVERT; + } + } // if (dir == ACROSSVERT) + + // dir is either ACROSSEDGE or ACROSSFACE. + + enextesymself(*searchtet); // Go to the opposite face. + fsymself(*searchtet); // Enter the adjacent tet. + + if (dir == ACROSSEDGE) { + // Check whether two segments are intersecting. + if (issubseg(*searchtet)) { + return ACROSSSEG; + } + } else if (dir == ACROSSFACE) { + if (checksubfaceflag) { + // Check whether a segment and a subface are intersecting. + if (issubface(*searchtet)) { + return ACROSSSUB; + } } } - if (b->verbose > 1) { - printf(" Scout ref point of seg (%d, %d).\n", pointmark(startpt), - pointmark(endpt)); + if (refpt == NULL) { + // Do not need a reference point. Return. + return dir; } - facecount = across_face_count; - enextfnextself(*searchtet); // Go to the opposite face. - symedgeself(*searchtet); // Enter the adjacent tet. + triface neightet, reftet; + point pa, pb, pc; + REAL angmax, ang; + int types[2], poss[4]; + int pos = 0, i, j; pa = org(*searchtet); angmax = interiorangle(pa, startpt, endpt, NULL); @@ -19488,23 +14997,6 @@ enum tetgenmesh::interresult tetgenmesh::scoutsegment2(face* sseg, 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) { @@ -19516,13 +15008,10 @@ enum tetgenmesh::interresult tetgenmesh::scoutsegment2(face* sseg, // Search intersecting faces along the segment. while (1) { + pd = oppo(*searchtet); + assert(pd != dummypoint); // SELF_CHECK - 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; @@ -19535,12 +15024,12 @@ enum tetgenmesh::interresult tetgenmesh::scoutsegment2(face* sseg, } // Find a face intersecting the segment. - if (dir == INTERFACE) { + if (dir == ACROSSFACE) { // 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]; + neightet = *searchtet; + j = (neightet.ver & 3); // j is the current face number. + for (i = j + 1; i < j + 4; i++) { + neightet.ver = (i % 4); pa = org(neightet); pb = dest(neightet); pc = apex(neightet); @@ -19556,11 +15045,13 @@ enum tetgenmesh::interresult tetgenmesh::scoutsegment2(face* sseg, } assert(dir != DISJOINT); // SELF_CHECK } else { // dir == ACROSSEDGE - // Check the two opposite faces (of the edge) in 'searchtet'. - neightet = *searchtet; - neightet.ver = 0; + // Check the two opposite faces (of the edge) in 'searchtet'. for (i = 0; i < 2; i++) { - neightet.loc = locverpivot[searchtet->loc][searchtet->ver][i]; + if (i == 0) { + enextesym(*searchtet, neightet); + } else { + eprevesym(*searchtet, neightet); + } pa = org(neightet); pb = dest(neightet); pc = apex(neightet); @@ -19575,236 +15066,165 @@ enum tetgenmesh::interresult tetgenmesh::scoutsegment2(face* sseg, } } if (dir == DISJOINT) { - // No intersection. Go to the next tet. - dir = INTEREDGE; - tfnextself(*searchtet); + // No intersection. Rotate to the next tet at the edge. + dir = ACROSSEDGE; + fnextself(*searchtet); continue; } } - if (dir == INTERVERT) { + if (dir == ACROSSVERT) { // 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) { + // break; + return ACROSSVERT; + } else if (dir == ACROSSEDGE) { // Get the edge intersects with the segment. for (i = 0; i < pos; i++) { enextself(neightet); } } // Go to the next tet. - symedge(neightet, *searchtet); + fsym(neightet, *searchtet); - if (dir == INTEREDGE) { + if (dir == ACROSSEDGE) { // 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); + if (issubseg(*searchtet)) { + return ACROSSSEG; + } + } else if (dir == ACROSSFACE) { + if (checksubfaceflag) { + // Check whether a segment and a subface are intersecting. + if (issubface(*searchtet)) { + return ACROSSSUB; + } } - 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; + // A valid reference point should inside the diametrial circumsphere of + // the missing segment, i.e., it encroaches upon it. + if (2.0 * angmax < PI) { + *refpt = NULL; } + *searchtet = reftet; return dir; } /////////////////////////////////////////////////////////////////////////////// // // -// getsegmentsplitpoint() Calculate a split point in the given segment. // +// getsteinerpointonsegment() Get a Steiner point on a segment. // +// // +// Return '1' if 'refpt' lies on an adjacent segment of this segment. Other- // +// wise, return '0'. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::getsegmentsplitpoint2(face* sseg, point refpt, REAL* vt) +int tetgenmesh::getsteinerptonsegment(face* seg, point refpt, point steinpt) { - point ei, ej, ek; - REAL split, L, d, d1, d2, d3; - int stype, sign; - int i; + point ei = sorg(*seg); + point ej = sdest(*seg); + int adjflag = 0, i; - // Decide the type of this segment. - sign = 1; - ei = sorg(*sseg); - ej = sdest(*sseg); - - 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; - } + if (refpt != NULL) { + REAL L, L1, t; + + if (pointtype(refpt) == FREESEGVERTEX) { + face parentseg; + sdecode(point2sh(refpt), parentseg); + int sidx1 = getfacetindex(parentseg); + point far_pi = segmentendpointslist[sidx1 * 2]; + point far_pj = segmentendpointslist[sidx1 * 2 + 1]; + int sidx2 = getfacetindex(*seg); + point far_ei = segmentendpointslist[sidx2 * 2]; + point far_ej = segmentendpointslist[sidx2 * 2 + 1]; + if ((far_pi == far_ei) || (far_pj == far_ei)) { + // Create a Steiner point at the intersection of the segment + // [far_ei, far_ej] and the sphere centered at far_ei with + // radius |far_ei - refpt|. + L = distance(far_ei, far_ej); + L1 = distance(far_ei, refpt); + t = L1 / L; + for (i = 0; i < 3; i++) { + steinpt[i] = far_ei[i] + t * (far_ej[i] - far_ei[i]); } - } - } 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; - } - } + adjflag = 1; + } else if ((far_pi == far_ej) || (far_pj == far_ej)) { + L = distance(far_ei, far_ej); + L1 = distance(far_ej, refpt); + t = L1 / L; + for (i = 0; i < 3; i++) { + steinpt[i] = far_ej[i] + t * (far_ei[i] - far_ej[i]); } - } - } - } - - // 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)); - } - 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; + adjflag = 1; } else { - split = 0.5; - } - for (i = 0; i < 3; i++) { - vt[i] = ei[i] + split * (ej[i] - ei[i]); + // Cut the segment by the projection point of refpt. + projpt2edge(refpt, ei, ej, steinpt); } } 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; - } + // Cut the segment by the projection point of refpt. + projpt2edge(refpt, ei, ej, steinpt); + } + + // Make sure that steinpt is not too close to ei and ej. + L = distance(ei, ej); + L1 = distance(steinpt, ei); + t = L1 / L; + if ((t < 0.2) || (t > 0.8)) { + // Split the point at the middle. for (i = 0; i < 3; i++) { - vt[i] = ej[i] + split * (ei[i] - ej[i]); + steinpt[i] = ei[i] + 0.5 * (ej[i] - ei[i]); } } - r1count++; } else { - // Use rule-2. - ek = getsubsegfarorg(sseg); - L = DIST(ek, ej); - d = DIST(ek, refpt); - split = d / L; + // Split the point at the middle. 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; - } - for (i = 0; i < 3; i++) { - vt[i] = ek[i] + split * (ej[i] - ek[i]); - } + steinpt[i] = ei[i] + 0.5 * (ej[i] - ei[i]); } - d1 > d2 ? r3count++ : r2count++; } - if (b->verbose > 1) { - printf(" split (%g), vt (%g, %g, %g).\n", split, vt[0], vt[1], vt[2]); - } + + return adjflag; } + + /////////////////////////////////////////////////////////////////////////////// // // -// delaunizesegments() Recover segments in a Delaunay tetrahedralization. // +// delaunizesegments() Recover segments in a DT. // +// // +// All segments need to be recovered are in 'subsegstack' (Q). They will be // +// be recovered one by one (in a random order). // +// // +// Given a segment s in the Q, this routine first queries s in the DT, if s // +// matches an edge in DT, it is 'locked' at the edge. Otherwise, s is split // +// by inserting a new point p in both the DT and itself. The two new subseg- // +// ments of s are queued in Q. The process continues until Q is empty. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::delaunizesegments2() +void tetgenmesh::delaunizesegments() { - triface searchtet; - face splitsh; - face *psseg, sseg; // *parysh; + triface searchtet, spintet; + face searchsh; + face sseg, *psseg; point refpt, newpt; enum interresult dir; - bool visflag; + insertvertexflags ivf; + int t1ver; - if (b->verbose) { - printf(" Delaunizing segments.\n"); - } + + ivf.bowywat = 1; // Use Bowyer-Watson insertion. + ivf.assignmeshsize = b->metric; + ivf.sloc = (int) ONEDGE; // on 'sseg'. + ivf.sbowywat = 1; // Use Bowyer-Watson insertion. // Loop until 'subsegstack' is empty. while (subsegstack->objects > 0l) { @@ -19813,1338 +15233,855 @@ void tetgenmesh::delaunizesegments2() psseg = (face *) fastlookup(subsegstack, subsegstack->objects); sseg = *psseg; - if (!sinfected(sseg)) continue; // Not a missing segment. - suninfect(sseg); + // Check if this segment has been recovered. + sstpivot1(sseg, searchtet); + if (searchtet.tet != NULL) { + continue; // Not a missing segment. + } - // 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); + // Search the segment. + dir = scoutsegment(sorg(sseg), sdest(sseg), &searchtet, &refpt, NULL); + + if (dir == SHAREEDGE) { + // Found this segment, insert it. + if (!issubseg(searchtet)) { + // Let the segment remember an adjacent tet. + sstbond1(sseg, searchtet); + // Bond the segment to all tets containing it. + spintet = searchtet; + do { + tssbond1(spintet, sseg); + fnextself(spintet); + } while (spintet.tet != searchtet.tet); } else { - /*if (getpointtype(refpt) != ACUTEVERTEX) { - setpointtype(refpt, RIDGEVERTEX); + // Collision! Maybe a bug. + assert(0); + } + } else { + if ((dir == ACROSSFACE) || (dir == ACROSSEDGE)) { + // The segment is missing. Split it. + // Create a new point. + makepoint(&newpt, FREESEGVERTEX); + //setpointtype(newpt, FREESEGVERTEX); + getsteinerptonsegment(&sseg, refpt, newpt); + + // Start searching from 'searchtet'. + ivf.iloc = (int) OUTSIDE; + // Insert the new point into the tetrahedralization T. + // Missing segments and subfaces are queued for recovery. + // Note that T is convex (nonconvex = 0). + if (insertpoint(newpt, &searchtet, &searchsh, &sseg, &ivf)) { + // The new point has been inserted. + st_segref_count++; + if (steinerleft > 0) steinerleft--; + } else { + assert (ivf.iloc == (enum locateresult) NEARVERTEX); + terminatetetgen(this, 4); } - // 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); + } else { + // Indicate it is an input problem. + terminatetetgen(this, 3); } } - } - - if (b->verbose) { - printf(" %ld protecting points.\n", r1count + r2count + r3count); - } -} + } // while +} /////////////////////////////////////////////////////////////////////////////// // // -// 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. // +// scoutsubface() Search subface in the tetrahedralization. // // // -// 'convexflag' indicates the current mesh is convex (1) or non-convex (0). // +// 'searchsh' is searched in T. If it exists, it is 'locked' at the face in // +// T. 'searchtet' refers to the face. Otherwise, it is missing. // // // -// 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'. // +// The return value indicates one of the following cases: // +// - SHAREFACE, 'searchsh' exists and is inserted in T. // +// - COLLISIONFACE, 'searchsh' exists in T, but it conflicts with another // +// subface which was inserted earlier. It is not inserted. // // // /////////////////////////////////////////////////////////////////////////////// -enum tetgenmesh::interresult tetgenmesh::scoutsubface(face* pssub, - triface* searchtet, int convexflag) +enum tetgenmesh::interresult + tetgenmesh::scoutsubface(face* searchsh, triface* searchtet) { triface spintet; - face checksh; - point pa, pb, pc, pd; + point pa, pb, pc; enum interresult dir; - int hitbdry; - int i; - - 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'. - } - } else { - // 'searchtet' holds the current edge of 'pssub'. - pa = org(*searchtet); - pb = dest(*searchtet); - } + int t1ver; - pc = sapex(*pssub); + pa = sorg(*searchsh); + pb = sdest(*searchsh); - 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). + // Get a tet whose origin is a. + point2tetorg(pa, *searchtet); + // Search the edge [a,b]. + dir = finddirection(searchtet, pb); + if (dir == ACROSSVERT) { + // Check validity of a PLC. + if (dest(*searchtet) != pb) { + // A vertex lies on the search edge. + enextself(*searchtet); + // It is possible a PLC self-intersection problem. + terminatetetgen(this, 3); + return TOUCHEDGE; + } + // The edge exists. Check if the face exists. + pc = sapex(*searchsh); + // Searchtet holds edge [a,b]. Search a face with apex c. + spintet = *searchtet; + while (1) { + if (apex(spintet) == pc) { + // Found a face matching to 'searchsh'! + if (!issubface(spintet)) { + // Insert 'searchsh'. + tsbond(spintet, *searchsh); + fsymself(spintet); + sesymself(*searchsh); + tsbond(spintet, *searchsh); + *searchtet = spintet; return SHAREFACE; + } else { + // Another subface is already inserted. + face checksh; + tspivot(spintet, checksh); + assert(checksh.sh != searchsh->sh); // SELF_CHECK + // This is possibly an input problem, i.e., two facets overlap. + // Report this problem and exit. + printf("Warning: Found two facets nearly overlap.\n"); + terminatetetgen(this, 5); + // unifysubfaces(&checksh, searchsh); + *searchtet = spintet; + return COLLISIONFACE; } } + fnextself(spintet); + if (spintet.tet == searchtet->tet) break; } - if (!fnextself(spintet)) { - hitbdry++; - if (hitbdry == 2) break; - esym(*searchtet, spintet); - if (!fnextself(spintet)) break; - } - pd = apex(spintet); - if (pd == apex(*searchtet)) break; } - return INTERTET; + // dir is either ACROSSEDGE or ACROSSFACE. + return dir; } /////////////////////////////////////////////////////////////////////////////// // // -// scoutcrosstet() Scout a tetrahedron across a facet. // -// // -// 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. // +// formregion() Form the missing region of a missing subface. // // // -// 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. // +// 'missh' is a missing subface. From it we form a missing region R which is // +// a connected region formed by a set of missing subfaces of a facet. // +// Comment: There should be no segment inside R. // // // -// 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. // +// 'missingshs' returns the list of subfaces in R. All subfaces in this list // +// are oriented as the 'missh'. 'missingshbds' returns the list of boundary // +// edges (tetrahedral handles) of R. 'missingshverts' returns all vertices // +// of R. They are all pmarktested. // // // +// Except the first one (which is 'missh') in 'missingshs', each subface in // +// this list represents an internal edge of R, i.e., it is missing in the // +// tetrahedralization. Since R may contain interior vertices, not all miss- // +// ing edges can be found by this way. // /////////////////////////////////////////////////////////////////////////////// -enum tetgenmesh::interresult tetgenmesh::scoutcrosstet(face *pssub, - triface* searchtet, arraypool* facpoints) +void tetgenmesh::formregion(face* missh, arraypool* missingshs, + arraypool* missingshbds, arraypool* missingshverts) { - 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; - - if (facpoints != NULL) { - // Infect all vertices of the facet. - for (i = 0; i < (int) facpoints->objects; i++) { - pd = * (point *) fastlookup(facpoints, i); - pinfect(pd); - } - } - - // Search an edge crossing the facet containing abc. - if (searchtet->ver & 01) { - esymself(*searchtet); // Adjust to 0th edge ring. - sesymself(*pssub); - } - - pa = sorg(*pssub); - pb = sdest(*pssub); - pc = sapex(*pssub); + triface searchtet, spintet; + face neighsh, *parysh; + face neighseg, fakeseg; + point pa, pb, *parypt; + enum interresult dir; + int t1ver; + int i, j; - // 'searchtet' refers to edge pa->pb. - assert(org(*searchtet) == pa); - assert(dest(*searchtet) == pb); + smarktest(*missh); + missingshs->newindex((void **) &parysh); + *parysh = *missh; - // 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. + // Incrementally find other missing subfaces. + for (i = 0; i < missingshs->objects; i++) { + missh = (face *) fastlookup(missingshs, i); + for (j = 0; j < 3; j++) { + pa = sorg(*missh); + pb = sdest(*missh); + point2tetorg(pa, searchtet); + dir = finddirection(&searchtet, pb); + if (dir != ACROSSVERT) { + // This edge is missing. Its neighbor is a missing subface. + spivot(*missh, neighsh); + if (!smarktested(neighsh)) { + // Adjust the face orientation. + if (sorg(neighsh) != pb) sesymself(neighsh); + smarktest(neighsh); + missingshs->newindex((void **) &parysh); + *parysh = neighsh; + } + } else { + if (dest(searchtet) != pb) { + // This might be a self-intersection problem. + terminatetetgen(this, 3); + } } - esym(*searchtet, spintet); - if (!fnextself(spintet)) { - cofacetflag = true; break; // Not found. + // Collect the vertices of R. + if (!pmarktested(pa)) { + pmarktest(pa); + missingshverts->newindex((void **) &parypt); + *parypt = pa; } - } - pd = apex(spintet); - if (pd == apex(*searchtet)) { - cofacetflag = true; break; // Not found. - } - } + senextself(*missh); + } // j + } // i - 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; - } - } - } 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); + // Get the boundary edges of R. + for (i = 0; i < missingshs->objects; i++) { + missh = (face *) fastlookup(missingshs, i); + for (j = 0; j < 3; j++) { + spivot(*missh, neighsh); + if ((neighsh.sh == NULL) || !smarktested(neighsh)) { + // A boundary edge of R. + // Let the segment point to the adjacent tet. + point2tetorg(sorg(*missh), searchtet); + finddirection(&searchtet, sdest(*missh)); + missingshbds->newindex((void **) &parysh); + *parysh = *missh; + // Check if this edge is a segment. + sspivot(*missh, neighseg); + if (neighseg.sh == NULL) { + // Temporarily create a segment at this edge. + makeshellface(subsegs, &fakeseg); + setsorg(fakeseg, sorg(*missh)); + setsdest(fakeseg, sdest(*missh)); + sinfect(fakeseg); // Mark it as faked. + // Connect it to all tets at this edge. + spintet = searchtet; + while (1) { + tssbond1(spintet, fakeseg); + fnextself(spintet); + if (spintet.tet == searchtet.tet) break; } + neighseg = fakeseg; } + // Let the segment and the boundary edge point to each other. + ssbond(*missh, neighseg); + sstbond1(neighseg, searchtet); } - // 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; - } + senextself(*missh); + } // j + } // i - 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'. + + // Unmarktest collected missing subfaces. + for (i = 0; i < missingshs->objects; i++) { + parysh = (face *) fastlookup(missingshs, i); + sunmarktest(*parysh); } } /////////////////////////////////////////////////////////////////////////////// // // -// recoversubfacebyflips() Recover a subface by flips in the surface mesh. // +// scoutcrossedge() Search an edge that crosses the missing region. // // // -// 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. // +// Return 1 if a crossing edge is found. It is returned by 'crosstet'. More- // +// over, the edge is oriented such that its origin lies below R. Return 0 // +// if no such edge is found. // // // -// 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. // +// Assumption: All vertices of the missing region are marktested. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::recoversubfacebyflips(face* pssub, triface* crossface, - arraypool *facfaces) +int tetgenmesh::scoutcrossedge(triface& crosstet, arraypool* missingshbds, + arraypool* missingshs) { - triface neightet; - face flipfaces[2], *parysh; - face checkseg; + triface searchtet, spintet; + face *parysh; + face neighseg; 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; + enum interresult dir; + REAL ori; + int types[2], poss[4]; + int searchflag, interflag; + int t1ver; + int i, j; - 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]; - } + searchflag = 0; + for (j = 0; j < missingshbds->objects && !searchflag; j++) { + parysh = (face *) fastlookup(missingshbds, j); + sspivot(*parysh, neighseg); + sstpivot1(neighseg, searchtet); + interflag = 0; + // Let 'spintet' be [#,#,d,e] where [#,#] is the boundary edge of R. + spintet = searchtet; 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; + pd = apex(spintet); + pe = oppo(spintet); + // Skip a hull edge. + if ((pd != dummypoint) && (pe != dummypoint)) { + // Skip an edge containing a vertex of R. + if (!pmarktested(pd) && !pmarktested(pe)) { + // Check if [d,e] intersects R. + for (i = 0; i < missingshs->objects && !interflag; i++) { + parysh = (face *) fastlookup(missingshs, i); + pa = sorg(*parysh); + pb = sdest(*parysh); + pc = sapex(*parysh); + interflag=tri_edge_test(pa, pb, pc, pd, pe, NULL, 1, types, poss); + if (interflag > 0) { + if (interflag == 2) { + // They intersect at a single point. + dir = (enum interresult) types[0]; + if ((dir == ACROSSFACE) || (dir == ACROSSEDGE)) { + //pos = poss[0]; + // Go to the crossing edge [d,e,#,#]. + edestoppo(spintet, crosstet); // // [d,e,#,#]. + // Check if it is a segment. + if (issubseg(crosstet)) { + //face checkseg; + //tsspivot1(crosstet, checkseg); + //reportselfintersect(&checkseg, parysh); + terminatetetgen(this, 3); + } + // Adjust the edge such that d lies below [a,b,c]. + ori = orient3d(pa, pb, pc, pd); + assert(ori != 0); + if (ori < 0) { + esymself(crosstet); + } + searchflag = 1; + } + } + break; + } // if (interflag > 0) + } + } } - break; - } - } + // Leave search at this bdry edge if an intersection is found. + if (interflag > 0) break; + // Go to the next tetrahedron. + fnextself(spintet); + if (spintet.tet == searchtet.tet) break; + } // while (1) + } // j - if (pe == dummypoint) { - pe[0] = pe[1] = pe[2] = 0; - } + return searchflag; } /////////////////////////////////////////////////////////////////////////////// // // // 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. // +// The missing region R is formed by a set of missing subfaces 'missingshs'. // +// In the following, we assume R is horizontal and oriented. (All subfaces // +// of R are oriented in the same way.) 'searchtet' is a tetrahedron [d,e,#, // +// #] which intersects R in its interior, where the edge [d,e] intersects R, // +// and d lies below 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). // +// 'crosstets' returns the set of crossing tets. Every tet in it has the // +// form [d,e,#,#] where [d,e] is a crossing edge, and d lies below R. The // +// set of tets form the cavity C, which is divided into two parts by R, one // +// 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. // // // -// 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. // +// Important: This routine assumes all vertices of the facet containing this // +// subface are marked, i.e., pmarktested(p) returns true. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::formcavity(face *pssub, arraypool* crosstets, - arraypool* topfaces, arraypool* botfaces, arraypool* toppoints, - arraypool* botpoints, arraypool* facpoints, arraypool* facfaces) +bool tetgenmesh::formcavity(triface* searchtet, arraypool* missingshs, + arraypool* crosstets, arraypool* topfaces, + arraypool* botfaces, arraypool* toppoints, + arraypool* botpoints) { 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; - - // For triangle-edge test. - enum interresult dir; + triface spintet, neightet, *parytet; + face *parysh = NULL; + point pa, pd, pe, *parypt; + enum interresult dir; + bool testflag, invalidflag; int types[2], poss[4]; + int t1ver; + int i, j, k; - // 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. + // Temporarily re-use 'topfaces' for all crossing edges. crossedges = topfaces; + + if (b->verbose > 2) { + printf(" Form the cavity of a missing region.\n"); + } + // Mark this edge to avoid testing it later. + markedge(*searchtet); crossedges->newindex((void **) &parytet); - *parytet = crosstet; + *parytet = *searchtet; + + invalidflag = 0; // 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; + // form [d,e,#,#], where [d,e] is a crossing edge, d lies below R. + // NEITHER d NOR e is a vertex of R (!pmarktested). + for (i = 0; i < crossedges->objects; i++) { + // Get a crossing edge [d,e,#,#]. + searchtet = (triface *) fastlookup(crossedges, i); + + // Sort vertices into the bottom and top arrays. + pd = org(*searchtet); + if (!pinfected(pd)) { + pinfect(pd); + botpoints->newindex((void **) &parypt); + *parypt = pd; + } + pe = dest(*searchtet); + if (!pinfected(pe)) { + pinfect(pe); + toppoints->newindex((void **) &parypt); + *parypt = pe; + } + + // All tets sharing this edge are crossing tets. + spintet = *searchtet; + while (1) { + if (!infected(spintet)) { + infect(spintet); + crosstets->newindex((void **) &parytet); + *parytet = spintet; } - // 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)) { + // Go to the next crossing tet. + fnextself(spintet); + if (spintet.tet == searchtet->tet) break; + } // while (1) + + // Detect new crossing edges. + spintet = *searchtet; + while (1) { + // spintet is [d,e,a,#], where d lies below R, and e lies above R. + pa = apex(spintet); + if (pa != dummypoint) { + if (!pmarktested(pa)) { + // There exists a crossing edge, either [e,a] or [a,d]. First check + // if the crossing edge has already be added, i.e., check if a + // tetrahedron at this edge is marked. + testflag = true; + for (j = 0; j < 2 && testflag; j++) { + if (j == 0) { + enext(spintet, neightet); + } else { + eprev(spintet, neightet); + } + while (1) { + if (edgemarked(neightet)) { + // This crossing edge has already been tested. Skip it. + testflag = false; + break; + } + fnextself(neightet); + if (neightet.tet == spintet.tet) break; + } + } // j + if (testflag) { + // Test if [e,a] or [a,d] intersects R. + // Do a brute-force search in the set of subfaces of R. Slow! + // Need to be improved! + pd = org(spintet); + pe = dest(spintet); + for (k = 0; k < missingshs->objects; k++) { + parysh = (face *) fastlookup(missingshs, k); + if (tri_edge_test(sorg(*parysh), sdest(*parysh), sapex(*parysh), + pe, pa, NULL, 1, types, poss)) { + // Found intersection. 'a' lies below R. + enext(spintet, neightet); 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; + if ((dir == ACROSSFACE) || (dir == ACROSSEDGE)) { + // A valid intersection. + } else { + // A non-valid intersection. Maybe a PLC problem. + invalidflag = 1; } + break; } - // Check if pe->pf crosses the facet. - if (tri_edge_test(pa, pb, pc, pe, pf, NULL, 1, types, poss)) { + if (tri_edge_test(sorg(*parysh), sdest(*parysh), sapex(*parysh), + pa, pd, NULL, 1, types, poss)) { + // Found intersection. 'a' lies above R. + eprev(spintet, neightet); dir = (enum interresult) types[0]; - 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; + if ((dir == ACROSSFACE) || (dir == ACROSSEDGE)) { + // A valid intersection. + } else { + // A non-valid intersection. Maybe a PLC problem. + invalidflag = 1; + } + break; + } + } // k + if (k < missingshs->objects) { + // Found a pair of triangle - edge intersection. + if (invalidflag) { + if (!b->quiet) { + printf("Warning: A non-valid facet - edge intersection\n"); + printf(" subface: (%d, %d, %d) edge: (%d, %d)\n", + pointmark(sorg(*parysh)), pointmark(sdest(*parysh)), + pointmark(sapex(*parysh)), pointmark(org(neightet)), + pointmark(dest(neightet))); } + // It may be a PLC problem. + terminatetetgen(this, 3); } - } - // 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. + // Adjust the edge direction, so that its origin lies below R, + // and its destination lies above R. + esymself(neightet); + // Check if this edge is a segment. + if (issubseg(neightet)) { + // Invalid PLC! + //face checkseg; + //tsspivot1(neightet, checkseg); + //reportselfintersect(&checkseg, parysh); + terminatetetgen(this, 3); + } + // Mark this edge to avoid testing it again. + markedge(neightet); crossedges->newindex((void **) &parytet); - *parytet = neightet; - } - } - // } - tfnextself(spintet); - if (spintet.tet == dummytet) { - // Encounter a boundary face. - assert(0); // Not handled yet. - } - if (apex(spintet) == pg) break; - } - } - } + *parytet = neightet; + } else { + // No intersection is found. It may be a PLC problem. + invalidflag = 1; + // Split the subface intersecting [d,e]. + for (k = 0; k < missingshs->objects; k++) { + parysh = (face *) fastlookup(missingshs, k); + // Test if this face intersects [e,a]. + if (tri_edge_test(sorg(*parysh),sdest(*parysh),sapex(*parysh), + pd, pe, NULL, 1, types, poss)) { + break; + } + } // k + if (k == missingshs->objects) { + // Not found such an edge. + // Arbitrarily choose an edge (except the first) to split. + k = randomnation(missingshs->objects - 1); + parysh = (face *) fastlookup(missingshs, k + 1); + } + recentsh = *parysh; + recenttet = spintet; // For point location. + break; // the while (1) loop + } // if (k == missingshs->objects) + } // if (testflag) + } // if (!pmarktested(pa) || b->psc) + } // if (pa != dummypoint) + // Go to the next crossing tet. + fnextself(spintet); + if (spintet.tet == searchtet->tet) break; + } // while (1) + + //if (b->psc) { + if (invalidflag) break; + //} + } // i - // Unmark all facet vertices. - for (i = 0; i < (int) facpoints->objects; i++) { - ppt = (point *) fastlookup(facpoints, i); - puninfect(*ppt); - } - - // 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. - - // 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; - } - } + if (b->verbose > 2) { + printf(" Formed cavity: %ld (%ld) cross tets (edges).\n", + crosstets->objects, crossedges->objects); } - if (b->verbose > 1) { - printf(" Formed cavity: %ld (%ld) cross tets (edges).\n", - crosstets->objects, crossedges->objects); + // Unmark all marked edges. + for (i = 0; i < crossedges->objects; i++) { + searchtet = (triface *) fastlookup(crossedges, i); + assert(edgemarked(*searchtet)); // SELF_CHECK + unmarkedge(*searchtet); } 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; - } - } else { - continue; // Go to the next cross tet. + + if (invalidflag) { + // Unmark all collected tets. + for (i = 0; i < crosstets->objects; i++) { + searchtet = (triface *) fastlookup(crosstets, i); + uninfect(*searchtet); } - 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; - } - } else { - continue; + // Unmark all collected vertices. + for (i = 0; i < botpoints->objects; i++) { + parypt = (point *) fastlookup(botpoints, i); + puninfect(*parypt); } - break; + for (i = 0; i < toppoints->objects; i++) { + parypt = (point *) fastlookup(toppoints, i); + puninfect(*parypt); + } + crosstets->restart(); + botpoints->restart(); + toppoints->restart(); + + // Randomly split an interior edge of R. + i = randomnation(missingshs->objects - 1); + recentsh = * (face *) fastlookup(missingshs, i); + return false; } - 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)) { + // and bottom vertices have been infected. Uninfected vertices must be + // middle vertices (i.e., the vertices of R). + // NOTE 1: Hull tets may be collected. Process them as a normal one. + // NOTE 2: Some previously recovered subfaces may be completely inside the + // cavity. In such case, we remove these subfaces from the cavity and put + // them into 'subfacstack'. They will be recovered later. + // NOTE 3: Some segments may be completely inside the cavity, e.g., they + // attached to a subface which is inside the cavity. Such segments are + // put in 'subsegstack'. They will be recovered later. + // NOTE4 : The interior subfaces and segments mentioned in NOTE 2 and 3 + // are identified in the routine "carvecavity()". + + for (i = 0; i < crosstets->objects; i++) { + searchtet = (triface *) fastlookup(crosstets, i); + // searchtet is [d,e,a,b]. + eorgoppo(*searchtet, spintet); + fsym(spintet, neightet); // neightet is [a,b,e,#] + if (!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; - } - } 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; - } - } + *parytet = neightet; } - enext2fnext(crosstet, spintet); - enext2self(spintet); - symedge(spintet, neightet); - // if (!infected(neightet)) { - if ((neightet.tet == dummytet) || !infected(neightet)) { + edestoppo(*searchtet, spintet); + fsym(spintet, neightet); // neightet is [b,a,d,#] + if (!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; - } else { - *parytet = neightet; - } - } 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; - } - } + *parytet = neightet; } // 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; - // } + pa = org(neightet); + if (!pinfected(pa)) { + if (pa != dummypoint) { + pinfect(pa); + botpoints->newindex((void **) &parypt); + *parypt = pa; + toppoints->newindex((void **) &parypt); + *parypt = pa; + } + } + pa = dest(neightet); + if (!pinfected(pa)) { + if (pa != dummypoint) { + pinfect(pa); + botpoints->newindex((void **) &parypt); + *parypt = pa; + toppoints->newindex((void **) &parypt); + *parypt = pa; + } } - } + } // i - // Unmark all collected top, bottom, and middle vertices. - for (i = 0; i < (int) toppoints->objects; i++) { - ppt = (point *) fastlookup(toppoints, i); - puninfect(*ppt); + // Uninfect all collected top, bottom, and middle vertices. + for (i = 0; i < toppoints->objects; i++) { + parypt = (point *) fastlookup(toppoints, i); + puninfect(*parypt); } - for (i = 0; i < (int) botpoints->objects; i++) { - ppt = (point *) fastlookup(botpoints, i); - puninfect(*ppt); + for (i = 0; i < botpoints->objects; i++) { + parypt = (point *) fastlookup(botpoints, i); + puninfect(*parypt); } - // Comments: Now no vertex is marked. + cavitycount++; + + return true; } /////////////////////////////////////////////////////////////////////////////// // // // 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(). // +// The cavity C to be tetrahedralized is the top or bottom part of a whole // +// cavity. 'cavfaces' contains the boundary faces of C. NOTE: faces in 'cav- // +// faces' do not form a closed polyhedron. The "open" side are subfaces of // +// the missing facet. These faces will be recovered later in fillcavity(). // +// // +// This routine first constructs the DT of the vertices. Then it identifies // +// the half boundary faces of the cavity in DT. Possiblely the cavity C will // +// be enlarged. // // // -// 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'. // // // /////////////////////////////////////////////////////////////////////////////// -bool tetgenmesh::delaunizecavity(arraypool *cavpoints, arraypool *cavfaces, - arraypool *cavshells, arraypool *newtets, arraypool *crosstets, - arraypool *misfaces) +void tetgenmesh::delaunizecavity(arraypool *cavpoints, arraypool *cavfaces, + arraypool *cavshells, arraypool *newtets, + arraypool *crosstets, arraypool *misfaces) { - triface *parytet, searchtet, neightet, spintet, *parytet1; - triface newtet, faketet; - face checksh, tmpsh, *parysh; - face checkseg; + triface searchtet, neightet, *parytet, *parytet1; + face tmpsh, *parysh; point pa, pb, pc, pd, pt[3], *parypt; - // badface *newflipface; enum interresult dir; + insertvertexflags ivf; REAL ori; - // int miscount; - int i, j, k; + long baknum, bakhullsize; + int bakchecksubsegflag, bakchecksubfaceflag; + int t1ver; + int i, j; - if (b->verbose > 1) { - printf(" Delaunizing cavity: %ld points, %ld faces.\n", - cavpoints->objects, cavfaces->objects); + if (b->verbose > 2) { + printf(" Delaunizing cavity: %ld points, %ld faces.\n", + cavpoints->objects, cavfaces->objects); } + // Remember the current number of crossing tets. It may be enlarged later. + baknum = crosstets->objects; + bakhullsize = hullsize; + bakchecksubsegflag = checksubsegflag; + bakchecksubfaceflag = checksubfaceflag; + hullsize = 0l; + checksubsegflag = 0; + checksubfaceflag = 0; + b->verbose--; // Suppress informations for creating Delaunay tetra. + b->plc = 0; // Do not check near vertices. + + ivf.bowywat = 1; // Use Bowyer-Watson algorithm. // 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); + pa = pb = pc = NULL; + for (i = 0; i < cavfaces->objects; i++) { + parytet = (triface *) fastlookup(cavfaces, i); + parytet->ver = epivot[parytet->ver]; + if (apex(*parytet) != dummypoint) { + pa = org(*parytet); + pb = dest(*parytet); + pc = apex(*parytet); + break; + } + } pd = NULL; - for (i = 1; i < (int) cavfaces->objects; i++) { + for (; i < cavfaces->objects; i++) { parytet = (triface *) fastlookup(cavfaces, i); pt[0] = org(*parytet); pt[1] = dest(*parytet); pt[2] = apex(*parytet); for (j = 0; j < 3; j++) { - // if (pt[j] != dummypoint) { // Do not include a hull point. - if (!pinfected(pt[j])) { - ori = orient3d(pa, pb, pc, pt[j]); - if (ori != 0) { - pd = pt[j]; - if (ori > 0) { // Swap pa and pb. - pt[j] = pa; pa = pb; pb = pt[j]; - } - break; + if (pt[j] != dummypoint) { // Do not include a hull point. + ori = orient3d(pa, pb, pc, pt[j]); + if (ori != 0) { + pd = pt[j]; + if (ori > 0) { // Swap pa and pb. + pt[j] = pa; pa = pb; pb = pt[j]; } + break; } - // } + } } if (pd != NULL) break; } - assert(i < (int) cavfaces->objects); // SELF_CHECK - pinfect(pd); + assert(i < cavfaces->objects); // SELF_CHECK // 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; + initialdelaunay(pa, pb, pc, pd); - for (i = 0; i < (int) cavpoints->objects; i++) { + // Incrementally insert the vertices (duplicated vertices are ignored). + for (i = 0; i < cavpoints->objects; i++) { pt[0] = * (point *) fastlookup(cavpoints, i); - assert(pt[0] != dummypoint); // SELF_CHECK - if (!pinfected(pt[0])) { - searchtet = recenttet; - insertvertexbw(pt[0], &searchtet, true, false, false, false); - } else { - puninfect(pt[0]); // It is already inserted. - } + searchtet = recenttet; + ivf.iloc = (int) OUTSIDE; + insertpoint(pt[0], &searchtet, NULL, NULL, &ivf); + } + + if (b->verbose > 2) { + printf(" Identifying %ld boundary faces of the cavity.\n", + cavfaces->objects); } - // Comment: All vertices of the cavity are NOT marked. while (1) { - // 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); + // Identify boundary faces. Mark interior tets. Save missing faces. + for (i = 0; i < cavfaces->objects; i++) { + parytet = (triface *) fastlookup(cavfaces, i); + // Skip an interior face (due to the enlargement of the cavity). + if (infected(*parytet)) continue; + parytet->ver = epivot[parytet->ver]; + 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]); + // Insert tmpsh in DT. + searchtet.tet = NULL; + dir = scoutsubface(&tmpsh, &searchtet); + if (dir == SHAREFACE) { + // Inserted! 'tmpsh' must face toward the inside of the cavity. + // Remember the boundary tet (outside the cavity) in tmpsh + // (use the adjacent tet slot). + tmpsh.sh[0] = (shellface) encode(*parytet); + // Save this subface. + cavshells->newindex((void **) &parysh); + *parysh = tmpsh; + } + else { + // This boundary face is missing. + shellfacedealloc(subfaces, tmpsh.sh); + // Save this face in list. + misfaces->newindex((void **) &parytet1); + *parytet1 = *parytet; } - 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; - } + } // i - 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(); + if (misfaces->objects > 0) { + if (b->verbose > 2) { + printf(" Enlarging the cavity. %ld missing bdry faces\n", + misfaces->objects); + } - // 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. - } + // Removing all temporary subfaces. + for (i = 0; i < cavshells->objects; i++) { + parysh = (face *) fastlookup(cavshells, i); + stpivot(*parysh, neightet); + tsdissolve(neightet); // Detach it from adj. tets. + fsymself(neightet); + tsdissolve(neightet); + shellfacedealloc(subfaces, parysh->sh); + } + cavshells->restart(); - // 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))); - } - 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 { + // Infect the points which are of the cavity. + for (i = 0; i < cavpoints->objects; i++) { + pt[0] = * (point *) fastlookup(cavpoints, i); + pinfect(pt[0]); // Mark it as inserted. + } + + // Enlarge the cavity. + for (i = 0; i < misfaces->objects; i++) { + // Get a missing face. + parytet = (triface *) fastlookup(misfaces, i); + if (!infected(*parytet)) { + // 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)) { + searchtet = recenttet; + ivf.iloc = (int) OUTSIDE; + insertpoint(pd, &searchtet, NULL, NULL, &ivf); + pinfect(pd); + cavpoints->newindex((void **) &parypt); + *parypt = pd; + } + // Add three opposite faces into the boundary list. + for (j = 0; j < 3; j++) { + esym(*parytet, neightet); + fsymself(neightet); + if (!infected(neightet)) { + cavfaces->newindex((void **) &parytet1); *parytet1 = neightet; - } - } 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))); - } - stdissolve(checksh); - sesymself(checksh); - stdissolve(checksh); - subfacstack->newindex((void **) &parysh); - *parysh = checksh; - } - } - enextself(*parytet); - } // j - } // if (!infected(parytet)) - } + } + enextself(*parytet); + } // j + } // if (!infected(parytet)) + } // i - // 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]); - } + // Uninfect the points which are of the cavity. + for (i = 0; i < cavpoints->objects; i++) { + pt[0] = * (point *) fastlookup(cavpoints, i); + puninfect(pt[0]); + } - misfaces->restart(); - cavityexpcount++; - continue; - } + misfaces->restart(); + continue; + } // if (misfaces->objects > 0) - break; + break; } // while (1) @@ -21152,29 +16089,32 @@ bool tetgenmesh::delaunizecavity(arraypool *cavpoints, arraypool *cavfaces, marktest(recenttet); newtets->newindex((void **) &parytet); *parytet = recenttet; - for (i = 0; i < (int) newtets->objects; i++) { + for (i = 0; i < 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; - } + for (j = 0; j < 4; j++) { + decode(searchtet.tet[j], neightet); + if (!marktested(neightet)) { + marktest(neightet); + newtets->newindex((void **) &parytet); + *parytet = neightet; } } } cavpoints->restart(); - // Comment: Now no vertex is marked. cavfaces->restart(); - if (cavshells->objects > (long) maxcavsize) { - maxcavsize = cavshells->objects; + if (crosstets->objects > baknum) { + // The cavity has been enlarged. + cavityexpcount++; } - return true; + // Restore the original values. + hullsize = bakhullsize; + checksubsegflag = bakchecksubsegflag; + checksubfaceflag = bakchecksubfaceflag; + b->verbose++; + b->plc = 1; } /////////////////////////////////////////////////////////////////////////////// @@ -21185,369 +16125,391 @@ bool tetgenmesh::delaunizecavity(arraypool *cavpoints, arraypool *cavfaces, // 'topfaces' and 'botfaces' are the boundaries of these two sets, respect- // // ively. 'midfaces' is empty on input, and will store faces in the facet. // // // +// Important: This routine assumes all vertices of the missing region R are // +// marktested, i.e., pmarktested(p) returns true. // +// // /////////////////////////////////////////////////////////////////////////////// bool tetgenmesh::fillcavity(arraypool* topshells, arraypool* botshells, - arraypool* midfaces, arraypool* facpoints) + arraypool* midfaces, arraypool* missingshs, + arraypool* topnewtets, arraypool* botnewtets, + triface* crossedge) { arraypool *cavshells; - triface *parytet, bdrytet, toptet, bottet, neightet, midface, spintet; - face checksh, *parysh; + triface bdrytet, neightet, *parytet; + triface searchtet, spintet; + face *parysh; face checkseg; - point pa, pb, pc, pf, pg; - REAL ori, len, n[3]; - bool mflag, bflag; - int i, j, k; + point pa, pb, pc; + bool mflag; + int t1ver; + int i, j; - // Connect newtets to tets outside the cavity. - for (k = 0; k < 2; k++) { - cavshells = (k == 0 ? topshells : botshells); + // Connect newtets to tets outside the cavity. These connections are needed + // for identifying the middle faces (which belong to R). + for (j = 0; j < 2; j++) { + cavshells = (j == 0 ? topshells : botshells); if (cavshells != NULL) { - for (i = 0; i < (int) cavshells->objects; i++) { + for (i = 0; i < cavshells->objects; i++) { // Get a temp subface. parysh = (face *) fastlookup(cavshells, i); - // Get the boundary tet outsode the cavity. + // Get the boundary tet outside the cavity (saved in sh[0]). 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); + pa = org(bdrytet); + pb = dest(bdrytet); pc = apex(bdrytet); - // Get the adjacent new tet which is in the cavity. + // Get the adjacent new tet inside 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); - } + // Mark neightet as an interior tet of this cavity. + infect(neightet); + // Connect the two tets (the old connections are replaced). + bond(bdrytet, neightet); + tsdissolve(neightet); // Clear the pointer to tmpsh. // Update the point-to-tets map. - setpoint2tet(pa, encode(neightet)); - setpoint2tet(pb, encode(neightet)); - setpoint2tet(pc, encode(neightet)); - // Delete the temp subface. - // shellfacedealloc(subfacepool, parysh->sh); - // if (oppo(bdrytet) == dummypoint) { - // Delete a faked tet. - // tetrahedrondealloc(bdrytet.tet); - // } - } + setpoint2tet(pa, (tetrahedron) neightet.tet); + setpoint2tet(pb, (tetrahedron) neightet.tet); + setpoint2tet(pc, (tetrahedron) neightet.tet); + } // i } // if (cavshells != NULL) - } + } // j + + if (crossedge != NULL) { + // Glue top and bottom tets at their common facet. + triface toptet, bottet, spintet, *midface; + point pd, pe; + REAL ori; + int types[2], poss[4]; + int interflag; + int bflag; + + mflag = false; + pd = org(*crossedge); + pe = dest(*crossedge); + + // Search the first (middle) face in R. + // Since R may be non-convex, we must make sure that the face is in the + // interior of R. We search a face in 'topnewtets' whose three vertices + // are on R and it intersects 'crossedge' in its interior. Then search + // a matching face in 'botnewtets'. + for (i = 0; i < topnewtets->objects && !mflag; i++) { + searchtet = * (triface *) fastlookup(topnewtets, i); + for (searchtet.ver = 0; searchtet.ver < 4 && !mflag; searchtet.ver++) { + pa = org(searchtet); + if (pmarktested(pa)) { + pb = dest(searchtet); + if (pmarktested(pb)) { + pc = apex(searchtet); + if (pmarktested(pc)) { + // Check if this face intersects [d,e]. + interflag = tri_edge_test(pa,pb,pc,pd,pe,NULL,1,types,poss); + if (interflag == 2) { + // They intersect at a single point. Found. + toptet = searchtet; + // The face lies in the interior of R. + // Get the tet (in topnewtets) which lies above R. + ori = orient3d(pa, pb, pc, pd); + assert(ori != 0); + if (ori < 0) { + fsymself(toptet); + pa = org(toptet); + pb = dest(toptet); + } + // Search the face [b,a,c] in 'botnewtets'. + for (j = 0; j < botnewtets->objects; j++) { + neightet = * (triface *) fastlookup(botnewtets, j); + // Is neightet contains 'b'. + if ((point) neightet.tet[4] == pb) { + neightet.ver = 11; + } else if ((point) neightet.tet[5] == pb) { + neightet.ver = 3; + } else if ((point) neightet.tet[6] == pb) { + neightet.ver = 7; + } else if ((point) neightet.tet[7] == pb) { + neightet.ver = 0; + } else { + continue; + } + // Is the 'neightet' contains edge [b,a]. + if (dest(neightet) == pa) { + // 'neightet' is just the edge. + } else if (apex(neightet) == pa) { + eprevesymself(neightet); + } else if (oppo(neightet) == pa) { + esymself(neightet); + enextself(neightet); + } else { + continue; + } + // Is 'neightet' the face [b,a,c]. + if (apex(neightet) == pc) { + bottet = neightet; + mflag = true; + break; + } + } // j + } // if (interflag == 2) + } // pc + } // pb + } // pa + } // toptet.ver + } // i - mflag = true; // Initialize it. + if (mflag) { + // Found a pair of matched faces in 'toptet' and 'bottet'. + bond(toptet, bottet); + // Both are interior tets. + infect(toptet); + infect(bottet); + // Add this face into search list. + markface(toptet); + midfaces->newindex((void **) &parytet); + *parytet = toptet; + } else { + // No pair of 'toptet' and 'bottet'. + toptet.tet = NULL; + // Randomly split an interior edge of R. + i = randomnation(missingshs->objects - 1); + recentsh = * (face *) fastlookup(missingshs, i); + } + + // Find other middle faces, connect top and bottom tets. + for (i = 0; i < midfaces->objects && mflag; i++) { + // Get a matched middle face [a, b, c] + midface = (triface *) fastlookup(midfaces, i); + // The tet must be a new created tet (marktested). + assert(marktested(*midface)); // SELF_CHECK + // Check the neighbors at the edges of this face. + for (j = 0; j < 3 && mflag; j++) { + toptet = *midface; + bflag = false; + while (1) { + // Go to the next face in the same tet. + esymself(toptet); + pc = apex(toptet); + if (pmarktested(pc)) { + break; // Find a subface. + } + if (pc == dummypoint) { + assert(0); // Check this case. + break; // Find a subface. + } + // Go to the adjacent tet. + fsymself(toptet); + // 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)) { + fsym(*midface, bottet); + spintet = bottet; + while (1) { + esymself(bottet); + pd = apex(bottet); + if (pd == pc) break; // Face matched. + fsymself(bottet); + if (bottet.tet == spintet.tet) { + // Not found a matched bottom face. + mflag = false; + break; + } + } // while (1) + 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. + markface(toptet); + midfaces->newindex((void **) &parytet); + *parytet = toptet; + } + } else { // mflag == false + // Adjust 'toptet' and 'bottet' to be the crossing edges. + fsym(*midface, bottet); + spintet = bottet; + while (1) { + esymself(bottet); + pd = apex(bottet); + if (pmarktested(pd)) { + // assert(pd != pc); + // Let 'toptet' be [a,b,c,#], and 'bottet' be [b,a,d,*]. + // Adjust 'toptet' and 'bottet' to be the crossing edges. + // Test orient3d(b,c,#,d). + ori = orient3d(dest(toptet), pc, oppo(toptet), pd); + if (ori < 0) { + // Edges [a,d] and [b,c] cross each other. + enextself(toptet); // [b,c] + enextself(bottet); // [a,d] + } else if (ori > 0) { + // Edges [a,c] and [b,d] cross each other. + eprevself(toptet); // [c,a] + eprevself(bottet); // [d,b] + } else { + // b,c,#,and d are coplanar!. + assert(0); + } + break; // Not matched + } + fsymself(bottet); + assert (bottet.tet != spintet.tet); + } + } // if (!mflag) + } // if (!facemarked(toptet)) + } // if (!bflag) + enextself(*midface); + } // j + } // i - if (midfaces != NULL) { + if (mflag) { + if (b->verbose > 2) { + printf(" Found %ld middle subfaces.\n", midfaces->objects); + } + face oldsh, newsh, casout, casin, neighsh; - // Mark all facet vertices for finding middle subfaces. - for (i = 0; i < (int) facpoints->objects; i++) { - pf = * (point *) fastlookup(facpoints, i); - pinfect(pf); - } + oldsh = * (face *) fastlookup(missingshs, 0); - // 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'. - } - } + // Create new subfaces to fill the region R. + for (i = 0; i < midfaces->objects; i++) { + // Get a matched middle face [a, b, c] + midface = (triface *) fastlookup(midfaces, i); + unmarkface(*midface); + makeshellface(subfaces, &newsh); + setsorg(newsh, org(*midface)); + setsdest(newsh, dest(*midface)); + setsapex(newsh, apex(*midface)); + // The new subface gets its markers from the old one. + setshellmark(newsh, shellmark(oldsh)); + if (checkconstraints) { + setareabound(newsh, areabound(oldsh)); } + // Connect the new subface to adjacent tets. + tsbond(*midface, newsh); + fsym(*midface, neightet); + sesymself(newsh); + tsbond(neightet, newsh); } - enextself(midface); // Go to the next edge. - } // j - } // i - } // if (midfaces != NULL) + // Connect new subfaces together and to the bdry of R. + // Delete faked segments. + for (i = 0; i < midfaces->objects; i++) { + // Get a matched middle face [a, b, c] + midface = (triface *) fastlookup(midfaces, i); + for (j = 0; j < 3; j++) { + tspivot(*midface, newsh); + spivot(newsh, casout); + if (casout.sh == NULL) { + // Search its neighbor. + fnext(*midface, searchtet); + while (1) { + // (1) First check if this side is a bdry edge of R. + tsspivot1(searchtet, checkseg); + if (checkseg.sh != NULL) { + // It's a bdry edge of R. + assert(!infected(searchtet)); // It must not be a cavity tet. + // Get the old subface. + checkseg.shver = 0; + spivot(checkseg, oldsh); + if (sinfected(checkseg)) { + // It's a faked segment. Delete it. + spintet = searchtet; + while (1) { + tssdissolve1(spintet); + fnextself(spintet); + if (spintet.tet == searchtet.tet) break; + } + shellfacedealloc(subsegs, checkseg.sh); + ssdissolve(oldsh); + checkseg.sh = NULL; + } + spivot(oldsh, casout); + if (casout.sh != NULL) { + casin = casout; + if (checkseg.sh != NULL) { + // Make sure that the subface has the right ori at the + // segment. + checkseg.shver = 0; + if (sorg(newsh) != sorg(checkseg)) { + sesymself(newsh); + } + spivot(casin, neighsh); + while (neighsh.sh != oldsh.sh) { + casin = neighsh; + spivot(casin, neighsh); + } + } + sbond1(newsh, casout); + sbond1(casin, newsh); + } + if (checkseg.sh != NULL) { + ssbond(newsh, checkseg); + } + break; + } // if (checkseg.sh != NULL) + // (2) Second check if this side is an interior edge of R. + tspivot(searchtet, neighsh); + if (neighsh.sh != NULL) { + // Found an adjacent subface of newsh (an interior edge). + sbond(newsh, neighsh); + break; + } + fnextself(searchtet); + assert(searchtet.tet != midface->tet); + } // while (1) + } // if (casout.sh == NULL) + enextself(*midface); + } // j + } // i - 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; + // Delete old subfaces. + for (i = 0; i < missingshs->objects; i++) { + parysh = (face *) fastlookup(missingshs, i); + shellfacedealloc(subfaces, parysh->sh); } - // 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 { + if (toptet.tet != NULL) { + // Faces at top and bottom are not matched. + // Choose a Steiner point in R. + // Split one of the crossing edges. + pa = org(toptet); + pb = dest(toptet); + pc = org(bottet); + pd = dest(bottet); + // Search an edge in R which is either [a,b] or [c,d]. + // Reminder: Subfaces in this list 'missingshs', except the first + // one, represents an interior edge of R. + for (i = 1; i < missingshs->objects; i++) { + parysh = (face *) fastlookup(missingshs, i); + if (((sorg(*parysh) == pa) && (sdest(*parysh) == pb)) || + ((sorg(*parysh) == pb) && (sdest(*parysh) == pa))) break; + if (((sorg(*parysh) == pc) && (sdest(*parysh) == pd)) || + ((sorg(*parysh) == pd) && (sdest(*parysh) == pc))) break; + } + if (i < missingshs->objects) { + // Found. Return it. + recentsh = *parysh; + } else { + assert(0); + } } } + + midfaces->restart(); } 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++; - } + mflag = true; + } - if (facpoints != NULL) { - // Unmark all facet vertices. - for (i = 0; i < (int) facpoints->objects; i++) { - pf = * (point *) fastlookup(facpoints, i); - puninfect(pf); - } - } - - // Delete the temp subfaces and faked tets. - for (k = 0; k < 2; k++) { - cavshells = (k == 0 ? topshells : botshells); + // Delete the temp subfaces. + for (j = 0; j < 2; j++) { + cavshells = (j == 0 ? topshells : botshells); if (cavshells != NULL) { - for (i = 0; i < (int) cavshells->objects; i++) { + for (i = 0; i < 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); - } shellfacedealloc(subfaces, parysh->sh); } } @@ -21557,10 +16519,6 @@ bool tetgenmesh::fillcavity(arraypool* topshells, arraypool* botshells, if (botshells != NULL) { botshells->restart(); } - if (midfaces != NULL) { - midfaces->restart(); - } - // Comment: Now no vertex is marked. return mflag; } @@ -21572,106 +16530,191 @@ bool tetgenmesh::fillcavity(arraypool* topshells, arraypool* botshells, /////////////////////////////////////////////////////////////////////////////// void tetgenmesh::carvecavity(arraypool *crosstets, arraypool *topnewtets, - arraypool *botnewtets) + arraypool *botnewtets) { arraypool *newtets; - triface *parytet, *pnewtet, neightet; - face checkseg; //, *parysh; - // int hitbdry; - int i, j, k; + shellface *sptr, *ssptr; + triface *parytet, *pnewtet, newtet, neightet, spintet; + face checksh, *parysh; + face checkseg, *paryseg; + int t1ver; + int i, j; + + if (b->verbose > 2) { + printf(" Carve cavity: %ld old tets.\n", crosstets->objects); + } + + // First process subfaces and segments which are adjacent to the cavity. + // They must be re-connected to new tets in the cavity. + // Comment: It is possible that some subfaces and segments are completely + // inside the cavity. This can happen even if the cavity is not enlarged. + // Before deleting the old tets, find and queue all interior subfaces + // and segments. They will be recovered later. 2010-05-06. - /*// 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++) { + // Collect all subfaces and segments which attached to the old tets. + for (i = 0; i < crosstets->objects; i++) { parytet = (triface *) fastlookup(crosstets, i); - assert(infected(*parytet)); // SELF_CHECK - if (parytet->tet[8] != NULL) { + if ((sptr = (shellface*) parytet->tet[9]) != NULL) { + for (j = 0; j < 4; j++) { + if (sptr[j]) { + sdecode(sptr[j], checksh); + if (!sinfected(checksh)) { + sinfect(checksh); + cavetetshlist->newindex((void **) &parysh); + *parysh = checksh; + } + } + } // j + } + if ((ssptr = (shellface*) 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))); - } + if (ssptr[j]) { + sdecode(ssptr[j], checkseg); + // Skip a deleted segment (was a faked segment) + if (checkseg.sh[3] != NULL) { + if (!sinfected(checkseg)) { sinfect(checkseg); - subsegstack->newindex((void **) &parysh); - *parysh = checkseg; + cavetetseglist->newindex((void **) &paryseg); + *paryseg = checkseg; } } } + } // j + } + } // i + + // Uninfect collected subfaces. + for (i = 0; i < cavetetshlist->objects; i++) { + parysh = (face *) fastlookup(cavetetshlist, i); + suninfect(*parysh); + } + // Uninfect collected segments. + for (i = 0; i < cavetetseglist->objects; i++) { + paryseg = (face *) fastlookup(cavetetseglist, i); + suninfect(*paryseg); + } + + // Connect subfaces to new tets. + for (i = 0; i < cavetetshlist->objects; i++) { + parysh = (face *) fastlookup(cavetetshlist, i); + // Get an adjacent tet at this subface. + stpivot(*parysh, neightet); + // Does this tet lie inside the cavity. + if (infected(neightet)) { + // Yes. Get the other adjacent tet at this subface. + sesymself(*parysh); + stpivot(*parysh, neightet); + // Does this tet lie inside the cavity. + if (infected(neightet)) { + checksh = *parysh; + stdissolve(checksh); + caveencshlist->newindex((void **) &parysh); + *parysh = checksh; + } + } + if (!infected(neightet)) { + // Found an outside tet. Re-connect this subface to a new tet. + fsym(neightet, newtet); + assert(marktested(newtet)); // It's a new tet. + sesymself(*parysh); + tsbond(newtet, *parysh); + } + } // i + + + for (i = 0; i < cavetetseglist->objects; i++) { + checkseg = * (face *) fastlookup(cavetetseglist, i); + // Check if the segment is inside the cavity. + sstpivot1(checkseg, neightet); + spintet = neightet; + while (1) { + if (!infected(spintet)) { + // This segment is on the boundary of the cavity. + break; + } + fnextself(spintet); + if (spintet.tet == neightet.tet) { + sstdissolve1(checkseg); + caveencseglist->newindex((void **) &paryseg); + *paryseg = checkseg; + break; } } - }*/ + if (!infected(spintet)) { + // A boundary segment. Connect this segment to the new tets. + sstbond1(checkseg, spintet); + neightet = spintet; + while (1) { + tssbond1(spintet, checkseg); + fnextself(spintet); + if (spintet.tet == neightet.tet) break; + } + } + } // i + + + cavetetshlist->restart(); + cavetetseglist->restart(); // Delete the old tets in cavity. - for (i = 0; i < (int) crosstets->objects; i++) { + for (i = 0; i < crosstets->objects; i++) { parytet = (triface *) fastlookup(crosstets, i); + if (ishulltet(*parytet)) { + hullsize--; + } tetrahedrondealloc(parytet->tet); } + crosstets->restart(); // crosstets will be re-used. - // Collect infected new tets in cavity. - for (k = 0; k < 2; k++) { - newtets = (k == 0 ? topnewtets : botnewtets); + // Collect new tets in cavity. Some new tets have already been found + // (and infected) in the fillcavity(). We first collect them. + for (j = 0; j < 2; j++) { + newtets = (j == 0 ? topnewtets : botnewtets); if (newtets != NULL) { - for (i = 0; i < (int) newtets->objects; i++) { + for (i = 0; i < newtets->objects; i++) { parytet = (triface *) fastlookup(newtets, i); if (infected(*parytet)) { crosstets->newindex((void **) &pnewtet); *pnewtet = *parytet; } - } + } // i } - } - // Collect all new tets in cavity. - for (i = 0; i < (int) crosstets->objects; i++) { + } // j + + // Now we collect all new tets in cavity. + for (i = 0; i < crosstets->objects; i++) { parytet = (triface *) fastlookup(crosstets, i); - if (i == 0) { - recenttet = *parytet; // Remember a live handle. - } for (j = 0; j < 4; j++) { decode(parytet->tet[j], neightet); if (marktested(neightet)) { // Is it a new tet? if (!infected(neightet)) { // Find an interior tet. - assert(neightet.tet != dummytet); // SELF_CHECK + //assert((point) neightet.tet[7] != dummypoint); // SELF_CHECK infect(neightet); crosstets->newindex((void **) &pnewtet); *pnewtet = neightet; } } - } - } + } // j + } // i - // Delete outer new tets (those new tets which are not infected). - for (k = 0; k < 2; k++) { - newtets = (k == 0 ? topnewtets : botnewtets); + parytet = (triface *) fastlookup(crosstets, 0); + recenttet = *parytet; // Remember a live handle. + + // Delete outer new tets. + for (j = 0; j < 2; j++) { + newtets = (j == 0 ? topnewtets : botnewtets); if (newtets != NULL) { - for (i = 0; i < (int) newtets->objects; i++) { + for (i = 0; i < newtets->objects; i++) { parytet = (triface *) fastlookup(newtets, i); if (infected(*parytet)) { // This is an interior tet. uninfect(*parytet); unmarktest(*parytet); + if (ishulltet(*parytet)) { + hullsize++; + } } else { // An outer tet. Delete it. tetrahedrondealloc(parytet->tet); @@ -21694,38 +16737,29 @@ void tetgenmesh::carvecavity(arraypool *crosstets, arraypool *topnewtets, /////////////////////////////////////////////////////////////////////////////// void tetgenmesh::restorecavity(arraypool *crosstets, arraypool *topnewtets, - arraypool *botnewtets) + arraypool *botnewtets, arraypool *missingshbds) { - triface *parytet, neightet; - face checksh; + triface *parytet, neightet, spintet; + face *parysh; + face checkseg; point *ppt; + int t1ver; int i, j; // Reconnect crossing tets to cavity boundary. - for (i = 0; i < (int) crosstets->objects; i++) { + for (i = 0; i < crosstets->objects; i++) { parytet = (triface *) fastlookup(crosstets, i); assert(infected(*parytet)); // SELF_CHECK - if (i == 0) { - recenttet = *parytet; // Remember a live handle. - } parytet->ver = 0; - for (parytet->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)) { + for (parytet->ver = 0; parytet->ver < 4; parytet->ver++) { + fsym(*parytet, neightet); + if (!infected(neightet)) { + // Restore the old connections of tets. bond(*parytet, neightet); - tspivot(*parytet, checksh); - if (checksh.sh != dummysh) { - tsbond(*parytet, checksh); - } } } // Update the point-to-tet map. - parytet->loc = 0; + parytet->ver = 0; ppt = (point *) &(parytet->tet[4]); for (j = 0; j < 4; j++) { setpoint2tet(ppt[j], encode(*parytet)); @@ -21733,19 +16767,44 @@ void tetgenmesh::restorecavity(arraypool *crosstets, arraypool *topnewtets, } // Uninfect all crossing tets. - for (i = 0; i < (int) crosstets->objects; i++) { + for (i = 0; i < crosstets->objects; i++) { parytet = (triface *) fastlookup(crosstets, i); uninfect(*parytet); } + // Remember a live handle. + recenttet = * (triface *) fastlookup(crosstets, 0); + + // Delete faked segments. + for (i = 0; i < missingshbds->objects; i++) { + parysh = (face *) fastlookup(missingshbds, i); + sspivot(*parysh, checkseg); + assert(checkseg.sh != NULL); + if (checkseg.sh[3] != NULL) { + if (sinfected(checkseg)) { + // It's a faked segment. Delete it. + sstpivot1(checkseg, neightet); + spintet = neightet; + while (1) { + tssdissolve1(spintet); + fnextself(spintet); + if (spintet.tet == neightet.tet) break; + } + shellfacedealloc(subsegs, checkseg.sh); + ssdissolve(*parysh); + //checkseg.sh = NULL; + } + } + } // i + // Delete new tets. - for (i = 0; i < (int) topnewtets->objects; i++) { + for (i = 0; i < topnewtets->objects; i++) { parytet = (triface *) fastlookup(topnewtets, i); tetrahedrondealloc(parytet->tet); } if (botnewtets != NULL) { - for (i = 0; i < (int) botnewtets->objects; i++) { + for (i = 0; i < botnewtets->objects; i++) { parytet = (triface *) fastlookup(botnewtets, i); tetrahedrondealloc(parytet->tet); } @@ -21760,12655 +16819,14023 @@ void tetgenmesh::restorecavity(arraypool *crosstets, arraypool *topnewtets, /////////////////////////////////////////////////////////////////////////////// // // -// splitsubedge() Split a non-Delaunay edge (not a segment) in the // -// surface mesh of a facet. // -// // -// 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. // +// flipcertify() Insert a crossing face into priority queue. // // // -// Next,the actual inserted new point is also inserted into the surface mesh.// -// Non-Delaunay segments and newly created subfaces are queued for recovery. // +// A crossing face of a facet must have at least one top and one bottom ver- // +// tex of the facet. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::splitsubedge(point newpt, face *searchsh, arraypool *facfaces, - arraypool *facpoints) +void tetgenmesh::flipcertify(triface *chkface,badface **pqueue,point plane_pa, + point plane_pb, point plane_pc) { - // queue *flipqueue; - triface searchtet; - face splitsh; - face *psseg, sseg; // *parysh; - point pa, pb; - enum locateresult loc; - int s, i; + badface *parybf, *prevbf, *nextbf; + triface neightet; + face checksh; + point p[5]; + REAL w[5]; + REAL insph, ori4; + int topi, boti; + int 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. - } - } -} + // Compute the flip time \tau. + fsym(*chkface, neightet); -/////////////////////////////////////////////////////////////////////////////// -// // -// constrainedfacets() Recover subfaces saved in 'subfacestack'. // -// // -/////////////////////////////////////////////////////////////////////////////// + p[0] = org(*chkface); + p[1] = dest(*chkface); + p[2] = apex(*chkface); + p[3] = oppo(*chkface); + p[4] = oppo(neightet); -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; + // Check if the face is a crossing face. + topi = boti = 0; + for (i = 0; i < 3; i++) { + if (pmarktest2ed(p[i])) topi++; + if (pmarktest3ed(p[i])) boti++; + } + if ((topi == 0) || (boti == 0)) { + // It is not a crossing face. + // return; + for (i = 3; i < 5; i++) { + if (pmarktest2ed(p[i])) topi++; + if (pmarktest3ed(p[i])) boti++; + } + if ((topi == 0) || (boti == 0)) { + // The two tets sharing at this face are on one side of the facet. + // Check if this face is locally Delaunay (due to rounding error). + if ((p[3] != dummypoint) && (p[4] != dummypoint)) { + // Do not check it if it is a subface. + tspivot(*chkface, checksh); + if (checksh.sh == NULL) { + insph = insphere_s(p[1], p[0], p[2], p[3], p[4]); + assert(insph != 0); + if (insph > 0) { + // Add the face into queue. + if (b->verbose > 2) { + printf(" A locally non-Delanay face (%d, %d, %d)-%d,%d\n", + pointmark(p[0]), pointmark(p[1]), pointmark(p[2]), + pointmark(p[3]), pointmark(p[4])); + } + parybf = (badface *) flippool->alloc(); + parybf->key = 0.; // tau = 0, do immediately. + parybf->tt = *chkface; + parybf->forg = p[0]; + parybf->fdest = p[1]; + parybf->fapex = p[2]; + parybf->foppo = p[3]; + parybf->noppo = p[4]; + // Add it at the top of the priority queue. + if (*pqueue == NULL) { + *pqueue = parybf; + parybf->nextitem = NULL; + } else { + parybf->nextitem = *pqueue; + *pqueue = parybf; + } + } // if (insph > 0) + } // if (checksh.sh == NULL) + } + //return; + } + return; // Test: omit this face. + } - if (b->verbose) { - printf(" Constraining facets.\n"); + // Decide the "height" for each point. + for (i = 0; i < 5; i++) { + if (pmarktest2ed(p[i])) { + // A top point has a positive weight. + w[i] = orient3dfast(plane_pa, plane_pb, plane_pc, p[i]); + if (w[i] < 0) w[i] = -w[i]; + assert(w[i] != 0); + } else { + w[i] = 0; + } } - // 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; + // Make sure orient3d(p[1], p[0], p[2], p[3]) > 0; + // Hence if (insphere(p[1], p[0], p[2], p[3], p[4]) > 0) means that + // p[4] lies inside the circumsphere of p[1], p[0], p[2], p[3]. + // The same if orient4d(p[1], p[0], p[2], p[3], p[4]) > 0 means that + // p[4] lies below the oriented hyperplane passing through + // p[1], p[0], p[2], p[3]. - // Loop until 'subfacstack' is empty. - while (subfacstack->objects > 0l) { - subfacstack->objects--; - pssub = (face *) fastlookup(subfacstack, subfacstack->objects); - ssub = *pssub; + insph = insphere(p[1], p[0], p[2], p[3], p[4]); + ori4 = orient4d(p[1], p[0], p[2], p[3], p[4], w[1], w[0], w[2], w[3], w[4]); - if (ssub.sh[3] == NULL) continue; // Skip a dead subface. + if (b->verbose > 2) { + printf(" Heights: (%g, %g, %g, %g, %g)\n", w[0],w[1],w[2],w[3],w[4]); + printf(" Insph: %g, ori4: %g, tau = %g\n", insph, ori4, -insph/ori4); + } - stpivot(ssub, neightet); - if (neightet.tet == dummytet) { - sesymself(ssub); - stpivot(ssub, neightet); + if (ori4 > 0) { + // Add the face into queue. + if (b->verbose > 2) { + printf(" Insert face (%d, %d, %d) - %d, %d\n", pointmark(p[0]), + pointmark(p[1]), pointmark(p[2]), pointmark(p[3]), pointmark(p[4])); } - - 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; - } + + parybf = (badface *) flippool->alloc(); + + parybf->key = -insph / ori4; + parybf->tt = *chkface; + parybf->forg = p[0]; + parybf->fdest = p[1]; + parybf->fapex = p[2]; + parybf->foppo = p[3]; + parybf->noppo = p[4]; + + // Push the face into priority queue. + //pq.push(bface); + if (*pqueue == NULL) { + *pqueue = parybf; + parybf->nextitem = NULL; + } else { + // Search an item whose key is larger or equal to current key. + prevbf = NULL; + nextbf = *pqueue; + //if (!b->flipinsert_random) { // Default use a priority queue. + // Insert the item into priority queue. + while (nextbf != NULL) { + if (nextbf->key < parybf->key) { + prevbf = nextbf; + nextbf = nextbf->nextitem; } 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); + break; + } } - 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++; + //} // if (!b->flipinsert_random) + // Insert the new item between prev and next items. + if (prevbf == NULL) { + *pqueue = parybf; + } else { + prevbf->nextitem = parybf; } - // Now the mesh should be constrained Delaunay. - } // if (neightet.tet == NULL) - } - - if (b->verbose) { - printf(" %ld subedge flips.\n", flip22count - bakflip22count); - printf(" %ld cavities remeshed.\n", cavitycount); + parybf->nextitem = nextbf; + } + } else if (ori4 == 0) { + } - - // 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; } /////////////////////////////////////////////////////////////////////////////// // // -// formskeleton() Form a constrained tetrahedralization. // +// flipinsertfacet() Insert a facet into a CDT by flips. // // // -// The segments and facets of a PLS will be recovered. // +// The algorithm is described in Shewchuk's paper "Updating and Constructing // +// Constrained Delaunay and Constrained Regular Triangulations by Flips", in // +// Proc. 19th Ann. Symp. on Comput. Geom., 86--95, 2003. // +// // +// 'crosstets' contains the set of crossing tetrahedra (infected) of the // +// facet. 'toppoints' and 'botpoints' are points lies above and below the // +// facet, not on the facet. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::formskeleton(clock_t& tv) +void tetgenmesh::flipinsertfacet(arraypool *crosstets, arraypool *toppoints, + arraypool *botpoints, arraypool *midpoints) { - triface searchtet; - face *pssub, ssub; - int s, i; + arraypool *crossfaces, *bfacearray; + triface fliptets[6], baktets[2], fliptet, newface; + triface neightet, *parytet; + face checksh; + face checkseg; + badface *pqueue; + badface *popbf, bface; + point plane_pa, plane_pb, plane_pc; + point p1, p2, pd, pe; + point *parypt; + flipconstraints fc; + REAL ori[3]; + int convcount, copcount; + int flipflag, fcount; + int n, i; + long f23count, f32count, f44count; + long totalfcount; - if (!b->quiet) { - printf("Recovering boundaries.\n"); - } + f23count = flip23count; + f32count = flip32count; + f44count = flip44count; - caveshlist = new arraypool(sizeof(face), 10); - caveshbdlist = new arraypool(sizeof(face), 10); + // Get three affinely independent vertices in the missing region R. + calculateabovepoint(midpoints, &plane_pa, &plane_pb, &plane_pc); - // 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; + // Mark top and bottom points. Do not mark midpoints. + for (i = 0; i < toppoints->objects; i++) { + parypt = (point *) fastlookup(toppoints, i); + if (!pmarktested(*parypt)) { + pmarktest2(*parypt); } - } 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; + } + for (i = 0; i < botpoints->objects; i++) { + parypt = (point *) fastlookup(botpoints, i); + if (!pmarktested(*parypt)) { + pmarktest3(*parypt); } } - // Segments will be introduced. - checksubsegs = 1; - // Recover segments. - delaunizesegments2(); + // Collect crossing faces. + crossfaces = cavetetlist; // Re-use array 'cavetetlist'. - tv = clock(); + // Each crossing face contains at least one bottom vertex and + // one top vertex. + for (i = 0; i < crosstets->objects; i++) { + parytet = (triface *) fastlookup(crosstets, i); + fliptet = *parytet; + for (fliptet.ver = 0; fliptet.ver < 4; fliptet.ver++) { + fsym(fliptet, neightet); + if (infected(neightet)) { // It is an interior face. + if (!marktested(neightet)) { // It is an unprocessed face. + crossfaces->newindex((void **) &parytet); + *parytet = fliptet; + } + } + } + marktest(fliptet); + } - // 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; + if (b->verbose > 1) { + printf(" Found %ld crossing faces.\n", crossfaces->objects); } - // Subfaces will be introduced. - checksubfaces = 1; - // Recover facets. - constrainedfacets2(); + for (i = 0; i < crosstets->objects; i++) { + parytet = (triface *) fastlookup(crosstets, i); + unmarktest(*parytet); + uninfect(*parytet); + } - delete caveshlist; - delete caveshbdlist; - caveshlist = NULL; - caveshbdlist = NULL; + // Initialize the priority queue. + pqueue = NULL; - // 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(); + for (i = 0; i < crossfaces->objects; i++) { + parytet = (triface *) fastlookup(crossfaces, i); + flipcertify(parytet, &pqueue, plane_pa, plane_pb, plane_pc); } - // Now no segment is bonded to tets. - checksubsegs = 0; - // Delete the memory. - tet2segpool->restart(); -} + crossfaces->restart(); -/////////////////////////////////////////////////////////////////////////////// -// // -// 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. // -// // -/////////////////////////////////////////////////////////////////////////////// + // The list for temporarily storing unflipable faces. + bfacearray = new arraypool(sizeof(triface), 4); -void tetgenmesh::infecthull(memorypool *viri) -{ - triface tetloop, tsymtet; - tetrahedron **deadtet; - face hullface; - // point horg, hdest, hapex; - if (b->verbose > 1) { - 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); - } - */ - } - } - } - } - tetloop.tet = tetrahedrontraverse(); - } -} + fcount = 0; // Count the number of flips. -/////////////////////////////////////////////////////////////////////////////// -// // -// 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. // -// // -/////////////////////////////////////////////////////////////////////////////// + // Flip insert the facet. + while (pqueue != NULL) { -void tetgenmesh::plague(memorypool *viri) -{ - tetrahedron **virusloop; - tetrahedron **deadtet; - triface testtet, neighbor; - face neighsh, testseg; - face spinsh, casingin, casingout; - int firstdadsub; - int i; + // Pop a face from the priority queue. + popbf = pqueue; + bface = *popbf; - if (b->verbose > 1) { - 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))); - } + // Update the queue. + pqueue = pqueue->nextitem; + + // Delete the popped item from the pool. + flippool->dealloc((void *) popbf); + + if (!isdeadtet(bface.tt)) { + if ((org(bface.tt) == bface.forg) && (dest(bface.tt) == bface.fdest) && + (apex(bface.tt) == bface.fapex) && (oppo(bface.tt) == bface.foppo)) { + // It is still a crossing face of R. + fliptet = bface.tt; + fsym(fliptet, neightet); + assert(!isdeadtet(neightet)); + if (oppo(neightet) == bface.noppo) { + pd = oppo(fliptet); + pe = oppo(neightet); + + if (b->verbose > 2) { + printf(" Get face (%d, %d, %d) - %d, %d, tau = %.17g\n", + pointmark(bface.forg), pointmark(bface.fdest), + pointmark(bface.fapex), pointmark(bface.foppo), + pointmark(bface.noppo), bface.key); } - // For keep the same enext() direction. - findedge(&testtet, sorg(neighsh), sdest(neighsh)); + flipflag = 0; + + // Check for which type of flip can we do. + convcount = 3; + copcount = 0; 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); + p1 = org(fliptet); + p2 = dest(fliptet); + ori[i] = orient3d(p1, p2, pd, pe); + if (ori[i] < 0) { + convcount--; + //break; + } else if (ori[i] == 0) { + convcount--; // Possible 4-to-4 flip. + copcount++; + //break; + } + enextself(fliptet); + } + + if (convcount == 3) { + // A 2-to-3 flip is found. + // The face should not be a subface. + tspivot(fliptet, checksh); + assert(checksh.sh == NULL); + + fliptets[0] = fliptet; // abcd, d may be the new vertex. + fliptets[1] = neightet; // bace. + flip23(fliptets, 1, &fc); + // Put the link faces into check list. + for (i = 0; i < 3; i++) { + eprevesym(fliptets[i], newface); + crossfaces->newindex((void **) &parytet); + *parytet = newface; + } + for (i = 0; i < 3; i++) { + enextesym(fliptets[i], newface); + crossfaces->newindex((void **) &parytet); + *parytet = newface; + } + flipflag = 1; + } else if (convcount == 2) { + assert(copcount <= 1); + //if (copcount <= 1) { + // A 3-to-2 or 4-to-4 may be possible. + // Get the edge which is locally non-convex or flat. + for (i = 0; i < 3; i++) { + if (ori[i] <= 0) break; + enextself(fliptet); + } + // The edge should not be a segment. + tsspivot1(fliptet, checkseg); + assert(checkseg.sh == NULL); + + // Collect tets sharing at this edge. + // NOTE: This operation may collect tets which lie outside the + // cavity, e.g., when the edge lies on the boundary of the + // cavity. Do not flip if there are outside tets at this edge. + // 2012-07-27. + esym(fliptet, fliptets[0]); // [b,a,d,c] + n = 0; + do { + p1 = apex(fliptets[n]); + if (!(pmarktested(p1) || pmarktest2ed(p1) || pmarktest3ed(p1))) { + // This apex is not on the cavity. Hence the face does not + // lie inside the cavity. Do not flip this edge. + n = 1000; break; + } + fnext(fliptets[n], fliptets[n + 1]); + n++; + } while ((fliptets[n].tet != fliptet.tet) && (n < 5)); + + if (n == 3) { + // Found a 3-to-2 flip. + flip32(fliptets, 1, &fc); + // Put the link faces into check list. + for (i = 0; i < 3; i++) { + esym(fliptets[0], newface); + crossfaces->newindex((void **) &parytet); + *parytet = newface; + enextself(fliptets[0]); } - 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); + for (i = 0; i < 3; i++) { + esym(fliptets[1], newface); + crossfaces->newindex((void **) &parytet); + *parytet = newface; + enextself(fliptets[1]); + } + flipflag = 1; + } else if (n == 4) { + if (copcount == 1) { + // Found a 4-to-4 flip. + // Let the six vertices are: a,b,c,d,e,f, where + // fliptets[0] = [b,a,d,c] + // [1] = [b,a,c,e] + // [2] = [b,a,e,f] + // [3] = [b,a,f,d] + // After the 4-to-4 flip, edge [a,b] is flipped, edge [e,d] + // is created. + // First do a 2-to-3 flip. + // Comment: This flip temporarily creates a degenerated + // tet (whose volume is zero). It will be removed by the + // followed 3-to-2 flip. + fliptets[0] = fliptet; // = [a,b,c,d], d is the new vertex. + // fliptets[1]; // = [b,a,c,e]. + baktets[0] = fliptets[2]; // = [b,a,e,f] + baktets[1] = fliptets[3]; // = [b,a,f,d] + // The flip may involve hull tets. + flip23(fliptets, 1, &fc); + // Put the "outer" link faces into check list. + // fliptets[0] = [e,d,a,b] => will be flipped, so + // [a,b,d] and [a,b,e] are not "outer" link faces. + for (i = 1; i < 3; i++) { + eprevesym(fliptets[i], newface); + crossfaces->newindex((void **) &parytet); + *parytet = newface; + } + for (i = 1; i < 3; i++) { + enextesym(fliptets[i], newface); + crossfaces->newindex((void **) &parytet); + *parytet = newface; + } + // Then do a 3-to-2 flip. + enextesymself(fliptets[0]); // fliptets[0] is [e,d,a,b]. + eprevself(fliptets[0]); // = [b,a,d,c], d is the new vertex. + fliptets[1] = baktets[0]; // = [b,a,e,f] + fliptets[2] = baktets[1]; // = [b,a,f,d] + flip32(fliptets, 1, &fc); + // Put the "outer" link faces into check list. + // fliptets[0] = [d,e,f,a] + // fliptets[1] = [e,d,f,b] + // Faces [a,b,d] and [a,b,e] are not "outer" link faces. + enextself(fliptets[0]); + for (i = 1; i < 3; i++) { + esym(fliptets[0], newface); + crossfaces->newindex((void **) &parytet); + *parytet = newface; + enextself(fliptets[0]); + } + enextself(fliptets[1]); + for (i = 1; i < 3; i++) { + esym(fliptets[1], newface); + crossfaces->newindex((void **) &parytet); + *parytet = newface; + enextself(fliptets[1]); + } + flip23count--; + flip32count--; + flip44count++; + flipflag = 1; } 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); + //n == 4, convflag != 0; assert(0); } + } else { + // n > 4 => unflipable. //assert(0); } - 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); + // There are more than 1 non-convex or coplanar cases. + flipflag = -1; // Ignore this face. + if (b->verbose > 2) { + printf(" Ignore face (%d, %d, %d) - %d, %d, tau = %.17g\n", + pointmark(bface.forg), pointmark(bface.fdest), + pointmark(bface.fapex), pointmark(bface.foppo), + pointmark(bface.noppo), bface.key); + } + } // if (convcount == 1) + + if (flipflag == 1) { + // Update the priority queue. + for (i = 0; i < crossfaces->objects; i++) { + parytet = (triface *) fastlookup(crossfaces, i); + flipcertify(parytet, &pqueue, plane_pa, plane_pb, plane_pc); + } + crossfaces->restart(); + if (1) { // if (!b->flipinsert_random) { + // Insert all queued unflipped faces. + for (i = 0; i < bfacearray->objects; i++) { + parytet = (triface *) fastlookup(bfacearray, i); + // This face may be changed. + if (!isdeadtet(*parytet)) { + flipcertify(parytet, &pqueue, plane_pa, plane_pb, plane_pc); + } + } + bfacearray->restart(); + } + fcount++; + } else if (flipflag == 0) { + // Queue an unflippable face. To process it later. + bfacearray->newindex((void **) &parytet); + *parytet = fliptet; } - // This side becomes hull. Update the handle in dummytet. - dummytet[0] = encode(neighbor); - } - } + } // if (pe == bface.noppo) + } // if ((pa == bface.forg) && ...) + } // if (bface.tt != NULL) + + } // while (pqueue != NULL) + + if (bfacearray->objects > 0) { + if (fcount == 0) { + printf("!! No flip is found in %ld faces.\n", bfacearray->objects); + assert(0); } - // 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. // -// // -// 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. // -// // -/////////////////////////////////////////////////////////////////////////////// + // 'bfacearray' may be not empty (for what reason ??). + //dbg_unflip_facecount += bfacearray->objects; -void tetgenmesh:: -regionplague(memorypool *regionviri, REAL attribute, REAL volume) -{ - tetrahedron **virusloop; - tetrahedron **regiontet; - triface testtet, neighbor; - face neighsh; + assert(flippool->items == 0l); + delete bfacearray; - 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; - } - } - // 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"); + // Un-mark top and bottom points. + for (i = 0; i < toppoints->objects; i++) { + parypt = (point *) fastlookup(toppoints, i); + punmarktest2(*parypt); + } + for (i = 0; i < botpoints->objects; i++) { + parypt = (point *) fastlookup(botpoints, i); + punmarktest3(*parypt); } - regionviri->traversalinit(); - virusloop = (tetrahedron **) regionviri->traverse(); - while (virusloop != (tetrahedron **) NULL) { - testtet.tet = *virusloop; - uninfect(testtet); - virusloop = (tetrahedron **) regionviri->traverse(); + + f23count = flip23count - f23count; + f32count = flip32count - f32count; + f44count = flip44count - f44count; + totalfcount = f23count + f32count + f44count; + if (b->verbose > 2) { + printf(" Total %ld flips. f23(%ld), f32(%ld), f44(%ld).\n", + totalfcount, f23count, f32count, f44count); } - // Empty the virus pool. - regionviri->restart(); } /////////////////////////////////////////////////////////////////////////////// // // -// removeholetets() Remove tetrahedra which are outside the domain. // +// fillregion() Fill the missing region by a set of new subfaces. // +// // +// 'missingshs' contains the list of subfaces in R. Moreover, each subface // +// (except the first one) in this list represents an interior edge of R. // +// // +// Note: We assume that all vertices of R are marktested so we can detect // +// new subface by checking the flag in apexes. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::removeholetets(memorypool* viri) +bool tetgenmesh::fillregion(arraypool* missingshs, arraypool* missingshbds, + arraypool* newshs) { - tetrahedron **virusloop; - triface testtet, neighbor; - point checkpt; - int *tetspernodelist; + badface *newflipface, *popface; + triface searchtet, spintet, neightet; + face oldsh, newsh, opensh, *parysh; + face casout, casin, neighsh, checksh; + face neighseg, checkseg; + point pc; + int success; + int t1ver; 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(); - } - - 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++; + // Search the first new subface to fill the region. + for (i = 0; i < missingshbds->objects; i++) { + parysh = (face *) fastlookup(missingshbds, i); + sspivot(*parysh, neighseg); + sstpivot1(neighseg, searchtet); + j = 0; // Count the number of passes of R. + spintet = searchtet; + while (1) { + pc = apex(spintet); + if (pmarktested(pc)) { + neightet = spintet; + j++; } + fnextself(spintet); + if (spintet.tet == searchtet.tet) break; } - // 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++; - } - } + assert(j >= 1); + if (j == 1) { + // Found an interior new subface. + searchtet = neightet; + oldsh = *parysh; + break; } - // 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. // -// // -// 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::assignregionattribs() -{ - list *regionnumlist; - list *regiontetlist; - triface tetloop, regiontet, neightet; - face checksh; - bool flag; - int regionnum, num; - int attridx, count; - int i; + } // i - if (b->verbose > 1) { - printf(" Assign region numbers.\n"); + if (i == missingshbds->objects) { + // Failed to find any interior subface. + // Need Steiner points. + return false; } - regionnumlist = new list(sizeof(int), NULL, 256); - regiontetlist = new list(sizeof(triface), NULL, 1024); - attridx = in->numberoftetrahedronattributes; + makeshellface(subfaces, &newsh); + setsorg(newsh, org(searchtet)); + setsdest(newsh, dest(searchtet)); + setsapex(newsh, apex(searchtet)); + // The new subface gets its markers from the old one. + setshellmark(newsh, shellmark(oldsh)); + if (checkconstraints) { + setareabound(newsh, areabound(oldsh)); + } + // Connect the new subface to adjacent tets. + tsbond(searchtet, newsh); + fsymself(searchtet); + sesymself(newsh); + tsbond(searchtet, newsh); + // Connect newsh to outer subfaces. + sspivot(oldsh, checkseg); + if (sinfected(checkseg)) { + // It's a faked segment. Delete it. + spintet = searchtet; + while (1) { + tssdissolve1(spintet); + fnextself(spintet); + if (spintet.tet == searchtet.tet) break; + } + shellfacedealloc(subsegs, checkseg.sh); + ssdissolve(oldsh); + checkseg.sh = NULL; + } + spivot(oldsh, casout); + if (casout.sh != NULL) { + casin = casout; + if (checkseg.sh != NULL) { + // Make sure that the subface has the right ori at the segment. + checkseg.shver = 0; + if (sorg(newsh) != sorg(checkseg)) { + sesymself(newsh); + } + spivot(casin, neighsh); + while (neighsh.sh != oldsh.sh) { + casin = neighsh; + spivot(casin, neighsh); + } + } + sbond1(newsh, casout); + sbond1(casin, newsh); + } + if (checkseg.sh != NULL) { + ssbond(newsh, checkseg); + } + // Add this new subface into list. + sinfect(newsh); + newshs->newindex((void **) &parysh); + *parysh = newsh; - // 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); - } + // Push two "open" side of the new subface into stack. + for (i = 0; i < 2; i++) { + senextself(newsh); + newflipface = (badface *) flippool->alloc(); + newflipface->ss = newsh; + newflipface->nextitem = flipstack; + flipstack = newflipface; + } + + success = 1; + + // Loop until 'flipstack' is empty. + while ((flipstack != NULL) && success) { + // Pop an "open" side from the stack. + popface = flipstack; + opensh = popface->ss; + flipstack = popface->nextitem; // The next top item in stack. + flippool->dealloc((void *) popface); + + // opensh is either (1) an interior edge or (2) a bdry edge. + stpivot(opensh, searchtet); + tsspivot1(searchtet, checkseg); + if (checkseg.sh == NULL) { + // No segment. It is an interior edge of R. + // Search for a new face in R. + spintet = searchtet; + fnextself(spintet); // Skip the current face. + while (1) { + pc = apex(spintet); + if (pmarktested(pc)) { + // 'opensh' is an interior edge. + if (!issubface(spintet)) { + // Create a new subface. + makeshellface(subfaces, &newsh); + setsorg(newsh, org(spintet)); + setsdest(newsh, dest(spintet)); + setsapex(newsh, pc); + // The new subface gets its markers from its neighbor. + setshellmark(newsh, shellmark(opensh)); + if (checkconstraints) { + setareabound(newsh, areabound(opensh)); } + // Connect the new subface to adjacent tets. + tsbond(spintet, newsh); + fsymself(spintet); + sesymself(newsh); + tsbond(spintet, newsh); + // Connect newsh to its adjacent subface. + sbond(newsh, opensh); + // Add this new subface into list. + sinfect(newsh); + newshs->newindex((void **) &parysh); + *parysh = newsh; + // Push two "open" side of the new subface into stack. + for (i = 0; i < 2; i++) { + senextself(newsh); + newflipface = (badface *) flippool->alloc(); + newflipface->ss = newsh; + newflipface->nextitem = flipstack; + flipstack = newflipface; + } + } else { + // Connect to another open edge. + tspivot(spintet, checksh); + sbond(opensh, checksh); } + break; + } // if (pmarktested(pc)) + fnextself(spintet); + if (spintet.tet == searchtet.tet) { + // Not find any face to fill in R at this side. + // Suggest a point to split the edge. + success = 0; + break; } - // 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); + } // while (1) + } else { + // This side coincident with a boundary edge of R. + checkseg.shver = 0; + spivot(checkseg, oldsh); + if (sinfected(checkseg)) { + // It's a faked segment. Delete it. + spintet = searchtet; + while (1) { + tssdissolve1(spintet); + fnextself(spintet); + if (spintet.tet == searchtet.tet) break; } - if (!flag) regionnumlist->append(®ionnum); - // Clear list for the next region. - regiontetlist->clear(); + shellfacedealloc(subsegs, checkseg.sh); + ssdissolve(oldsh); + checkseg.sh = NULL; } - } - tetloop.tet = tetrahedrontraverse(); - } - - if (b->verbose) { - printf(" %d user-specified regions.\n", regionnumlist->len()); - } + spivot(oldsh, casout); + if (casout.sh != NULL) { + casin = casout; + if (checkseg.sh != NULL) { + // Make sure that the subface has the right ori at the segment. + checkseg.shver = 0; + if (sorg(opensh) != sorg(checkseg)) { + sesymself(opensh); + } + spivot(casin, neighsh); + while (neighsh.sh != oldsh.sh) { + casin = neighsh; + spivot(casin, neighsh); + } + } + sbond1(opensh, casout); + sbond1(casin, opensh); + } + if (checkseg.sh != NULL) { + ssbond(opensh, checkseg); + } + } // if (checkseg.sh != NULL) + } // while ((flipstack != NULL) && success) - // 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); + if (success) { + // Uninfect all new subfaces. + for (i = 0; i < newshs->objects; i++) { + parysh = (face *) fastlookup(newshs, i); + suninfect(*parysh); + } + // Delete old subfaces. + for (i = 0; i < missingshs->objects; i++) { + parysh = (face *) fastlookup(missingshs, i); + shellfacedealloc(subfaces, parysh->sh); + } + fillregioncount++; + } else { + // Failed to fill the region. + // Re-connect old subfaces at boundaries of R. + // Also delete fake segments. + for (i = 0; i < missingshbds->objects; i++) { + parysh = (face *) fastlookup(missingshbds, i); + // It still connect to 'casout'. + // Re-connect 'casin' to it. + spivot(*parysh, casout); + casin = casout; + spivot(casin, neighsh); + while (1) { + if (sinfected(neighsh)) break; + if (neighsh.sh == parysh->sh) break; + casin = neighsh; + spivot(casin, neighsh); + } + if (sinfected(neighsh)) { + sbond1(casin, *parysh); + } + sspivot(*parysh, checkseg); + if (checkseg.sh != NULL) { + if (checkseg.sh[3] != NULL) { + if (sinfected(checkseg)) { + sstpivot1(checkseg, searchtet); + spintet = searchtet; + while (1) { + tssdissolve1(spintet); + fnextself(spintet); + if (spintet.tet == searchtet.tet) break; } + ssdissolve(*parysh); + shellfacedealloc(subsegs, checkseg.sh); } } } - regiontetlist->clear(); - regionnum++; // The next region number. } - tetloop.tet = tetrahedrontraverse(); - } + // Delete all new subfaces. + for (i = 0; i < newshs->objects; i++) { + parysh = (face *) fastlookup(newshs, i); + shellfacedealloc(subfaces, parysh->sh); + } + // Clear the flip pool. + flippool->restart(); + flipstack = NULL; - // 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) { - printf(" %d regions are numbered.\n", count); + // Choose an interior edge of R to split. + assert(missingshs->objects > 1); + // Skip the first subface in 'missingshs'. + i = randomnation(missingshs->objects - 1) + 1; + parysh = (face *) fastlookup(missingshs, i); + recentsh = *parysh; } - delete regionnumlist; - delete regiontetlist; + newshs->restart(); + + return success; } /////////////////////////////////////////////////////////////////////////////// // // -// 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. // -// // -// This routine mainly calls other routines to carry out all these functions.// +// insertpoint_cdt() Insert a new point into a CDT. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::carveholes() +int tetgenmesh::insertpoint_cdt(point newpt, triface *searchtet, face *splitsh, + face *splitseg, insertvertexflags *ivf, + arraypool *cavpoints, arraypool *cavfaces, + arraypool *cavshells, arraypool *newtets, + arraypool *crosstets, arraypool *misfaces) { - memorypool *holeviri, *regionviri; - tetrahedron *tptr, **holetet, **regiontet; - triface searchtet, *holetets, *regiontets; - enum locateresult intersect; + triface neightet, *parytet; + face checksh, *parysh, *parysh1; + face *paryseg, *paryseg1; + point *parypt; + int t1ver; int i; - if (!b->quiet) { - printf("Removing exterior tetrahedra.\n"); - if ((b->verbose > 1) && (in->numberofholes > 0)) { - printf(" Marking holes for elimination.\n"); - } + if (b->verbose > 2) { + printf(" Insert point %d into CDT\n", pointmark(newpt)); } - // 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); + if (!insertpoint(newpt, searchtet, NULL, NULL, ivf)) { + // Point is not inserted. Check ivf->iloc for reason. + return 0; + } - 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; - } - - // 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(); - } + + for (i = 0; i < cavetetvertlist->objects; i++) { + cavpoints->newindex((void **) &parypt); + *parypt = * (point *) fastlookup(cavetetvertlist, i); } + // Add the new point into the point list. + cavpoints->newindex((void **) &parypt); + *parypt = newpt; - 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"); + for (i = 0; i < cavebdrylist->objects; i++) { + cavfaces->newindex((void **) &parytet); + *parytet = * (triface *) fastlookup(cavebdrylist, i); + } + + for (i = 0; i < caveoldtetlist->objects; i++) { + crosstets->newindex((void **) &parytet); + *parytet = * (triface *) fastlookup(caveoldtetlist, i); + } + + cavetetvertlist->restart(); + cavebdrylist->restart(); + caveoldtetlist->restart(); + + // Insert the point using the cavity algorithm. + delaunizecavity(cavpoints, cavfaces, cavshells, newtets, crosstets, + misfaces); + fillcavity(cavshells, NULL, NULL, NULL, NULL, NULL, NULL); + carvecavity(crosstets, newtets, NULL); + + if ((splitsh != NULL) || (splitseg != NULL)) { + // Insert the point into the surface mesh. + sinsertvertex(newpt, splitsh, splitseg, ivf->sloc, ivf->sbowywat, 0); + + // Put all new subfaces into stack. + for (i = 0; i < caveshbdlist->objects; i++) { + // Get an old subface at edge [a, b]. + parysh = (face *) fastlookup(caveshbdlist, i); + spivot(*parysh, checksh); // The new subface [a, b, p]. + // Do not recover a deleted new face (degenerated). + if (checksh.sh[3] != NULL) { + subfacstack->newindex((void **) &parysh); + *parysh = checksh; } } - // 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. - } - } - } - // Free up memory. - delete [] regiontets; - delete regionviri; - } - // Now acutually remove the outside and hole tets. - removeholetets(holeviri); - // The mesh is nonconvex now. - nonconvex = 1; + if (splitseg != NULL) { + // Queue two new subsegments in C(p) for recovery. + for (i = 0; i < cavesegshlist->objects; i++) { + paryseg = (face *) fastlookup(cavesegshlist, i); + subsegstack->newindex((void **) &paryseg1); + *paryseg1 = *paryseg; + } + } // if (splitseg != NULL) + + // Delete the old subfaces in sC(p). + for (i = 0; i < caveshlist->objects; i++) { + parysh = (face *) fastlookup(caveshlist, i); + if (checksubfaceflag) { + // It is possible that this subface still connects to adjacent + // tets which are not in C(p). If so, clear connections in the + // adjacent tets at this subface. + stpivot(*parysh, neightet); + if (neightet.tet != NULL) { + if (neightet.tet[4] != NULL) { + // Found an adjacent tet. It must be not in C(p). + assert(!infected(neightet)); + tsdissolve(neightet); + fsymself(neightet); + assert(!infected(neightet)); + tsdissolve(neightet); + } + } + } + shellfacedealloc(subfaces, parysh->sh); + } + if (splitseg != NULL) { + // Delete the old segment in sC(p). + shellfacedealloc(subsegs, splitseg->sh); + } - // Update the point-to-tet map. - makepoint2tetmap(); + // Clear working lists. + caveshlist->restart(); + caveshbdlist->restart(); + cavesegshlist->restart(); + } // if ((splitsh != NULL) || (splitseg != NULL)) - if (b->regionattrib) { - if (b->regionattrib > 1) { - // -AA switch. Assign each tet a region number (> 0). - assignregionattribs(); + // Put all interior subfaces into stack for recovery. + // They were collected in carvecavity(). + // Note: Some collected subfaces may be deleted by sinsertvertex(). + for (i = 0; i < caveencshlist->objects; i++) { + parysh = (face *) fastlookup(caveencshlist, i); + if (parysh->sh[3] != NULL) { + subfacstack->newindex((void **) &parysh1); + *parysh1 = *parysh; } - // Note the fact that each tetrahedron has an additional attribute. - in->numberoftetrahedronattributes++; } - // Free up memory. - delete holeviri; -} + // Put all interior segments into stack for recovery. + // They were collected in carvecavity(). + // Note: Some collected segments may be deleted by sinsertvertex(). + for (i = 0; i < caveencseglist->objects; i++) { + paryseg = (face *) fastlookup(caveencseglist, i); + if (paryseg->sh[3] != NULL) { + subsegstack->newindex((void **) &paryseg1); + *paryseg1 = *paryseg; + } + } -//// //// -//// //// -//// constrained_cxx ////////////////////////////////////////////////////////// + caveencshlist->restart(); + caveencseglist->restart(); -//// steiner_cxx ////////////////////////////////////////////////////////////// -//// //// -//// //// + return 1; +} /////////////////////////////////////////////////////////////////////////////// // // -// initializecavity() Initialize the cavity. // +// refineregion() Refine a missing region by inserting points. // // // -// 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()). // +// 'splitsh' represents an edge of the facet to be split. It must be not a // +// segment. // // -// 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. // +// Assumption: The current mesh is a CDT and is convex. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::initializecavity(list* floorlist, list* ceillist, - list* frontlist, list *ptlist, list* glueshlist) +void tetgenmesh::refineregion(face &splitsh, arraypool *cavpoints, + arraypool *cavfaces, arraypool *cavshells, + arraypool *newtets, arraypool *crosstets, + arraypool *misfaces) { - triface neightet, casingtet; - triface faketet; - face worksh; - point *ppt; - int i, j; + triface searchtet, spintet; + face splitseg, *paryseg; + point steinpt, pa, pb, refpt; + insertvertexflags ivf; + enum interresult dir; + long baknum = points->items; + int t1ver; + int i; - // Infect all points of the re-triangulated cavity. - for (i = 0; i < ptlist->len(); i++) { - ppt = (point *)(* ptlist)[i]; - pinfect(*ppt); - } - - // 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); - } - } - // 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); + if (b->verbose > 2) { + printf(" Refining region at edge (%d, %d, %d).\n", + pointmark(sorg(splitsh)), pointmark(sdest(splitsh)), + pointmark(sapex(splitsh))); + } + + // Add the Steiner point at the barycenter of the face. + pa = sorg(splitsh); + pb = sdest(splitsh); + // Create a new point. + makepoint(&steinpt, FREEFACETVERTEX); + for (i = 0; i < 3; i++) { + steinpt[i] = 0.5 * (pa[i] + pb[i]); + } + + ivf.bowywat = 1; // Use the Bowyer-Watson algorrithm. + ivf.cdtflag = 1; // Only create the initial cavity. + ivf.sloc = (int) ONEDGE; + ivf.sbowywat = 1; + ivf.assignmeshsize = b->metric; + + point2tetorg(pa, searchtet); // Start location from it. + ivf.iloc = (int) OUTSIDE; + + ivf.rejflag = 1; // Reject it if it encroaches upon any segment. + if (!insertpoint_cdt(steinpt, &searchtet, &splitsh, NULL, &ivf, cavpoints, + cavfaces, cavshells, newtets, crosstets, misfaces)) { + if (ivf.iloc == (int) ENCSEGMENT) { + pointdealloc(steinpt); + // Split an encroached segment. + assert(encseglist->objects > 0); + i = randomnation(encseglist->objects); + paryseg = (face *) fastlookup(encseglist, i); + splitseg = *paryseg; + encseglist->restart(); + + // Split the segment. + pa = sorg(splitseg); + pb = sdest(splitseg); + // Create a new point. + makepoint(&steinpt, FREESEGVERTEX); + for (i = 0; i < 3; i++) { + steinpt[i] = 0.5 * (pa[i] + pb[i]); + } + point2tetorg(pa, searchtet); + ivf.iloc = (int) OUTSIDE; + ivf.rejflag = 0; + if (!insertpoint_cdt(steinpt, &searchtet, &splitsh, &splitseg, &ivf, + cavpoints, cavfaces, cavshells, newtets, + crosstets, misfaces)) { + assert(0); + } + st_segref_count++; + if (steinerleft > 0) steinerleft--; } else { - frontlist->append(&casingtet); + assert(0); } + } else { + st_facref_count++; + if (steinerleft > 0) steinerleft--; } - // Uninfect all points of the re-triangulated cavity. - for (i = 0; i < ptlist->len(); i++) { - ppt = (point *)(* ptlist)[i]; - puninfect(*ppt); + while (subsegstack->objects > 0l) { + // seglist is used as a stack. + subsegstack->objects--; + paryseg = (face *) fastlookup(subsegstack, subsegstack->objects); + splitseg = *paryseg; + + // Check if this segment has been recovered. + sstpivot1(splitseg, searchtet); + if (searchtet.tet != NULL) continue; + + // Search the segment. + dir = scoutsegment(sorg(splitseg), sdest(splitseg), &searchtet, &refpt, + NULL); + if (dir == SHAREEDGE) { + // Found this segment, insert it. + if (!issubseg(searchtet)) { + // Let the segment remember an adjacent tet. + sstbond1(splitseg, searchtet); + // Bond the segment to all tets containing it. + spintet = searchtet; + do { + tssbond1(spintet, splitseg); + fnextself(spintet); + } while (spintet.tet != searchtet.tet); + } else { + // Collision! Should not happen. + assert(0); + } + } else { + if ((dir == ACROSSFACE) || (dir == ACROSSEDGE)) { + // Split the segment. + // Create a new point. + makepoint(&steinpt, FREESEGVERTEX); + //setpointtype(newpt, FREESEGVERTEX); + getsteinerptonsegment(&splitseg, refpt, steinpt); + ivf.iloc = (int) OUTSIDE; + ivf.rejflag = 0; + if (!insertpoint_cdt(steinpt, &searchtet, &splitsh, &splitseg, &ivf, + cavpoints, cavfaces, cavshells, newtets, + crosstets, misfaces)) { + assert(0); + } + st_segref_count++; + if (steinerleft > 0) steinerleft--; + } else { + // Maybe a PLC problem. + assert(0); + } + } + } // while + + if (b->verbose > 2) { + printf(" Added %ld Steiner points.\n", points->items - baknum); } } /////////////////////////////////////////////////////////////////////////////// // // -// delaunizecavvertices() Form a DT of the vertices of a cavity. // -// // -// 'floorptlist' and 'ceilptlist' are the vertices of the cavity. // +// constrainedfacets() Recover constrained facets in a CDT. // // // -// 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. // +// All unrecovered subfaces are queued in 'subfacestack'. // // // /////////////////////////////////////////////////////////////////////////////// -bool tetgenmesh::delaunizecavvertices(triface* oldtet, list* floorptlist, - list* ceilptlist, list* newtetlist, queue* flipque) +void tetgenmesh::constrainedfacets() { - point *insertarray; - triface bakhulltet, newtet; - long bakhullsize; - long arraysize; - bool success; - int bakchksub; + arraypool *tg_crosstets, *tg_topnewtets, *tg_botnewtets; + arraypool *tg_topfaces, *tg_botfaces, *tg_midfaces; + arraypool *tg_topshells, *tg_botshells, *tg_facfaces; + arraypool *tg_toppoints, *tg_botpoints; + arraypool *tg_missingshs, *tg_missingshbds, *tg_missingshverts; + triface searchtet, neightet, crossedge; + face searchsh, *parysh, *parysh1; + face *paryseg; + point *parypt; + enum interresult dir; + int facetcount; + int success; + int t1ver; 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]; + // Initialize arrays. + tg_crosstets = new arraypool(sizeof(triface), 10); + tg_topnewtets = new arraypool(sizeof(triface), 10); + tg_botnewtets = new arraypool(sizeof(triface), 10); + tg_topfaces = new arraypool(sizeof(triface), 10); + tg_botfaces = new arraypool(sizeof(triface), 10); + tg_midfaces = new arraypool(sizeof(triface), 10); + tg_toppoints = new arraypool(sizeof(point), 8); + tg_botpoints = new arraypool(sizeof(point), 8); + tg_facfaces = new arraypool(sizeof(face), 10); + tg_topshells = new arraypool(sizeof(face), 10); + tg_botshells = new arraypool(sizeof(face), 10); + tg_missingshs = new arraypool(sizeof(face), 10); + tg_missingshbds = new arraypool(sizeof(face), 10); + tg_missingshverts = new arraypool(sizeof(point), 8); + // This is a global array used by refineregion(). + encseglist = new arraypool(sizeof(face), 4); + + facetcount = 0; + + while (subfacstack->objects > 0l) { + + subfacstack->objects--; + parysh = (face *) fastlookup(subfacstack, subfacstack->objects); + searchsh = *parysh; + + if (searchsh.sh[3] == NULL) continue; // It is dead. + if (isshtet(searchsh)) continue; // It is recovered. + + // Collect all unrecovered subfaces which are co-facet. + smarktest(searchsh); + tg_facfaces->newindex((void **) &parysh); + *parysh = searchsh; + for (i = 0; i < tg_facfaces->objects; i++) { + parysh = (face *) fastlookup(tg_facfaces, i); + for (j = 0; j < 3; j++) { + if (!isshsubseg(*parysh)) { + spivot(*parysh, searchsh); + assert(searchsh.sh != NULL); // SELF_CHECK + if (!smarktested(searchsh)) { + if (!isshtet(searchsh)) { + smarktest(searchsh); + tg_facfaces->newindex((void **) &parysh1); + *parysh1 = searchsh; + } + } + } + senextself(*parysh); + } // j + } // i + // Have found all facet subfaces. Unmark them. + for (i = 0; i < tg_facfaces->objects; i++) { + parysh = (face *) fastlookup(tg_facfaces, i); + sunmarktest(*parysh); } - } - // The incrflipdelaunay() is re-used. Backup global variables. - decode(dummytet[0], bakhulltet); - bakhullsize = hullsize; - bakchksub = checksubfaces; - checksubfaces = 0; - b->verbose--; + if (b->verbose > 2) { + printf(" Recovering facet #%d: %ld subfaces.\n", facetcount + 1, + tg_facfaces->objects); + } + facetcount++; - // 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); + while (tg_facfaces->objects > 0l) { - delete [] insertarray; + tg_facfaces->objects--; + parysh = (face *) fastlookup(tg_facfaces, tg_facfaces->objects); + searchsh = *parysh; - if (success) { - // Get a tet in D. - decode(dummytet[0], newtet); - newtetlist->append(&newtet); - // Get all tets of D. - retrievenewtets(newtetlist); - } + if (searchsh.sh[3] == NULL) continue; // It is dead. + if (isshtet(searchsh)) continue; // It is recovered. - // Restore global variables. - dummytet[0] = encode(bakhulltet); - hullsize = bakhullsize; - checksubfaces = bakchksub; - b->verbose++; + searchtet.tet = NULL; + dir = scoutsubface(&searchsh, &searchtet); + if (dir == SHAREFACE) continue; // The subface is inserted. + + // The subface is missing. Form the missing region. + // Re-use 'tg_crosstets' for 'adjtets'. + formregion(&searchsh, tg_missingshs, tg_missingshbds, tg_missingshverts); + + if (scoutcrossedge(searchtet, tg_missingshbds, tg_missingshs)) { + // Save this crossing edge, will be used by fillcavity(). + crossedge = searchtet; + // Form a cavity of crossing tets. + success = formcavity(&searchtet, tg_missingshs, tg_crosstets, + tg_topfaces, tg_botfaces, tg_toppoints, + tg_botpoints); + if (success) { + if (!b->flipinsert) { + // Tetrahedralize the top part. Re-use 'tg_midfaces'. + delaunizecavity(tg_toppoints, tg_topfaces, tg_topshells, + tg_topnewtets, tg_crosstets, tg_midfaces); + // Tetrahedralize the bottom part. Re-use 'tg_midfaces'. + delaunizecavity(tg_botpoints, tg_botfaces, tg_botshells, + tg_botnewtets, tg_crosstets, tg_midfaces); + // Fill the cavity with new tets. + success = fillcavity(tg_topshells, tg_botshells, tg_midfaces, + tg_missingshs, tg_topnewtets, tg_botnewtets, + &crossedge); + if (success) { + // Cavity is remeshed. Delete old tets and outer new tets. + carvecavity(tg_crosstets, tg_topnewtets, tg_botnewtets); + } else { + restorecavity(tg_crosstets, tg_topnewtets, tg_botnewtets, + tg_missingshbds); + } + } else { + // Use the flip algorithm of Shewchuk to recover the subfaces. + flipinsertfacet(tg_crosstets, tg_toppoints, tg_botpoints, + tg_missingshverts); + // Recover the missing region. + success = fillregion(tg_missingshs, tg_missingshbds, tg_topshells); + assert(success); + // Clear working lists. + tg_crosstets->restart(); + tg_topfaces->restart(); + tg_botfaces->restart(); + tg_toppoints->restart(); + tg_botpoints->restart(); + } // b->flipinsert - return success; + if (success) { + // Recover interior subfaces. + for (i = 0; i < caveencshlist->objects; i++) { + parysh = (face *) fastlookup(caveencshlist, i); + dir = scoutsubface(parysh, &searchtet); + if (dir != SHAREFACE) { + // Add this face at the end of the list, so it will be + // processed immediately. + tg_facfaces->newindex((void **) &parysh1); + *parysh1 = *parysh; + } + } + caveencshlist->restart(); + // Recover interior segments. This should always be recovered. + for (i = 0; i < caveencseglist->objects; i++) { + paryseg = (face *) fastlookup(caveencseglist, i); + dir = scoutsegment(sorg(*paryseg),sdest(*paryseg),&searchtet, + NULL, NULL); + assert(dir == SHAREEDGE); + // Insert this segment. + if (!issubseg(searchtet)) { + // Let the segment remember an adjacent tet. + sstbond1(*paryseg, searchtet); + // Bond the segment to all tets containing it. + neightet = searchtet; + do { + tssbond1(neightet, *paryseg); + fnextself(neightet); + } while (neightet.tet != searchtet.tet); + } else { + // Collision! Should not happen. + assert(0); + } + } + caveencseglist->restart(); + } // success - remesh cavity + } // success - form cavity + } else { + // Recover subfaces by retriangulate the surface mesh. + // Re-use tg_topshells for newshs. + success = fillregion(tg_missingshs, tg_missingshbds, tg_topshells); + } + + // Unmarktest all points of the missing region. + for (i = 0; i < tg_missingshverts->objects; i++) { + parypt = (point *) fastlookup(tg_missingshverts, i); + punmarktest(*parypt); + } + tg_missingshverts->restart(); + tg_missingshbds->restart(); + tg_missingshs->restart(); + + if (!success) { + // The missing region can not be recovered. Refine it. + refineregion(recentsh, tg_toppoints, tg_topfaces, tg_topshells, + tg_topnewtets, tg_crosstets, tg_midfaces); + // Clean the current list of facet subfaces. + // tg_facfaces->restart(); + } + } // while (tg_facfaces->objects) + + } // while ((subfacstack->objects) + + // Accumulate the dynamic memory. + totalworkmemory += (tg_crosstets->totalmemory + tg_topnewtets->totalmemory + + tg_botnewtets->totalmemory + tg_topfaces->totalmemory + + tg_botfaces->totalmemory + tg_midfaces->totalmemory + + tg_toppoints->totalmemory + tg_botpoints->totalmemory + + tg_facfaces->totalmemory + tg_topshells->totalmemory + + tg_botshells->totalmemory + tg_missingshs->totalmemory + + tg_missingshbds->totalmemory + + tg_missingshverts->totalmemory + + encseglist->totalmemory); + + // Delete arrays. + delete tg_crosstets; + delete tg_topnewtets; + delete tg_botnewtets; + delete tg_topfaces; + delete tg_botfaces; + delete tg_midfaces; + delete tg_toppoints; + delete tg_botpoints; + delete tg_facfaces; + delete tg_topshells; + delete tg_botshells; + delete tg_missingshs; + delete tg_missingshbds; + delete tg_missingshverts; + delete encseglist; } /////////////////////////////////////////////////////////////////////////////// // // -// 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. // +// constraineddelaunay() Create a constrained Delaunay tetrahedralization.// // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::retrievenewtets(list* newtetlist) +void tetgenmesh::constraineddelaunay(clock_t& tv) { - triface searchtet, casingtet; - int i; + face searchsh, *parysh; + face searchseg, *paryseg; + int s, i; - // 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); + // Statistics. + long bakfillregioncount; + long bakcavitycount, bakcavityexpcount; + long bakseg_ref_count; + + if (!b->quiet) { + printf("Constrained Delaunay...\n"); } - // 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); + + makesegmentendpointsmap(); + + if (b->verbose) { + printf(" Delaunizing segments.\n"); } - // 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); - } - } + + checksubsegflag = 1; + + // Put all segments into the list (in random order). + subsegs->traversalinit(); + for (i = 0; i < subsegs->items; i++) { + s = randomnation(i + 1); + // Move the s-th seg to the i-th. + subsegstack->newindex((void **) &paryseg); + *paryseg = * (face *) fastlookup(subsegstack, s); + // Put i-th seg to be the s-th. + searchseg.sh = shellfacetraverse(subsegs); + //sinfect(searchseg); // Only save it once. + paryseg = (face *) fastlookup(subsegstack, s); + *paryseg = searchseg; } - // Uninfect new tets. - for (i = 0; i < newtetlist->len(); i++) { - searchtet = * (triface *)(* newtetlist)[i]; - uninfect(searchtet); + + // Recover non-Delaunay segments. + delaunizesegments(); + + if (b->verbose) { + printf(" Inserted %ld Steiner points.\n", st_segref_count); } -} -/////////////////////////////////////////////////////////////////////////////// -// // -// 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. // -// // -// '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::insertauxsubface(triface* front, triface* idfront) -{ - triface neightet; - face auxsh; - - // 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); - } - // Let s remember f. - auxsh.sh[0] = (shellface) encode(*front); -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// scoutfront() Scout a face in D. // -// // -// Search a 'front' f in D. If f is found, return TRUE and the face of D is // -// returned in 'idfront'. Otherwise, return FALSE. // -// // -/////////////////////////////////////////////////////////////////////////////// - -bool tetgenmesh::scoutfront(triface* front, triface* idfront) -{ - 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); - - point2tetorg(pa, *idfront); - assert(org(*idfront) == pa); - recenttet = *idfront; - - // 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); - } + tv = clock(); - 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 ++; - } - } - } - if (apex(spintet) == apex(*idfront)) break; - } while (hitbdry < 2); + if (b->verbose) { + printf(" Constraining facets.\n"); } - // f is missing in D. - if (b->verbose > 1) { - printf(" Front (%d, %d, %d) is missing.\n", pointmark(pa), - pointmark(pb), pointmark(apex(*front))); - } - return false; -} + // Subfaces will be introduced. + checksubfaceflag = 1; -/////////////////////////////////////////////////////////////////////////////// -// // -// 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. // -// // -// 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. // -// // -/////////////////////////////////////////////////////////////////////////////// + bakfillregioncount = fillregioncount; + bakcavitycount = cavitycount; + bakcavityexpcount = cavityexpcount; + bakseg_ref_count = st_segref_count; -void tetgenmesh::gluefronts(triface* front, triface* front1, list* gluetetlist, - list *glueshlist) -{ - 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. - - // 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); - } - } - // Add f1 into list. - gluetetlist->append(front1); - } + // 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 **) &parysh); + *parysh = * (face *) fastlookup(subfacstack, s); + // Put i-th subface to be the s-th. + searchsh.sh = shellfacetraverse(subfaces); + parysh = (face *) fastlookup(subfacstack, s); + *parysh = searchsh; } -} -/////////////////////////////////////////////////////////////////////////////// -// // -// identifyfronts() Identify cavity faces in D. // -// // -// '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'. // -// // -/////////////////////////////////////////////////////////////////////////////// + // Recover facets. + constrainedfacets(); -bool tetgenmesh::identifyfronts(list* frontlist, list* misfrontlist, - list* gluetetlist, list* glueshlist) -{ - triface front, front1, tfront; - triface idfront, neightet; - face auxsh, checksh; - int len, i, j; - - misfrontlist->clear(); - - // 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; - } - } - 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); + if (b->verbose) { + if (fillregioncount > bakfillregioncount) { + printf(" Remeshed %ld regions.\n", fillregioncount-bakfillregioncount); + } + if (cavitycount > bakcavitycount) { + printf(" Remeshed %ld cavities", cavitycount - bakcavitycount); + if (cavityexpcount - bakcavityexpcount) { + printf(" (%ld enlarged)", cavityexpcount - bakcavityexpcount); } + printf(".\n"); + } + if (st_segref_count + st_facref_count - bakseg_ref_count > 0) { + printf(" Inserted %ld (%ld, %ld) refine points.\n", + st_segref_count + st_facref_count - bakseg_ref_count, + st_segref_count - bakseg_ref_count, st_facref_count); } } - return misfrontlist->len() == 0; } -/////////////////////////////////////////////////////////////////////////////// -// // -// 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::detachauxsubfaces(list* newtetlist) -{ - triface newtet, neightet; - face auxsh; - int i; +//// //// +//// //// +//// constrained_cxx ////////////////////////////////////////////////////////// - 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); - } - } - } -} +//// steiner_cxx ////////////////////////////////////////////////////////////// +//// //// +//// //// /////////////////////////////////////////////////////////////////////////////// // // -// 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. // +// checkflipeligibility() A call back function for boundary recovery. // // // -// 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. // +// 'fliptype' indicates which elementary flip will be performed: 1 : 2-to-3, // +// and 2 : 3-to-2, respectively. // // // -// 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. // +// 'pa, ..., pe' are the vertices involved in this flip, where [a,b,c] is // +// the flip face, and [d,e] is the flip edge. NOTE: 'pc' may be 'dummypoint',// +// other points must not be 'dummypoint'. // // // /////////////////////////////////////////////////////////////////////////////// -bool tetgenmesh::carvecavity(list* newtetlist, list* outtetlist, - list* gluetetlist, queue* flipque) +int tetgenmesh::checkflipeligibility(int fliptype, point pa, point pb, + point pc, point pd, point pe, + int level, int edgepivot, + flipconstraints* fc) { - triface newtet, neightet, front, intet, outtet, oldtet; - face auxsh, consh; - point pa, pb, pc; - point pointptr; - REAL ori; - bool success; + point tmppts[3]; + enum interresult dir; + int types[2], poss[4]; + int intflag; + int rejflag = 0; int i; - // Clear work list. - outtetlist->clear(); - success = true; - - // 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 (fc->seg[0] != NULL) { + // A constraining edge is given (e.g., for edge recovery). + if (fliptype == 1) { + // A 2-to-3 flip: [a,b,c] => [e,d,a], [e,d,b], [e,d,c]. + tmppts[0] = pa; + tmppts[1] = pb; + tmppts[2] = pc; + for (i = 0; i < 3 && !rejflag; i++) { + if (tmppts[i] != dummypoint) { + // Test if the face [e,d,#] intersects the edge. + intflag = tri_edge_test(pe, pd, tmppts[i], fc->seg[0], fc->seg[1], + NULL, 1, types, poss); + if (intflag == 2) { + // They intersect at a single point. + dir = (enum interresult) types[0]; + if (dir == ACROSSFACE) { + // The interior of [e,d,#] intersect the segment. + rejflag = 1; + } else if (dir == ACROSSEDGE) { + if (poss[0] == 0) { + // The interior of [e,d] intersect the segment. + // Since [e,d] is the newly created edge. Reject this flip. + rejflag = 1; } - if (neightet.loc < 4) { - // It is an outside tet. Add it into list. - infect(outtet); - outtetlist->append(&outtet); + } + } else if (intflag == 4) { + // They may intersect at either a point or a line segment. + dir = (enum interresult) types[0]; + if (dir == ACROSSEDGE) { + if (poss[0] == 0) { + // The interior of [e,d] intersect the segment. + // Since [e,d] is the newly created edge. Reject this flip. + rejflag = 1; } } - } else { - 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; + } // if (tmppts[0] != dummypoint) + } // i + } else if (fliptype == 2) { + // A 3-to-2 flip: [e,d,a], [e,d,b], [e,d,c] => [a,b,c] + if (pc != dummypoint) { + // Check if the new face [a,b,c] intersect the edge in its interior. + intflag = tri_edge_test(pa, pb, pc, fc->seg[0], fc->seg[1], NULL, + 1, types, poss); + if (intflag == 2) { + // They intersect at a single point. + dir = (enum interresult) types[0]; + if (dir == ACROSSFACE) { + // The interior of [a,b,c] intersect the segment. + rejflag = 1; // Do not flip. } + } else if (intflag == 4) { + // [a,b,c] is coplanar with the edge. + dir = (enum interresult) types[0]; + if (dir == ACROSSEDGE) { + // The boundary of [a,b,c] intersect the segment. + rejflag = 1; // Do not flip. + } + } + } // if (pc != dummypoint) + } + } // if (fc->seg[0] != NULL) + + if ((fc->fac[0] != NULL) && !rejflag) { + // A constraining face is given (e.g., for face recovery). + if (fliptype == 1) { + // A 2-to-3 flip. + // Test if the new edge [e,d] intersects the face. + intflag = tri_edge_test(fc->fac[0], fc->fac[1], fc->fac[2], pe, pd, + NULL, 1, types, poss); + if (intflag == 2) { + // They intersect at a single point. + dir = (enum interresult) types[0]; + if (dir == ACROSSFACE) { + rejflag = 1; + } else if (dir == ACROSSEDGE) { + rejflag = 1; + } + } else if (intflag == 4) { + // The edge [e,d] is coplanar with the face. + // There may be two intersections. + for (i = 0; i < 2 && !rejflag; i++) { + dir = (enum interresult) types[i]; + if (dir == ACROSSFACE) { + rejflag = 1; + } else if (dir == ACROSSEDGE) { + rejflag = 1; + } + } + } + } // if (fliptype == 1) + } // if (fc->fac[0] != NULL) + + if ((fc->remvert != NULL) && !rejflag) { + // The vertex is going to be removed. Do not create a new edge which + // contains this vertex. + if (fliptype == 1) { + // A 2-to-3 flip. + if ((pd == fc->remvert) || (pe == fc->remvert)) { + rejflag = 1; + } + } + } + + if (fc->remove_large_angle && !rejflag) { + // Remove a large dihedral angle. Do not create a new small angle. + REAL cosmaxd = 0, diff; + if (fliptype == 1) { + // We assume that neither 'a' nor 'b' is dummypoint. + assert((pa != dummypoint) && (pb != dummypoint)); // SELF_CHECK + // A 2-to-3 flip: [a,b,c] => [e,d,a], [e,d,b], [e,d,c]. + // The new tet [e,d,a,b] will be flipped later. Only two new tets: + // [e,d,b,c] and [e,d,c,a] need to be checked. + if ((pc != dummypoint) && (pe != dummypoint) && (pd != dummypoint)) { + // Get the largest dihedral angle of [e,d,b,c]. + tetalldihedral(pe, pd, pb, pc, NULL, &cosmaxd, NULL); + diff = cosmaxd - fc->cosdihed_in; + if (fabs(diff/fc->cosdihed_in) < b->epsilon) diff = 0.0; // Rounding. + if (diff <= 0) { //if (cosmaxd <= fc->cosdihed_in) { + rejflag = 1; } 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); + // Record the largest new angle. + if (cosmaxd < fc->cosdihed_out) { + fc->cosdihed_out = cosmaxd; + } + // Get the largest dihedral angle of [e,d,c,a]. + tetalldihedral(pe, pd, pc, pa, NULL, &cosmaxd, NULL); + diff = cosmaxd - fc->cosdihed_in; + if (fabs(diff/fc->cosdihed_in) < b->epsilon) diff = 0.0; // Rounding. + if (diff <= 0) { //if (cosmaxd <= fc->cosdihed_in) { + rejflag = 1; + } else { + // Record the largest new angle. + if (cosmaxd < fc->cosdihed_out) { + fc->cosdihed_out = cosmaxd; } - break; } } - } // for (newtet.loc) - } // if (!infected) - } - - if (!success) { - // Found inversed tet. The carvecavity failed. - for (i = 0; i < outtetlist->len(); i++) { - outtet = * (triface *)(* outtetlist)[i]; - uninfect(outtet); - } - 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); - } - } - } - } - - // 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); + } // if (pc != dummypoint && ...) + } else if (fliptype == 2) { + // A 3-to-2 flip: [e,d,a], [e,d,b], [e,d,c] => [a,b,c] + // We assume that neither 'e' nor 'd' is dummypoint. + assert((pe != dummypoint) && (pd != dummypoint)); // SELF_CHECK + if (level == 0) { + // Both new tets [a,b,c,d] and [b,a,c,e] are new tets. + if ((pa != dummypoint) && (pb != dummypoint) && (pc != dummypoint)) { + // Get the largest dihedral angle of [a,b,c,d]. + tetalldihedral(pa, pb, pc, pd, NULL, &cosmaxd, NULL); + diff = cosmaxd - fc->cosdihed_in; + if (fabs(diff/fc->cosdihed_in) < b->epsilon) diff = 0.0; // Rounding + if (diff <= 0) { //if (cosmaxd <= fc->cosdihed_in) { + rejflag = 1; + } else { + // Record the largest new angle. + if (cosmaxd < fc->cosdihed_out) { + fc->cosdihed_out = cosmaxd; + } + // Get the largest dihedral angle of [b,a,c,e]. + tetalldihedral(pb, pa, pc, pe, NULL, &cosmaxd, NULL); + diff = cosmaxd - fc->cosdihed_in; + if (fabs(diff/fc->cosdihed_in) < b->epsilon) diff = 0.0;// Rounding + if (diff <= 0) { //if (cosmaxd <= fc->cosdihed_in) { + rejflag = 1; + } else { + // Record the largest new angle. + if (cosmaxd < fc->cosdihed_out) { + fc->cosdihed_out = cosmaxd; + } + } + } } - // Detach n -x-> t. - dissolve(neightet); - } - } - // 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); - } - // 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 { // level > 0 + assert(edgepivot != 0); + if (edgepivot == 1) { + // The new tet [a,b,c,d] will be flipped. Only check [b,a,c,e]. + if ((pa != dummypoint) && (pb != dummypoint) && (pc != dummypoint)) { + // Get the largest dihedral angle of [b,a,c,e]. + tetalldihedral(pb, pa, pc, pe, NULL, &cosmaxd, NULL); + diff = cosmaxd - fc->cosdihed_in; + if (fabs(diff/fc->cosdihed_in) < b->epsilon) diff = 0.0;// Rounding + if (diff <= 0) { //if (cosmaxd <= fc->cosdihed_in) { + rejflag = 1; + } else { + // Record the largest new angle. + if (cosmaxd < fc->cosdihed_out) { + fc->cosdihed_out = cosmaxd; + } + } + } } 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)); - } - // 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); + assert(edgepivot == 2); + // The new tet [b,a,c,e] will be flipped. Only check [a,b,c,d]. + if ((pa != dummypoint) && (pb != dummypoint) && (pc != dummypoint)) { + // Get the largest dihedral angle of [b,a,c,e]. + tetalldihedral(pa, pb, pc, pd, NULL, &cosmaxd, NULL); + diff = cosmaxd - fc->cosdihed_in; + if (fabs(diff/fc->cosdihed_in) < b->epsilon) diff = 0.0;// Rounding + if (diff <= 0) { //if (cosmaxd <= fc->cosdihed_in) { + rejflag = 1; + } else { + // Record the largest new angle. + if (cosmaxd < fc->cosdihed_out) { + fc->cosdihed_out = cosmaxd; + } + } + } + } // edgepivot + } // level } - pointptr = org(newtet); - setpoint2tet(pointptr, encode(newtet)); - pointptr = dest(newtet); - setpoint2tet(pointptr, encode(newtet)); - pointptr = apex(newtet); - setpoint2tet(pointptr, encode(newtet)); } - return true; + return rejflag; } /////////////////////////////////////////////////////////////////////////////// // // -// replacepolygonsubs() Substitute the subfaces of a polygon. // +// removeedgebyflips() Remove an edge by flips. // // // -// '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. // +// 'flipedge' is a non-convex or flat edge [a,b,#,#] to be removed. // // // -// 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. // +// The return value is a positive integer, it indicates whether the edge is // +// removed or not. A value "2" means the edge is removed, otherwise, the // +// edge is not removed and the value (must >= 3) is the current number of // +// tets in the edge star. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::replacepolygonsubs(list* oldshlist, list* newshlist) +int tetgenmesh::removeedgebyflips(triface *flipedge, flipconstraints* fc) { - face newsh, oldsh, spinsh; - face casingout, casingin; - face checkseg; - point pa, pb; - int i, j, k, l; - - 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); + triface *abtets, spintet; + int t1ver; + int n, nn, i; + + + if (checksubsegflag) { + // Do not flip a segment. + if (issubseg(*flipedge)) { + if (fc->collectencsegflag) { + face checkseg, *paryseg; + tsspivot1(*flipedge, checkseg); + if (!sinfected(checkseg)) { + // Queue this segment in list. + sinfect(checkseg); + caveencseglist->newindex((void **) &paryseg); + *paryseg = checkseg; } } - // Go to the next edge of s. - senextself(newsh); + return 0; } } -} -/////////////////////////////////////////////////////////////////////////////// -// // -// orientnewsubs() Orient new subfaces facing to the inside of cavity. // -// // -// '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'. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::orientnewsubs(list* newshlist, face* orientsh, REAL* norm) -{ - face *newsh; - point pa, pb, pc; - REAL ref[3], ori, len, l; - int i; + // Count the number of tets at edge [a,b]. + n = 0; + spintet = *flipedge; + while (1) { + n++; + fnextself(spintet); + if (spintet.tet == flipedge->tet) break; + } + assert(n >= 3); - // 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]; - - // 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->flipstarsize > 0) && (n > b->flipstarsize)) { + // The star size exceeds the limit. + return 0; // Do not flip it. } -} -/////////////////////////////////////////////////////////////////////////////// -// // -// 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. // -// // -// Depending on the flip type, not all input points are used, those unused // -// points are set to be dummypoint for easily processing. // -// // -/////////////////////////////////////////////////////////////////////////////// + // Allocate spaces. + abtets = new triface[n]; + // Collect the tets at edge [a,b]. + spintet = *flipedge; + i = 0; + while (1) { + abtets[i] = spintet; + setelemcounter(abtets[i], 1); + i++; + fnextself(spintet); + if (spintet.tet == flipedge->tet) break; + } -bool tetgenmesh::registerelemflip(enum fliptype ft, point pa1, point pb1, - point pc1, point pa2, point pb2, point pc2) -{ - elemflip *ef; - bool rflag; - int i; - rflag = false; // The flip has not registed yet. + // Try to flip the edge (level = 0, edgepivot = 0). + nn = flipnm(abtets, n, 0, 0, fc); - pinfect(pa1); - pinfect(pb1); - pinfect(pc1); - pinfect(pa2); - pinfect(pb2); - pinfect(pc2); - // 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 (nn > 2) { + // Edge is not flipped. Unmarktest the remaining tets in Star(ab). + for (i = 0; i < nn; i++) { + setelemcounter(abtets[i], 0); } + // Restore the input edge (needed by Lawson's flip). + *flipedge = abtets[0]; } - puninfect(pa1); - puninfect(pb1); - puninfect(pc1); - puninfect(pa2); - puninfect(pb2); - puninfect(pc2); - - 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)); - } - return false; - } + // Release the temporary allocated spaces. + // NOTE: fc->unflip must be 0. + int bakunflip = fc->unflip; + fc->unflip = 0; + flipnm_post(abtets, n, nn, 0, fc); + fc->unflip = bakunflip; - // 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; + delete [] abtets; - return true; + return nn; } /////////////////////////////////////////////////////////////////////////////// // // -// check4fixededge() Check if the given edge [a, b] is a fixed edge. // +// removefacebyflips() Remove a face by flips. // +// // +// Return 1 if the face is removed. Otherwise, return 0. // // // -// A fixed edge is saved in the "fixededgelist". Return TRUE if [a, b] has // -// already existed in the list, otherwise, return FALSE. // +// ASSUMPTIONS: // +// - 'flipface' must not be a hull face. // // // /////////////////////////////////////////////////////////////////////////////// -bool tetgenmesh::check4fixededge(point pa, point pb) +int tetgenmesh::removefacebyflips(triface *flipface, flipconstraints* fc) { - point *ppt; - int i; + if (checksubfaceflag) { + if (issubface(*flipface)) { + return 0; + } + } - pinfect(pa); - pinfect(pb); + triface fliptets[3], flipedge; + point pa, pb, pc, pd, pe; + REAL ori; + int reducflag = 0; - 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)); + fliptets[0] = *flipface; + fsym(*flipface, fliptets[1]); + pa = org(fliptets[0]); + pb = dest(fliptets[0]); + pc = apex(fliptets[0]); + pd = oppo(fliptets[0]); + pe = oppo(fliptets[1]); + + ori = orient3d(pa, pb, pd, pe); + if (ori > 0) { + ori = orient3d(pb, pc, pd, pe); + if (ori > 0) { + ori = orient3d(pc, pa, pd, pe); + if (ori > 0) { + // Found a 2-to-3 flip. + reducflag = 1; + } else { + eprev(*flipface, flipedge); // [c,a] } - break; // This edge already exists. + } else { + enext(*flipface, flipedge); // [b,c] } + } else { + flipedge = *flipface; // [a,b] } - puninfect(pa); - puninfect(pb); + if (reducflag) { + // A 2-to-3 flip is found. + flip23(fliptets, 0, fc); + return 1; + } else { + // Try to flip the selected edge of this face. + if (removeedgebyflips(&flipedge, fc) == 2) { + return 1; + } + } - return i < (int) fixededgelist->objects; + // Face is not removed. + return 0; } /////////////////////////////////////////////////////////////////////////////// // // -// removeedgebyflips() Remove an edge by flips. // +// recoveredge() Recover an edge in current tetrahedralization. // +// // +// If the edge is recovered, 'searchtet' returns a tet containing the edge. // +// // +// This edge may intersect a set of faces and edges in the mesh. All these // +// faces or edges are needed to be removed. // +// // +// If the parameter 'fullsearch' is set, it tries to flip any face or edge // +// that intersects the recovering edge. Otherwise, only the face or edge // +// which is visible by 'startpt' is tried. // // // /////////////////////////////////////////////////////////////////////////////// -bool tetgenmesh::removeedgebyflips(triface* remedge, int *flipcount) +int tetgenmesh::recoveredgebyflips(point startpt, point endpt, + triface* searchtet, int fullsearch) { - 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; - point pa, pb; - bool remflag, subflag; - int n, n1, m, i; //, j; - - triface newtet; // For update point-to-tet map. - point *ppt; - int j; + flipconstraints fc; + enum interresult dir; - pa = org(*remedge); - pb = dest(*remedge); + fc.seg[0] = startpt; + fc.seg[1] = endpt; + fc.checkflipeligibility = 1; - if (b->verbose > 1) { - printf(" Remove edge (%d, %d).\n", pointmark(pa), pointmark(pb)); - } + // The mainloop of the edge reocvery. + while (1) { // Loop I - // 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]))); + // Search the edge from 'startpt'. + point2tetorg(startpt, *searchtet); + dir = finddirection(searchtet, endpt); + if (dir == ACROSSVERT) { + if (dest(*searchtet) == endpt) { + return 1; // Edge is recovered. + } else { + terminatetetgen(this, 3); // // It may be a PLC problem. } - 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; - } + // The edge is missing. - 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)); + // Try to flip the first intersecting face/edge. + enextesymself(*searchtet); // Go to the opposite face. + if (dir == ACROSSFACE) { + // A face is intersected with the segment. Try to flip it. + if (removefacebyflips(searchtet, &fc)) { + continue; } - } - *flipcount = *flipcount + 1; - return true; - } - - 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); + } else if (dir == ACROSSEDGE) { + // An edge is intersected with the segment. Try to flip it. + if (removeedgebyflips(searchtet, &fc) == 2) { + continue; } - for (i = 0; i < n1; i++) { - if (!isdead(&(bftetlist[i]))) { - tetrahedrondealloc(bftetlist[i].tet); + } else { + terminatetetgen(this, 3); // It may be a PLC problem. + } + + // The edge is missing. + + if (fullsearch) { + // Try to flip one of the faces/edges which intersects the edge. + triface neightet, spintet; + point pa, pb, pc, pd; + badface bakface; + enum interresult dir1; + int types[2], poss[4], pos = 0; + int success = 0; + int t1ver; + int i, j; + + // Loop through the sequence of intersecting faces/edges from + // 'startpt' to 'endpt'. + point2tetorg(startpt, *searchtet); + dir = finddirection(searchtet, endpt); + //assert(dir != ACROSSVERT); + + // Go to the face/edge intersecting the searching edge. + enextesymself(*searchtet); // Go to the opposite face. + // This face/edge has been tried in previous step. + + while (1) { // Loop I-I + + // Find the next intersecting face/edge. + fsymself(*searchtet); + if (dir == ACROSSFACE) { + neightet = *searchtet; + j = (neightet.ver & 3); // j is the current face number. + for (i = j + 1; i < j + 4; i++) { + neightet.ver = (i % 4); + pa = org(neightet); + pb = dest(neightet); + pc = apex(neightet); + pd = oppo(neightet); // The above point. + if (tri_edge_test(pa,pb,pc,startpt,endpt, pd, 1, types, poss)) { + dir = (enum interresult) types[0]; + pos = poss[0]; + break; + } else { + dir = DISJOINT; + pos = 0; + } + } // i + // There must be an intersection face/edge. + assert(dir != DISJOINT); // SELF_CHECK + } else { + assert(dir == ACROSSEDGE); + while (1) { // Loop I-I-I + // Check the two opposite faces (of the edge) in 'searchtet'. + for (i = 0; i < 2; i++) { + if (i == 0) { + enextesym(*searchtet, neightet); + } else { + eprevesym(*searchtet, neightet); + } + pa = org(neightet); + pb = dest(neightet); + pc = apex(neightet); + pd = oppo(neightet); // The above point. + if (tri_edge_test(pa,pb,pc,startpt,endpt,pd,1, types, poss)) { + dir = (enum interresult) types[0]; + pos = poss[0]; + break; // for loop + } else { + dir = DISJOINT; + pos = 0; + } + } // i + if (dir != DISJOINT) { + // Find an intersection face/edge. + break; // Loop I-I-I + } + // No intersection. Rotate to the next tet at the edge. + fnextself(*searchtet); + } // while (1) // Loop I-I-I } - } - 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)); + + // Adjust to the intersecting edge/vertex. + for (i = 0; i < pos; i++) { + enextself(neightet); + } + + if (dir == SHAREVERT) { + // Check if we have reached the 'endpt'. + pd = org(neightet); + if (pd == endpt) { + // Failed to recover the edge. + break; // Loop I-I + } else { + // We need to further check this case. It might be a PLC problem + // or a Steiner point that was added at a bad location. + assert(0); + } + } + + // The next to be flipped face/edge. + *searchtet = neightet; + + // Bakup this face (tetrahedron). + bakface.forg = org(*searchtet); + bakface.fdest = dest(*searchtet); + bakface.fapex = apex(*searchtet); + bakface.foppo = oppo(*searchtet); + + // Try to flip this intersecting face/edge. + if (dir == ACROSSFACE) { + if (removefacebyflips(searchtet, &fc)) { + success = 1; + break; // Loop I-I + } + } else if (dir == ACROSSEDGE) { + if (removeedgebyflips(searchtet, &fc) == 2) { + success = 1; + break; // Loop I-I + } + } else { + assert(0); // A PLC problem. + } + + // The face/edge is not flipped. + if ((searchtet->tet == NULL) || + (org(*searchtet) != bakface.forg) || + (dest(*searchtet) != bakface.fdest) || + (apex(*searchtet) != bakface.fapex) || + (oppo(*searchtet) != bakface.foppo)) { + // 'searchtet' was flipped. We must restore it. + point2tetorg(bakface.forg, *searchtet); + dir1 = finddirection(searchtet, bakface.fdest); + if (dir1 == ACROSSVERT) { + assert(dest(*searchtet) == bakface.fdest); + spintet = *searchtet; + while (1) { + if (apex(spintet) == bakface.fapex) { + // Found the face. + *searchtet = spintet; + break; + } + fnextself(spintet); + if (spintet.tet == searchtet->tet) { + searchtet->tet = NULL; + break; // Not find. + } + } // while (1) + if (searchtet->tet != NULL) { + if (oppo(*searchtet) != bakface.foppo) { + fsymself(*searchtet); + if (oppo(*searchtet) != bakface.foppo) { + assert(0); // Check this case. + searchtet->tet = NULL; + break; // Not find. + } + } + } + } else { + searchtet->tet = NULL; // Not find. + } + if (searchtet->tet == NULL) { + success = 0; // This face/edge has been destroyed. + break; // Loop I-I + } } + } // while (1) // Loop I-I + + if (success) { + // One of intersecting faces/edges is flipped. + continue; } - *flipcount = *flipcount + 1; - return true; - } - } - return false; + } // if (fullsearch) + + // The edge is missing. + break; // Loop I + + } // while (1) // Loop I + + return 0; } /////////////////////////////////////////////////////////////////////////////// // // -// removefacebyflips() Remove a face by a sequence of flips. // +// add_steinerpt_in_schoenhardtpoly() Insert a Steiner point in a Schoen- // +// hardt polyhedron. // // // -// The face should not be a subface. // +// 'abtets' is an array of n tets which all share at the edge [a,b]. Let the // +// tets are [a,b,p0,p1], [a,b,p1,p2], ..., [a,b,p_(n-2),p_(n-1)]. Moreover, // +// the edge [p0,p_(n-1)] intersects all of the tets in 'abtets'. A special // +// case is that the edge [p0,p_(n-1)] is coplanar with the edge [a,b]. // +// Such set of tets arises when we want to recover an edge from 'p0' to 'p_ // +// (n-1)', and the number of tets at [a,b] can not be reduced by any flip. // // // /////////////////////////////////////////////////////////////////////////////// -bool tetgenmesh::removefacebyflips(triface* remface, int *flipcount) +int tetgenmesh::add_steinerpt_in_schoenhardtpoly(triface *abtets, int n, + int chkencflag) { - triface neightet, checkface1, checkface2; - face checksh1, checksh2; - point pa, pb, pc, pd, pe; - enum interresult dir; - REAL ori; - int types[2], poss[4]; - int i; + triface worktet, *parytet; + triface faketet1, faketet2; + point pc, pd, steinerpt; + insertvertexflags ivf; + optparameters opm; + REAL vcd[3], sampt[3], smtpt[3]; + REAL maxminvol = 0.0, minvol = 0.0, ori; + int success, maxidx = 0; + int it, 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)); - } + pc = apex(abtets[0]); // pc = p0 + pd = oppo(abtets[n-1]); // pd = p_(n-1) - // 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); - } + // Find an optimial point in edge [c,d]. It is visible by all outer faces + // of 'abtets', and it maxmizes the min volume. - 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; + // initialize the list of 2n boundary faces. + for (i = 0; i < n; i++) { + edestoppo(abtets[i], worktet); // [p_i,p_i+1,a] + cavetetlist->newindex((void **) &parytet); + *parytet = worktet; + eorgoppo(abtets[i], worktet); // [p_i+1,p_i,b] + cavetetlist->newindex((void **) &parytet); + *parytet = worktet; } - 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; - } + int N = 100; + REAL stepi = 0.01; + + // Search the point along the edge [c,d]. + for (i = 0; i < 3; i++) vcd[i] = pd[i] - pc[i]; + + // Sample N points in edge [c,d]. + for (it = 1; it < N; it++) { + for (i = 0; i < 3; i++) { + sampt[i] = pc[i] + (stepi * (double) it) * vcd[i]; + } + for (i = 0; i < cavetetlist->objects; i++) { + parytet = (triface *) fastlookup(cavetetlist, i); + ori = orient3d(dest(*parytet), org(*parytet), apex(*parytet), sampt); + if (i == 0) { + minvol = ori; + } else { + if (minvol > ori) minvol = ori; } + } // i + if (it == 1) { + maxminvol = minvol; + maxidx = it; } 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; + if (maxminvol < minvol) { + maxminvol = minvol; + maxidx = it; + } + } + } // it + + if (maxminvol <= 0) { + cavetetlist->restart(); + return 0; + } + + for (i = 0; i < 3; i++) { + smtpt[i] = pc[i] + (stepi * (double) maxidx) * vcd[i]; + } + + // Create two faked tets to hold the two non-existing boundary faces: + // [d,c,a] and [c,d,b]. + maketetrahedron(&faketet1); + setvertices(faketet1, pd, pc, org(abtets[0]), dummypoint); + cavetetlist->newindex((void **) &parytet); + *parytet = faketet1; + maketetrahedron(&faketet2); + setvertices(faketet2, pc, pd, dest(abtets[0]), dummypoint); + cavetetlist->newindex((void **) &parytet); + *parytet = faketet2; + + // Point smooth options. + opm.max_min_volume = 1; + opm.numofsearchdirs = 20; + opm.searchstep = 0.001; + opm.maxiter = 100; // Limit the maximum iterations. + opm.initval = 0.0; // Initial volume is zero. + + // Try to relocate the point into the inside of the polyhedron. + success = smoothpoint(smtpt, cavetetlist, 1, &opm); + + if (success) { + while (opm.smthiter == 100) { + // It was relocated and the prescribed maximum iteration reached. + // Try to increase the search stepsize. + opm.searchstep *= 10.0; + //opm.maxiter = 100; // Limit the maximum iterations. + opm.initval = opm.imprval; + opm.smthiter = 0; // Init. + smoothpoint(smtpt, cavetetlist, 1, &opm); } + } // if (success) + + // Delete the two faked tets. + tetrahedrondealloc(faketet1.tet); + tetrahedrondealloc(faketet2.tet); + + cavetetlist->restart(); + + if (!success) { + return 0; } - // 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); + + // Insert the Steiner point. + makepoint(&steinerpt, FREEVOLVERTEX); + for (i = 0; i < 3; i++) steinerpt[i] = smtpt[i]; + + // Insert the created Steiner point. + for (i = 0; i < n; i++) { + infect(abtets[i]); + caveoldtetlist->newindex((void **) &parytet); + *parytet = abtets[i]; + } + worktet = abtets[0]; // No need point location. + ivf.iloc = (int) INSTAR; + ivf.chkencflag = chkencflag; + ivf.assignmeshsize = b->metric; + if (ivf.assignmeshsize) { + // Search the tet containing 'steinerpt' for size interpolation. + locate(steinerpt, &(abtets[0])); + worktet = abtets[0]; + } + + // Insert the new point into the tetrahedralization T. + // Note that T is convex (nonconvex = 0). + if (insertpoint(steinerpt, &worktet, NULL, NULL, &ivf)) { + // The vertex has been inserted. + st_volref_count++; + if (steinerleft > 0) steinerleft--; + return 1; + } else { + // Not inserted. + pointdealloc(steinerpt); + return 0; + } } /////////////////////////////////////////////////////////////////////////////// // // -// recoveredgebyflips() Recover edge [a, b] by a sequence of flips. // -// // -// 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. // +// add_steinerpt_in_segment() Add a Steiner point inside a segment. // // // /////////////////////////////////////////////////////////////////////////////// -bool tetgenmesh::recoveredgebyflips(triface *searchtet,point pb,int *flipcount) +int tetgenmesh::add_steinerpt_in_segment(face* misseg, int searchlevel) { - triface remface; - point pa; + triface searchtet; + face *paryseg, candseg; + point startpt, endpt, pc, pd; + flipconstraints fc; enum interresult dir; - bool success; + REAL P[3], Q[3], tp, tq; + REAL len, smlen = 0, split = 0, split_q = 0; + int success; + int i; - pa = org(*searchtet); + startpt = sorg(*misseg); + endpt = sdest(*misseg); - if (b->verbose > 1) { - printf(" Recover edge (%d, %d)\n", pointmark(pa), pointmark(pb)); - } + fc.seg[0] = startpt; + fc.seg[1] = endpt; + fc.checkflipeligibility = 1; + fc.collectencsegflag = 1; - assert(elemfliplist->objects == 0l); + point2tetorg(startpt, searchtet); + dir = finddirection(&searchtet, endpt); + //assert(dir != ACROSSVERT); - while (1) { + // Try to flip the first intersecting face/edge. + enextesymself(searchtet); // Go to the opposite face. + + int bak_fliplinklevel = b->fliplinklevel; + b->fliplinklevel = searchlevel; + + if (dir == ACROSSFACE) { + // A face is intersected with the segment. Try to flip it. + success = removefacebyflips(&searchtet, &fc); + assert(success == 0); + } else if (dir == ACROSSEDGE) { + // An edge is intersected with the segment. Try to flip it. + success = removeedgebyflips(&searchtet, &fc); + assert(success != 2); + } else { + terminatetetgen(this, 3); // It may be a PLC problem. + } + + split = 0; + for (i = 0; i < caveencseglist->objects; i++) { + paryseg = (face *) fastlookup(caveencseglist, i); + suninfect(*paryseg); + // Calculate the shortest edge between the two lines. + pc = sorg(*paryseg); + pd = sdest(*paryseg); + tp = tq = 0; + if (linelineint(startpt, endpt, pc, pd, P, Q, &tp, &tq)) { + // Does the shortest edge lie between the two segments? + // Round tp and tq. + if ((tp > 0) && (tq < 1)) { + if (tp < 0.5) { + if (tp < (b->epsilon * 1e+3)) tp = 0.0; + } else { + if ((1.0 - tp) < (b->epsilon * 1e+3)) tp = 1.0; + } + } + if ((tp <= 0) || (tp >= 1)) continue; + if ((tq > 0) && (tq < 1)) { + if (tq < 0.5) { + if (tq < (b->epsilon * 1e+3)) tq = 0.0; + } else { + if ((1.0 - tq) < (b->epsilon * 1e+3)) tq = 1.0; + } + } + if ((tq <= 0) || (tq >= 1)) continue; + // It is a valid shortest edge. Calculate its length. + len = distance(P, Q); + if (split == 0) { + smlen = len; + split = tp; + split_q = tq; + candseg = *paryseg; + } else { + if (len < smlen) { + smlen = len; + split = tp; + split_q = tq; + candseg = *paryseg; + } + } + } + } - // Go to the intersected face, try to flip it. - enextfnext(*searchtet, remface); + caveencseglist->restart(); + b->fliplinklevel = bak_fliplinklevel; + + if (split == 0) { + // Found no crossing segment. + return 0; + } - // Try to remove this crossing face. - success = removefacebyflips(&remface, flipcount); - if (!success) break; + face splitsh; + face splitseg; + point steinerpt, *parypt; + insertvertexflags ivf; - point2tetorg(pa, *searchtet); - assert(org(*searchtet) == pa); - dir = finddirection2(searchtet, pb); - if (dir == INTERVERT) { - break; // The edge has found. + if (b->addsteiner_algo == 1) { + // Split the segment at the closest point to a near segment. + makepoint(&steinerpt, FREESEGVERTEX); + for (i = 0; i < 3; i++) { + steinerpt[i] = startpt[i] + split * (endpt[i] - startpt[i]); } + } else { // b->addsteiner_algo == 2 + for (i = 0; i < 3; i++) { + P[i] = startpt[i] + split * (endpt[i] - startpt[i]); + } + pc = sorg(candseg); + pd = sdest(candseg); + for (i = 0; i < 3; i++) { + Q[i] = pc[i] + split_q * (pd[i] - pc[i]); + } + makepoint(&steinerpt, FREEVOLVERTEX); + for (i = 0; i < 3; i++) { + steinerpt[i] = 0.5 * (P[i] + Q[i]); + } + } - } // while (1) + // We need to locate the point. Start searching from 'searchtet'. + if (split < 0.5) { + point2tetorg(startpt, searchtet); + } else { + point2tetorg(endpt, searchtet); + } + if (b->addsteiner_algo == 1) { + splitseg = *misseg; + spivot(*misseg, splitsh); + } else { + splitsh.sh = NULL; + splitseg.sh = NULL; + } + ivf.iloc = (int) OUTSIDE; + ivf.bowywat = 1; + ivf.lawson = 0; + ivf.rejflag = 0; + ivf.chkencflag = 0; + ivf.sloc = (int) ONEDGE; + ivf.sbowywat = 1; + ivf.splitbdflag = 0; + ivf.validflag = 1; + ivf.respectbdflag = 1; + ivf.assignmeshsize = b->metric; + + if (!insertpoint(steinerpt, &searchtet, &splitsh, &splitseg, &ivf)) { + pointdealloc(steinerpt); + return 0; + } - // Clear the recorded flip list. - elemfliplist->restart(); + if (b->addsteiner_algo == 1) { + // Save this Steiner point (for removal). + // Re-use the array 'subvertstack'. + subvertstack->newindex((void **) &parypt); + *parypt = steinerpt; + st_segref_count++; + } else { // b->addsteiner_algo == 2 + // Queue the segment for recovery. + subsegstack->newindex((void **) &paryseg); + *paryseg = *misseg; + st_volref_count++; + } + if (steinerleft > 0) steinerleft--; - return success; + return 1; } /////////////////////////////////////////////////////////////////////////////// // // -// recoverfacebyflips() Recover a missing face by a sequence of 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. // +// addsteiner4recoversegment() Add a Steiner point for recovering a seg. // // // /////////////////////////////////////////////////////////////////////////////// -bool tetgenmesh::recoverfacebyflips(triface* front, int *flipcount) +int tetgenmesh::addsteiner4recoversegment(face* misseg, int splitsegflag) { - triface searchtet, spintet, bdrytet; - triface remedge, remface; - point pa, pb, pc, pd, pe, *ppt; + triface *abtets, searchtet, spintet; + face splitsh; + face *paryseg; + point startpt, endpt; + point pa, pb, pd, steinerpt, *parypt; enum interresult dir; - bool success; - int hitbdry; + insertvertexflags ivf; + int types[2], poss[4]; + int n, endi, success; + int t1ver; 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); - - // First recover three edges of this face. - 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; + startpt = sorg(*misseg); + if (pointtype(startpt) == FREESEGVERTEX) { + sesymself(*misseg); + startpt = sorg(*misseg); + } + endpt = sdest(*misseg); + + // Try to recover the edge by adding Steiner points. + point2tetorg(startpt, searchtet); + dir = finddirection(&searchtet, endpt); + enextself(searchtet); + //assert(apex(searchtet) == startpt); + + if (dir == ACROSSFACE) { + // The segment is crossing at least 3 faces. Find the common edge of + // the first 3 crossing faces. + esymself(searchtet); + fsym(searchtet, spintet); + pd = oppo(spintet); + for (i = 0; i < 3; i++) { + pa = org(spintet); + pb = dest(spintet); + //pc = apex(neightet); + if (tri_edge_test(pa, pb, pd, startpt, endpt, NULL, 1, types, poss)) { + break; // Found the edge. } + enextself(spintet); + eprevself(searchtet); } - // 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; + assert(i < 3); + esymself(searchtet); + } else { + assert(dir == ACROSSEDGE); + // PLC check. + if (issubseg(searchtet)) { + face checkseg; + tsspivot1(searchtet, checkseg); + printf("Found two segments intersect each other.\n"); + pa = farsorg(*misseg); + pb = farsdest(*misseg); + printf(" 1st: [%d,%d] %d.\n", pointmark(pa), pointmark(pb), + shellmark(*misseg)); + pa = farsorg(checkseg); + pb = farsdest(checkseg); + printf(" 2nd: [%d,%d] %d.\n", pointmark(pa), pointmark(pb), + shellmark(checkseg)); + terminatetetgen(this, 3); + } + } + assert(apex(searchtet) == startpt); + + spintet = searchtet; + n = 0; endi = -1; + while (1) { + // Check if the endpt appears in the star. + if (apex(spintet) == endpt) { + endi = n; // Remember the position of endpt. } - // Save this edge to avoid flipping it away. - fixededgelist->newindex((void **) &ppt); - ppt[0] = pa; - ppt[1] = pb; - // Go to the next edge. - enextself(*front); + n++; // Count a tet in the star. + fnextself(spintet); + if (spintet.tet == searchtet.tet) break; } + assert(n >= 3); - 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); + if (endi > 0) { + // endpt is also in the edge star + // Get all tets in the edge star. + abtets = new triface[n]; + spintet = searchtet; + for (i = 0; i < n; i++) { + abtets[i] = spintet; + fnextself(spintet); } - 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 (apex(spintet) == apex(searchtet)) break; - } while (hitbdry < 2); - - if (success) break; - - 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; + success = 0; + + if (dir == ACROSSFACE) { + // Find a Steiner points inside the polyhedron. + if (add_steinerpt_in_schoenhardtpoly(abtets, endi, 0)) { + success = 1; } - // 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; + } else if (dir == ACROSSEDGE) { + if (n > 4) { + // In this case, 'abtets' is separated by the plane (containing the + // two intersecting edges) into two parts, P1 and P2, where P1 + // consists of 'endi' tets: abtets[0], abtets[1], ..., + // abtets[endi-1], and P2 consists of 'n - endi' tets: + // abtets[endi], abtets[endi+1], abtets[n-1]. + if (endi > 2) { // P1 + // There are at least 3 tets in the first part. + if (add_steinerpt_in_schoenhardtpoly(abtets, endi, 0)) { + success++; + } } - } - 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; + if ((n - endi) > 2) { // P2 + // There are at least 3 tets in the first part. + if (add_steinerpt_in_schoenhardtpoly(&(abtets[endi]), n - endi, 0)) { + success++; + } } + } else { + // In this case, a 4-to-4 flip should be re-cover the edge [c,d]. + // However, there will be invalid tets (either zero or negtive + // volume). Otherwise, [c,d] should already be recovered by the + // recoveredge() function. + terminatetetgen(this, 2); // Report a bug. } - 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; + terminatetetgen(this, 10); // A PLC problem. } - if (!success) break; + delete [] abtets; - point2tetorg(pa, searchtet); - assert(org(searchtet) == pa); - dir = finddirection2(&searchtet, pb); - assert(dest(searchtet) == pb); + if (success) { + // Add the missing segment back to the recovering list. + subsegstack->newindex((void **) &paryseg); + *paryseg = *misseg; + return 1; + } + } // if (endi > 0) - } // while (1) + if (!splitsegflag) { + return 0; + } - // Clear the recored flips. - elemfliplist->restart(); - // Clear the fixed edges. - fixededgelist->restart(); + if (b->verbose > 2) { + printf(" Splitting segment (%d, %d)\n", pointmark(startpt), + pointmark(endpt)); + } + steinerpt = NULL; - return success; -} + if (b->addsteiner_algo > 0) { // -Y/1 or -Y/2 + if (add_steinerpt_in_segment(misseg, 3)) { + return 1; + } + sesymself(*misseg); + if (add_steinerpt_in_segment(misseg, 3)) { + return 1; + } + sesymself(*misseg); + } -/////////////////////////////////////////////////////////////////////////////// -// // -// constrainedcavity() Tetrahedralize a cavity by constrained tetrahedra. // -// // -// The cavity C is bounded by faces F in 'floorlist' and 'ceillist'. 'ptlist'// -// V is the set of vertices of C. // -// // -/////////////////////////////////////////////////////////////////////////////// -bool tetgenmesh::constrainedcavity(triface* oldtet, list* floorlist, - list* ceillist, list* ptlist, list* frontlist, list* misfrontlist, - list* newtetlist, list* gluetetlist, list* glueshlist, queue* flipque) -{ - triface misfront, newtet; - point pointptr; // Used with gluetetlist. - bool success; - int misfacecount; - int flipcount, totalflipcount; - int i; - if (b->verbose > 1) { - printf(" Constrained cavity (%d floors, %d ceilings, %d vertices).\n", - floorlist->len(), ceillist->len(), ptlist->len()); - } - // Initialize the cavity C. - initializecavity(floorlist, ceillist, frontlist, ptlist, glueshlist); - // Form the D of the vertices of C. - success = delaunizecavvertices(oldtet, ptlist, NULL, newtetlist, flipque); + if (steinerpt == NULL) { + // Split the segment at its midpoint. + makepoint(&steinerpt, FREESEGVERTEX); + for (i = 0; i < 3; i++) { + steinerpt[i] = 0.5 * (startpt[i] + endpt[i]); + } + + // We need to locate the point. + assert(searchtet.tet != NULL); // Start searching from 'searchtet'. + spivot(*misseg, splitsh); + ivf.iloc = (int) OUTSIDE; + ivf.bowywat = 1; + ivf.lawson = 0; + ivf.rejflag = 0; + ivf.chkencflag = 0; + ivf.sloc = (int) ONEDGE; + ivf.sbowywat = 1; + ivf.splitbdflag = 0; + ivf.validflag = 1; + ivf.respectbdflag = 1; + ivf.assignmeshsize = b->metric; + if (!insertpoint(steinerpt, &searchtet, &splitsh, misseg, &ivf)) { + assert(0); + } + } // if (endi > 0) - 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) + // Save this Steiner point (for removal). + // Re-use the array 'subvertstack'. + subvertstack->newindex((void **) &parypt); + *parypt = steinerpt; - if (success) { - // 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; - } - } - - // { - // Fail to tetrahedralize C. - // Remove aux subfaces. - detachauxsubfaces(newtetlist); - // Remove new tets. - for (i = 0; i < newtetlist->len(); i++) { - newtet = * (triface *)(* newtetlist)[i]; - assert(!isdead(&newtet)); - tetrahedrondealloc(newtet.tet); - } - newtetlist->clear(); - // Restore faces of C in frontlist. - for (i = 0; i < misfrontlist->len(); i++) { - 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; - // } + st_segref_count++; + if (steinerleft > 0) steinerleft--; + + return 1; } /////////////////////////////////////////////////////////////////////////////// // // -// findrelocatepoint() Find new location for relocating a point. // +// recoversegments() Recover all segments. // // // -// 'frontlist' contains the boundary faces of the cavity C. Some fronts are // -// visible by 'stpt' p, some are coplanar with p. // +// All segments need to be recovered are in 'subsegstack'. // +// // +// This routine first tries to recover each segment by only using flips. If // +// no flip is possible, and the flag 'steinerflag' is set, it then tries to // +// insert Steiner points near or in the segment. // // // /////////////////////////////////////////////////////////////////////////////// -bool tetgenmesh::findrelocatepoint2(point steinpt, point relocatept, - REAL* normal, list* frontlist, list* oldtetlist) +int tetgenmesh::recoversegments(arraypool *misseglist, int fullsearch, + int steinerflag) { - triface oldtet, 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; + triface searchtet, spintet; + face sseg, *paryseg; + point startpt, endpt; + int success; + int t1ver; + long bak_inpoly_count = st_volref_count; + long bak_segref_count = st_segref_count; if (b->verbose > 1) { - printf(" Find new location for point %d.\n", pointmark(steinpt)); + printf(" Recover segments [%s level = %2d] #: %ld.\n", + (b->fliplinklevel > 0) ? "fixed" : "auto", + (b->fliplinklevel > 0) ? b->fliplinklevel : autofliplinklevel, + subsegstack->objects); } - // Calculate a far enough point on the normal direction. - for (i = 0; i < 3; i++) { - farpt[i] = steinpt[i] + longest * normal[i]; - } - - // 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]); - } - break; - } + // Loop until 'subsegstack' is empty. + while (subsegstack->objects > 0l) { + // seglist is used as a stack. + subsegstack->objects--; + paryseg = (face *) fastlookup(subsegstack, subsegstack->objects); + sseg = *paryseg; + + // Check if this segment has been recovered. + sstpivot1(sseg, searchtet); + if (searchtet.tet != NULL) { + continue; // Not a missing segment. } - } - // There must be an interseced face. - if (i == oldtetlist->len()) { - return false; // assert(0); - } - // 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; + startpt = sorg(sseg); + endpt = sdest(sseg); + + if (b->verbose > 2) { + printf(" Recover segment (%d, %d).\n", pointmark(startpt), + pointmark(endpt)); + } + + success = 0; + + if (recoveredgebyflips(startpt, endpt, &searchtet, 0)) { + success = 1; + } else { + // Try to recover it from the other direction. + if (recoveredgebyflips(endpt, startpt, &searchtet, 0)) { + success = 1; } - if (j == 0) { - minvol1 = -ori; - } else { - if (minvol1 > (-ori)) { - minvol1 = -ori; + } + + if (!success && fullsearch) { + if (recoveredgebyflips(startpt, endpt, &searchtet, fullsearch)) { + success = 1; + } + } + + if (success) { + // Segment is recovered. Insert it. + // Let the segment remember an adjacent tet. + sstbond1(sseg, searchtet); + // Bond the segment to all tets containing it. + spintet = searchtet; + do { + tssbond1(spintet, sseg); + fnextself(spintet); + } while (spintet.tet != searchtet.tet); + } else { + if (steinerflag > 0) { + // Try to recover the segment but do not split it. + if (addsteiner4recoversegment(&sseg, 0)) { + success = 1; + } + if (!success && (steinerflag > 1)) { + // Split the segment. + addsteiner4recoversegment(&sseg, 1); + success = 1; } } - } // 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; - } else { - // The min. vol. begins to decrease. Stop the search. - stopflag = true; + if (!success) { + if (misseglist != NULL) { + // Save this segment. + misseglist->newindex((void **) &paryseg); + *paryseg = sseg; + } } } - } // i - if (minvol > 0) { - // We've found a valid relocation. Choose it. + } // while (subsegstack->objects > 0l) + + if (steinerflag) { 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]); + // Report the number of added Steiner points. + if (st_volref_count > bak_inpoly_count) { + printf(" Add %ld Steiner points in volume.\n", + st_volref_count - bak_inpoly_count); + } + if (st_segref_count > bak_segref_count) { + printf(" Add %ld Steiner points in segments.\n", + st_segref_count - bak_segref_count); + } } - for (i = 0; i < 3; i++) relocatept[i] = searchpt[i]; - return true; - } else { - return false; } + + return 0; } /////////////////////////////////////////////////////////////////////////////// // // -// relocatepoint() Relocate a point into the cavity. // +// recoverfacebyflips() Recover a face by flips. // // // -// 'frontlist' contains the boundary faces of the cavity C. All fronts must // -// be visible by 'steinpt'. Some fronts may hold by 'fake' tets (they are // -// hull faces). Fake tets will be removed when they're finished. // +// If 'searchsh' is not NULL, it is a subface to be recovered. It is only // +// used for checking self-intersections. // // // /////////////////////////////////////////////////////////////////////////////// -bool tetgenmesh::relocatepoint(point steinpt, triface* oldtet, list* frontlist, - list* newtetlist, queue* flipque) +int tetgenmesh::recoverfacebyflips(point pa, point pb, point pc, + face *searchsh, triface* searchtet) { - triface front, newtet, newface, neightet; - face checksh; - point pa, pb; - REAL attrib, volume; - bool bdflag; - int i, j, k, l; + triface spintet, flipedge; + point pd, pe; + enum interresult dir; + flipconstraints fc; + int types[2], poss[4], intflag; + int success, success1; + int t1ver; + int i, j; - if (b->verbose > 1) { - printf(" Insert Steiner point (%.12g, %.12g, %.12g) %d.\n", - steinpt[0], steinpt[1], steinpt[2], pointmark(steinpt)); - } - // Clear the list first. - newtetlist->clear(); - - // Create the tets formed by fronts and 'steinpt'. - for (i = 0; i < frontlist->len(); i++) { - // Get a front f. - front = * (triface *)(* frontlist)[i]; - // Let f face inside C. (f is a face of tet adjacent to C). - adjustedgering(front, CW); - if (b->verbose > 2) { - printf(" Get front (%d, %d, %d).\n", pointmark(org(front)), - pointmark(dest(front)), pointmark(apex(front))); - } - maketetrahedron(&newtet); - newtetlist->append(&newtet); - setorg(newtet, org(front)); - setdest(newtet, dest(front)); - setapex(newtet, apex(front)); - setoppo(newtet, steinpt); - 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); - } - } - // 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. - // Otherwise, 'dummytet' may point to a dead tetrahedron after the - // old cavity tets are removed. - dummytet[0] = encode(newtet); - } else { - // Bond two tetrahedra, also dissolve the old bond at 'front'. - bond(newtet, front); - } - if (checksh.sh != dummysh) { - sesymself(checksh); - tsbond(newtet, checksh); - } - if (flipque != (queue *) NULL) { - // f may be non-locally Delaunay and flipable. - enqueueflipface(newtet, flipque); - } - // The three neighbors are open. Will be finished later. - } - // 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; - for (j = 0; j < 3; j++) { - fnext(newtet, newface); - sym(newface, neightet); - if (neightet.tet == dummytet) { - // Find a neightet to connect it. - bdflag = false; - pa = org(newface); - pb = dest(newface); - assert(apex(newface) == steinpt); - for (k = i + 1; k < newtetlist->len() && !bdflag; k++) { - neightet = * (triface *)(* newtetlist)[k]; - neightet.ver = 0; - for (l = 0; l < 3; l++) { - if ((org(neightet) == pa && dest(neightet) == pb) || - (org(neightet) == pb && dest(neightet) == pa)) { - // Find the neighbor. - fnextself(neightet); - assert(apex(neightet) == steinpt); - // Now neightet is a face same as newface, bond them. - bond(newface, neightet); - bdflag = true; + fc.fac[0] = pa; + fc.fac[1] = pb; + fc.fac[2] = pc; + fc.checkflipeligibility = 1; + success = 0; + + for (i = 0; i < 3 && !success; i++) { + while (1) { + // Get a tet containing the edge [a,b]. + point2tetorg(fc.fac[i], *searchtet); + dir = finddirection(searchtet, fc.fac[(i+1)%3]); + //assert(dir == ACROSSVERT); + assert(dest(*searchtet) == fc.fac[(i+1)%3]); + // Search the face [a,b,c] + spintet = *searchtet; + while (1) { + if (apex(spintet) == fc.fac[(i+2)%3]) { + // Found the face. + *searchtet = spintet; + // Return the face [a,b,c]. + for (j = i; j > 0; j--) { + eprevself(*searchtet); + } + success = 1; + break; + } + fnextself(spintet); + if (spintet.tet == searchtet->tet) break; + } // while (1) + if (success) break; + // The face is missing. Try to recover it. + success1 = 0; + // Find a crossing edge of this face. + spintet = *searchtet; + while (1) { + pd = apex(spintet); + pe = oppo(spintet); + if ((pd != dummypoint) && (pe != dummypoint)) { + // Check if [d,e] intersects [a,b,c] + intflag = tri_edge_test(pa, pb, pc, pd, pe, NULL, 1, types, poss); + if (intflag > 0) { + // By our assumptions, they can only intersect at a single point. + if (intflag == 2) { + // Check the intersection type. + dir = (enum interresult) types[0]; + if ((dir == ACROSSFACE) || (dir == ACROSSEDGE)) { + // Go to the edge [d,e]. + edestoppo(spintet, flipedge); // [d,e,a,b] + if (searchsh != NULL) { + // Check if [e,d] is a segment. + if (issubseg(flipedge)) { + if (!b->quiet) { + face checkseg; + tsspivot1(flipedge, checkseg); + printf("Found a segment and a subface intersect.\n"); + pd = farsorg(checkseg); + pe = farsdest(checkseg); + printf(" 1st: [%d, %d] %d.\n", pointmark(pd), + pointmark(pe), shellmark(checkseg)); + printf(" 2nd: [%d,%d,%d] %d\n", pointmark(pa), + pointmark(pb), pointmark(pc), shellmark(*searchsh)); + } + terminatetetgen(this, 3); + } + } + // Try to flip the edge [d,e]. + success1 = (removeedgebyflips(&flipedge, &fc) == 2); + } else { + if (dir == TOUCHFACE) { + point touchpt, *parypt; + if (poss[1] == 0) { + touchpt = pd; // pd is a coplanar vertex. + } else { + touchpt = pe; // pe is a coplanar vertex. + } + if (pointtype(touchpt) == FREEVOLVERTEX) { + // A volume Steiner point was added in this subface. + // Split this subface by this point. + face checksh, *parysh; + int siloc = (int) ONFACE; + int sbowat = 0; // Only split this subface. + setpointtype(touchpt, FREEFACETVERTEX); + sinsertvertex(touchpt, searchsh, NULL, siloc, sbowat, 0); + st_volref_count--; + st_facref_count++; + // Queue this vertex for removal. + subvertstack->newindex((void **) &parypt); + *parypt = touchpt; + // Queue new subfaces for recovery. + // Put all new subfaces into stack for recovery. + for (i = 0; i < caveshbdlist->objects; i++) { + // Get an old subface at edge [a, b]. + parysh = (face *) fastlookup(caveshbdlist, i); + spivot(*parysh, checksh); // The new subface [a, b, p]. + // Do not recover a deleted new face (degenerated). + if (checksh.sh[3] != NULL) { + subfacstack->newindex((void **) &parysh); + *parysh = checksh; + } + } + // Delete the old subfaces in sC(p). + assert(caveshlist->objects == 1); + for (i = 0; i < caveshlist->objects; i++) { + parysh = (face *) fastlookup(caveshlist, i); + shellfacedealloc(subfaces, parysh->sh); + } + // Clear working lists. + caveshlist->restart(); + caveshbdlist->restart(); + cavesegshlist->restart(); + // We can return this function. + searchsh->sh = NULL; // It has been split. + success1 = 0; + success = 1; + } else { + // It should be a PLC problem. + if (pointtype(touchpt) == FREESEGVERTEX) { + // A segment and a subface intersect. + } else if (pointtype(touchpt) == FREEFACETVERTEX) { + // Two facets self-intersect. + } + terminatetetgen(this, 3); + } + } else { + assert(0); // Unknown cases. Debug. + } + } break; + } else { // intflag == 4. Coplanar case. + // This may be an input PLC error. + assert(0); } - enextself(neightet); - } - } - if (!bdflag) { - assert(0); // break; // The relocation failed. + } // if (intflag > 0) } - } - 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; - } - - if (flipque != (queue *) NULL) { - // Recover locally Delaunay faces. - lawson3d(flipque); - } + fnextself(spintet); + assert(spintet.tet != searchtet->tet); + } // while (1) + if (!success1) break; + } // while (1) + } // i - return true; + return success; } /////////////////////////////////////////////////////////////////////////////// // // -// findcollapseedge() Find collapseable edge to suppress an endpoint. // +// recoversubfaces() Recover all subfaces. // // // /////////////////////////////////////////////////////////////////////////////// -bool tetgenmesh::findcollapseedge(point suppt, point *conpt, list* oldtetlist, - list* ptlist) +int tetgenmesh::recoversubfaces(arraypool *misshlist, int steinerflag) { - triface front; - point pt, pa, pb, pc; - REAL *lenarray, ltmp, ori; - bool visflag; - int *idxarray, itmp; - int n, i, j; + triface searchtet, neightet, spintet; + face searchsh, neighsh, neineish, *parysh; + face bdsegs[3]; + point startpt, endpt, apexpt, *parypt; + point steinerpt; + enum interresult dir; + insertvertexflags ivf; + int success; + int t1ver; + int i, j; - if (b->verbose > 2) { - printf(" Search an edge (in %d edges) for collapse %d.\n", - ptlist->len(), pointmark(suppt)); + if (b->verbose > 1) { + printf(" Recover subfaces [%s level = %2d] #: %ld.\n", + (b->fliplinklevel > 0) ? "fixed" : "auto", + (b->fliplinklevel > 0) ? b->fliplinklevel : autofliplinklevel, + subfacstack->objects); } - // Candidate edges are p to the points of B(p) (in 'ptlist'). - n = ptlist->len(); - lenarray = new REAL[n]; - idxarray = new int[n]; - // Sort the points of B(p) by distance to p. - for (i = 0; i < n; i++) { - pt = * (point *)(* ptlist)[i]; - lenarray[i] = distance(suppt, pt); - idxarray[i] = i; - } - // Bubble sort. - for (i = 0; i < n - 1; i++) { - for (j = 0; j < n - 1 - i; j++) { - if (lenarray[j + 1] < lenarray[j]) { // compare the two neighbors - ltmp = lenarray[j]; // swap a[j] and a[j + 1] - lenarray[j] = lenarray[j + 1]; - lenarray[j + 1] = ltmp; - itmp = idxarray[j]; // swap a[j] and a[j + 1] - idxarray[j] = idxarray[j + 1]; - idxarray[j + 1] = itmp; - } + // Loop until 'subfacstack' is empty. + while (subfacstack->objects > 0l) { + + subfacstack->objects--; + parysh = (face *) fastlookup(subfacstack, subfacstack->objects); + searchsh = *parysh; + + if (searchsh.sh[3] == NULL) continue; // Skip a dead subface. + + stpivot(searchsh, neightet); + if (neightet.tet != NULL) continue; // Skip a recovered subface. + + + if (b->verbose > 2) { + printf(" Recover subface (%d, %d, %d).\n",pointmark(sorg(searchsh)), + pointmark(sdest(searchsh)), pointmark(sapex(searchsh))); } - } - // For each point q of B(p), test if the edge (p, q) can be collapseed. - for (i = 0; i < n; i++) { - pt = * (point *)(* ptlist)[idxarray[i]]; - // Is q visible by faces of B(p) not with q as a vertex. - lenarray[i] = 0.0; // zero volume. - visflag = true; - for (j = 0; j < oldtetlist->len() && visflag; j++) { - front = * (triface *)(* oldtetlist)[j]; - // Let f face to inside of B(p). - adjustedgering(front, CCW); - pa = org(front); - pb = dest(front); - pc = apex(front); - // Is f contains q? - if ((pa != pt) && (pb != pt) && (pc != pt)) { - ori = orient3d(pa, pb, pc, pt); - if (ori != 0.0) { - if (iscoplanar(pa, pb, pc, pt, ori, b->epsilon * 1e+2)) ori = 0.0; - } - visflag = ori < 0.0; - if (visflag) { - // Visible, set the smallest volume. - if (j == 0) { - lenarray[i] = fabs(ori); + + // The three edges of the face need to be existed first. + for (i = 0; i < 3; i++) { + sspivot(searchsh, bdsegs[i]); + if (bdsegs[i].sh != NULL) { + // The segment must exist. + sstpivot1(bdsegs[i], searchtet); + if (searchtet.tet == NULL) { + assert(0); + } + } else { + // This edge is not a segment (due to a Steiner point). + // Check whether it exists or not. + success = 0; + startpt = sorg(searchsh); + endpt = sdest(searchsh); + point2tetorg(startpt, searchtet); + dir = finddirection(&searchtet, endpt); + if (dir == ACROSSVERT) { + if (dest(searchtet) == endpt) { + success = 1; } else { - lenarray[i] = fabs(ori) < lenarray[i] ? fabs(ori) : lenarray[i]; + //assert(0); // A PLC problem. + terminatetetgen(this, 3); } } else { - // Invisible. Do not collapse (p, q). - lenarray[i] = 0.0; + // The edge is missing. Try to recover it. + if (recoveredgebyflips(startpt, endpt, &searchtet, 0)) { + success = 1; + } else { + if (recoveredgebyflips(endpt, startpt, &searchtet, 0)) { + success = 1; + } + } + } + if (success) { + // Insert a temporary segment to protect this edge. + makeshellface(subsegs, &(bdsegs[i])); + setshvertices(bdsegs[i], startpt, endpt, NULL); + smarktest2(bdsegs[i]); // It's a temporary segment. + // Insert this segment into surface mesh. + ssbond(searchsh, bdsegs[i]); + spivot(searchsh, neighsh); + if (neighsh.sh != NULL) { + ssbond(neighsh, bdsegs[i]); + } + // Insert this segment into tetrahedralization. + sstbond1(bdsegs[i], searchtet); + // Bond the segment to all tets containing it. + spintet = searchtet; + do { + tssbond1(spintet, bdsegs[i]); + fnextself(spintet); + } while (spintet.tet != searchtet.tet); + } else { + // An edge of this subface is missing. Can't recover this subface. + // Delete any temporary segment that has been created. + for (j = (i - 1); j >= 0; j--) { + if (smarktest2ed(bdsegs[j])) { + spivot(bdsegs[j], neineish); + assert(neineish.sh != NULL); + //if (neineish.sh != NULL) { + ssdissolve(neineish); + spivot(neineish, neighsh); + if (neighsh.sh != NULL) { + ssdissolve(neighsh); + // There should be only two subfaces at this segment. + spivotself(neighsh); // SELF_CHECK + assert(neighsh.sh == neineish.sh); + } + //} + sstpivot1(bdsegs[j], searchtet); + assert(searchtet.tet != NULL); + //if (searchtet.tet != NULL) { + spintet = searchtet; + while (1) { + tssdissolve1(spintet); + fnextself(spintet); + if (spintet.tet == searchtet.tet) break; + } + //} + shellfacedealloc(subsegs, bdsegs[j].sh); + } + } // j + if (steinerflag) { + // Add a Steiner point at the midpoint of this edge. + if (b->verbose > 2) { + printf(" Add a Steiner point in subedge (%d, %d).\n", + pointmark(startpt), pointmark(endpt)); + } + makepoint(&steinerpt, FREEFACETVERTEX); + for (j = 0; j < 3; j++) { + steinerpt[j] = 0.5 * (startpt[j] + endpt[j]); + } + + point2tetorg(startpt, searchtet); // Start from 'searchtet'. + ivf.iloc = (int) OUTSIDE; // Need point location. + ivf.bowywat = 1; + ivf.lawson = 0; + ivf.rejflag = 0; + ivf.chkencflag = 0; + ivf.sloc = (int) ONEDGE; + ivf.sbowywat = 1; // Allow flips in facet. + ivf.splitbdflag = 0; + ivf.validflag = 1; + ivf.respectbdflag = 1; + ivf.assignmeshsize = b->metric; + if (!insertpoint(steinerpt, &searchtet, &searchsh, NULL, &ivf)) { + assert(0); + } + // Save this Steiner point (for removal). + // Re-use the array 'subvertstack'. + subvertstack->newindex((void **) &parypt); + *parypt = steinerpt; + + st_facref_count++; + if (steinerleft > 0) steinerleft--; + } // if (steinerflag) + break; } } + senextself(searchsh); + } // i + + if (i == 3) { + // Recover the subface. + startpt = sorg(searchsh); + endpt = sdest(searchsh); + apexpt = sapex(searchsh); + + success = recoverfacebyflips(startpt,endpt,apexpt,&searchsh,&searchtet); + + // Delete any temporary segment that has been created. + for (j = 0; j < 3; j++) { + if (smarktest2ed(bdsegs[j])) { + spivot(bdsegs[j], neineish); + assert(neineish.sh != NULL); + //if (neineish.sh != NULL) { + ssdissolve(neineish); + spivot(neineish, neighsh); + if (neighsh.sh != NULL) { + ssdissolve(neighsh); + // There should be only two subfaces at this segment. + spivotself(neighsh); // SELF_CHECK + assert(neighsh.sh == neineish.sh); + } + //} + sstpivot1(bdsegs[j], neightet); + assert(neightet.tet != NULL); + //if (neightet.tet != NULL) { + spintet = neightet; + while (1) { + tssdissolve1(spintet); + fnextself(spintet); + if (spintet.tet == neightet.tet) break; + } + //} + shellfacedealloc(subsegs, bdsegs[j].sh); + } + } // j + + if (success) { + if (searchsh.sh != NULL) { + // Face is recovered. Insert it. + tsbond(searchtet, searchsh); + fsymself(searchtet); + sesymself(searchsh); + tsbond(searchtet, searchsh); + } + } else { + if (steinerflag) { + // Add a Steiner point at the barycenter of this subface. + if (b->verbose > 2) { + printf(" Add a Steiner point in subface (%d, %d, %d).\n", + pointmark(startpt), pointmark(endpt), pointmark(apexpt)); + } + makepoint(&steinerpt, FREEFACETVERTEX); + for (j = 0; j < 3; j++) { + steinerpt[j] = (startpt[j] + endpt[j] + apexpt[j]) / 3.0; + } + + point2tetorg(startpt, searchtet); // Start from 'searchtet'. + ivf.iloc = (int) OUTSIDE; // Need point location. + ivf.bowywat = 1; + ivf.lawson = 0; + ivf.rejflag = 0; + ivf.chkencflag = 0; + ivf.sloc = (int) ONFACE; + ivf.sbowywat = 1; // Allow flips in facet. + ivf.splitbdflag = 0; + ivf.validflag = 1; + ivf.respectbdflag = 1; + ivf.assignmeshsize = b->metric; + if (!insertpoint(steinerpt, &searchtet, &searchsh, NULL, &ivf)) { + assert(0); + } + // Save this Steiner point (for removal). + // Re-use the array 'subvertstack'. + subvertstack->newindex((void **) &parypt); + *parypt = steinerpt; + + st_facref_count++; + if (steinerleft > 0) steinerleft--; + } // if (steinerflag) + } + } else { + success = 0; } - if ((b->verbose > 2) && visflag) { - printf(" Got candidate %d vol(%g).\n", pointmark(pt), lenarray[i]); - } - } - // Select the largest non-zero volume (result in ltmp). - ltmp = lenarray[0]; - itmp = idxarray[0]; - for (i = 1; i < n; i++) { - if (lenarray[i] != 0.0) { - if (lenarray[i] > ltmp) { - ltmp = lenarray[i]; - itmp = idxarray[i]; // The index to find the point. + if (!success) { + if (misshlist != NULL) { + // Save this subface. + misshlist->newindex((void **) &parysh); + *parysh = searchsh; } } - } - delete [] lenarray; - delete [] idxarray; + } // while (subfacstack->objects > 0l) - if (ltmp == 0.0) { - // No edge can be collapseed. - *conpt = (point) NULL; - return false; - } else { - pt = * (point *)(* ptlist)[itmp]; - *conpt = pt; - return true; - } + return 0; } /////////////////////////////////////////////////////////////////////////////// // // -// collapseedge() Remove a point by edge collapse. // +// getvertexstar() Return the star of a vertex. // +// // +// If the flag 'fullstar' is set, return the complete star of this vertex. // +// Otherwise, only a part of the star which is bounded by facets is returned.// +// // +// 'tetlist' returns the list of tets in the star of the vertex 'searchpt'. // +// Every tet in 'tetlist' is at the face opposing to 'searchpt'. // +// // +// 'vertlist' returns the list of vertices in the star (exclude 'searchpt'). // +// // +// 'shlist' returns the list of subfaces in the star. Each subface must face // +// to the interior of this star. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::collapseedge(point suppt, point conpt, list* oldtetlist, - list* deadtetlist) +int tetgenmesh::getvertexstar(int fullstar, point searchpt, arraypool* tetlist, + arraypool* vertlist, arraypool* shlist) { - triface oldtet, deadtet; - triface adjtet1, adjtet2; - face adjsh; - point pa, pb, pc; + triface searchtet, neightet, *parytet; + face checksh, *parysh; + point pt, *parypt; + int collectflag; + int t1ver; int i, j; - if (b->verbose > 2) { - printf(" Collapse edge (%d,%d).\n", pointmark(suppt), pointmark(conpt)); - } - - // Loop in B(p), replace p with np, queue dead tets, uninfect old tets. - for (i = 0; i < oldtetlist->len(); i++) { - oldtet = * (triface *)(* oldtetlist)[i]; // assert(infected(oldtet)); - uninfect(oldtet); - pa = org(oldtet); - pb = dest(oldtet); - pc = apex(oldtet); - assert(oppo(oldtet) == suppt); - 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. - for (i = 0; i < deadtetlist->len(); i++) { - deadtet = * (triface *)(* deadtetlist)[i]; - // Get the adjacent tet n1 (outside B(p)). - sym(deadtet, adjtet1); - tspivot(deadtet, adjsh); - // Find the edge in deadtet opposite to conpt. - adjustedgering(deadtet, CCW); - for (j = 0; j < 3; j++) { - if (apex(deadtet) == conpt) break; - enextself(deadtet); - } - assert(j < 3); - // Get another adjacent tet n2. - fnext(deadtet, adjtet2); - symself(adjtet2); - assert(adjtet2.tet != dummytet); // n2 is inside B(p). - if (adjtet1.tet != dummytet) { - bond(adjtet1, adjtet2); // Bond n1 <--> n2. - } else { - dissolve(adjtet2); // Dissolve at n2. - dummytet[0] = encode(adjtet2); // Let dummytet holds n2. - } - if (adjsh.sh != dummysh) { - tsbond(adjtet2, adjsh); // Bond s <--> n2. + point2tetorg(searchpt, searchtet); + + // Go to the opposite face (the link face) of the vertex. + enextesymself(searchtet); + //assert(oppo(searchtet) == searchpt); + infect(searchtet); // Collect this tet (link face). + tetlist->newindex((void **) &parytet); + *parytet = searchtet; + if (vertlist != NULL) { + // Collect three (link) vertices. + j = (searchtet.ver & 3); // The current vertex index. + for (i = 1; i < 4; i++) { + pt = (point) searchtet.tet[4 + ((j + i) % 4)]; + pinfect(pt); + vertlist->newindex((void **) &parypt); + *parypt = pt; } - // Collapse deadtet. - tetrahedrondealloc(deadtet.tet); } - deadtetlist->clear(); -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// deallocfaketets() Deleted fake tets at fronts. // -// // -// This routine is only called when the findrelocatepoint() routine fails. // -// In other cases, the fake tets are removed automatically in carvecavity() // -// or relocatepoint(). // -// // -/////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::deallocfaketets(list* frontlist) -{ - triface front, neightet; - face checksh; - bool infectflag; - int i; - - for (i = 0; i < frontlist->len(); i++) { - // Get a front f. - front = * (triface *)(* frontlist)[i]; - // Let f face inside C. (f is a face of tet adjacent to C). - adjustedgering(front, CW); - sym(front, neightet); - tspivot(front, checksh); - if (oppo(front) == (point) NULL) { - if (b->verbose > 2) { - printf(" Get fake tet (%d, %d, %d).\n", pointmark(org(front)), - pointmark(dest(front)), pointmark(apex(front))); - } - if (neightet.tet != dummytet) { - // The neightet may be infected. After dissolve it, the infect flag - // will be lost. Save the flag and restore it later. - infectflag = infected(neightet); - dissolve(neightet); - if (infectflag) { - infect(neightet); - } + collectflag = 1; + esym(searchtet, neightet); + if (issubface(neightet)) { + if (shlist != NULL) { + tspivot(neightet, checksh); + if (!sinfected(checksh)) { + // Collect this subface (link edge). + sinfected(checksh); + shlist->newindex((void **) &parysh); + *parysh = checksh; } - if (checksh.sh != dummysh) { - infectflag = sinfected(checksh); - stdissolve(checksh); - if (infectflag) { - sinfect(checksh); + } + if (!fullstar) { + collectflag = 0; + } + } + if (collectflag) { + fsymself(neightet); // Goto the adj tet of this face. + esymself(neightet); // Goto the oppo face of this vertex. + // assert(oppo(neightet) == searchpt); + infect(neightet); // Collect this tet (link face). + tetlist->newindex((void **) &parytet); + *parytet = neightet; + if (vertlist != NULL) { + // Collect its apex. + pt = apex(neightet); + pinfect(pt); + vertlist->newindex((void **) &parypt); + *parypt = pt; + } + } // if (collectflag) + + // Continue to collect all tets in the star. + for (i = 0; i < tetlist->objects; i++) { + searchtet = * (triface *) fastlookup(tetlist, i); + // Note that 'searchtet' is a face opposite to 'searchpt', and the neighbor + // tet at the current edge is already collected. + // Check the neighbors at the other two edges of this face. + for (j = 0; j < 2; j++) { + collectflag = 1; + enextself(searchtet); + esym(searchtet, neightet); + if (issubface(neightet)) { + if (shlist != NULL) { + tspivot(neightet, checksh); + if (!sinfected(checksh)) { + // Collect this subface (link edge). + sinfected(checksh); + shlist->newindex((void **) &parysh); + *parysh = checksh; + } + } + if (!fullstar) { + collectflag = 0; } } - // Dealloc the 'fake' tet. - tetrahedrondealloc(front.tet); - // If 'neightet' is a hull face, let 'dummytet' bond to it. It is - // a 'dummytet' when this front was created from a new subface. - // In such case, it should not be bounded. - if (neightet.tet != dummytet) { - dummytet[0] = encode(neightet); - } + if (collectflag) { + fsymself(neightet); + if (!infected(neightet)) { + esymself(neightet); // Go to the face opposite to 'searchpt'. + infect(neightet); + tetlist->newindex((void **) &parytet); + *parytet = neightet; + if (vertlist != NULL) { + // Check if a vertex is collected. + pt = apex(neightet); + if (!pinfected(pt)) { + pinfect(pt); + vertlist->newindex((void **) &parypt); + *parypt = pt; + } + } + } // if (!infected(neightet)) + } // if (collectflag) + } // j + } // i + + + // Uninfect the list of tets and vertices. + for (i = 0; i < tetlist->objects; i++) { + parytet = (triface *) fastlookup(tetlist, i); + uninfect(*parytet); + } + + if (vertlist != NULL) { + for (i = 0; i < vertlist->objects; i++) { + parypt = (point *) fastlookup(vertlist, i); + puninfect(*parypt); + } + } + + if (shlist != NULL) { + for (i = 0; i < shlist->objects; i++) { + parysh = (face *) fastlookup(shlist, i); + suninfect(*parysh); } } + + return (int) tetlist->objects; } /////////////////////////////////////////////////////////////////////////////// // // -// restorepolyhedron() Restore the tetrahedralization in a polyhedron. // +// getedge() Get a tetrahedron having the two endpoints. // // // -// This routine is only called when the operation of suppressing a point is // -// aborted (eg., findrelocatepoint() routine fails). The polyhedron has been // -// remeshed by new tets. This routine restore the old tets in it. // +// The method here is to search the second vertex in the link faces of the // +// first vertex. The global array 'cavetetlist' is re-used for searching. // // // -// 'oldtetlist' contains the list of old tets. Each old tet t_o assumes that // -// it still connects to a tet t_b of the mesh, however, t_b does not connect // -// to t_o, this routine resets the connection such that t_b <--> t_o. // +// This function is used for the case when the mesh is non-convex. Otherwise,// +// the function finddirection() should be faster than this. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::restorepolyhedron(list* oldtetlist) +int tetgenmesh::getedge(point e1, point e2, triface *tedge) { - triface oldtet, neightet, neineitet; - face checksh; - point *ppt; + triface searchtet, neightet, *parytet; + point pt; + int done; int i, j; - for (i = 0; i < oldtetlist->len(); i++) { - // Get an old tet t_o. - oldtet = * (triface *)(* oldtetlist)[i]; - // Check the four sides of t_o. - for (oldtet.loc = 0; oldtet.loc < 4; oldtet.loc++) { - 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. - bond(neightet, oldtet); - if (checksh.sh != dummysh) { - tsbond(oldtet, checksh); - } - } - } else { - // t_o has a hull face. It should be the boundary of P. - tsbond(oldtet, checksh); - // Let dummytet[0] points to it. - dummytet[0] = encode(oldtet); + if (b->verbose > 2) { + printf(" Get edge from %d to %d.\n", pointmark(e1), pointmark(e2)); + } + + // Quickly check if 'tedge' is just this edge. + if (!isdeadtet(*tedge)) { + if (org(*tedge) == e1) { + if (dest(*tedge) == e2) { + return 1; + } + } else if (org(*tedge) == e2) { + if (dest(*tedge) == e1) { + esymself(*tedge); + return 1; } } - // Update the point-to-tet map. - ppt = (point *) &(oldtet.tet[4]); - for (j = 0; j < 4; j++) { - setpoint2tet(ppt[j], encode(oldtet)); + } + + // Search for the edge [e1, e2]. + point2tetorg(e1, *tedge); + finddirection(tedge, e2); + if (dest(*tedge) == e2) { + return 1; + } else { + // Search for the edge [e2, e1]. + point2tetorg(e2, *tedge); + finddirection(tedge, e1); + if (dest(*tedge) == e1) { + esymself(*tedge); + return 1; } } + + + // Go to the link face of e1. + point2tetorg(e1, searchtet); + enextesymself(searchtet); + //assert(oppo(searchtet) == e1); + + assert(cavebdrylist->objects == 0l); // It will re-use this list. + arraypool *tetlist = cavebdrylist; + + // Search e2. + for (i = 0; i < 3; i++) { + pt = apex(searchtet); + if (pt == e2) { + // Found. 'searchtet' is [#,#,e2,e1]. + eorgoppo(searchtet, *tedge); // [e1,e2,#,#]. + return 1; + } + enextself(searchtet); + } + + // Get the adjacent link face at 'searchtet'. + fnext(searchtet, neightet); + esymself(neightet); + // assert(oppo(neightet) == e1); + pt = apex(neightet); + if (pt == e2) { + // Found. 'neightet' is [#,#,e2,e1]. + eorgoppo(neightet, *tedge); // [e1,e2,#,#]. + return 1; + } + + // Continue searching in the link face of e1. + infect(searchtet); + tetlist->newindex((void **) &parytet); + *parytet = searchtet; + infect(neightet); + tetlist->newindex((void **) &parytet); + *parytet = neightet; + + done = 0; + + for (i = 0; (i < tetlist->objects) && !done; i++) { + parytet = (triface *) fastlookup(tetlist, i); + searchtet = *parytet; + for (j = 0; (j < 2) && !done; j++) { + enextself(searchtet); + fnext(searchtet, neightet); + if (!infected(neightet)) { + esymself(neightet); + pt = apex(neightet); + if (pt == e2) { + // Found. 'neightet' is [#,#,e2,e1]. + eorgoppo(neightet, *tedge); + done = 1; + } else { + infect(neightet); + tetlist->newindex((void **) &parytet); + *parytet = neightet; + } + } + } // j + } // i + + // Uninfect the list of visited tets. + for (i = 0; i < tetlist->objects; i++) { + parytet = (triface *) fastlookup(tetlist, i); + uninfect(*parytet); + } + tetlist->restart(); + + return done; } /////////////////////////////////////////////////////////////////////////////// // // -// suppressfacetpoint() Suppress a point inside a facet. // +// reduceedgesatvertex() Reduce the number of edges at a given vertex. // // // -// The point p inside a facet F will be suppressed from F by either being // -// deleted from the mesh or being relocated into the volume. // -// // -// 'supsh' is a subface f of F, and p = sapex(f); the other parameters are // -// working lists which are empty at the beginning and the end. // -// // -// 'optflag' is used for mesh optimization. If it is set, after removing p, // -// test the object function on each new tet, queue bad tets. // +// 'endptlist' contains the endpoints of edges connecting at the vertex. // // // /////////////////////////////////////////////////////////////////////////////// -bool tetgenmesh::suppressfacetpoint(face* supsh, list* frontlist, - list* misfrontlist, list* ptlist, list* conlist, memorypool* viri, - queue* flipque, bool noreloc, bool optflag) +int tetgenmesh::reduceedgesatvertex(point startpt, arraypool* endptlist) { - list *oldtetlist[2], *newtetlist[2]; - list *oldshlist, *newshlist; - list *gluetetlist; - list *glueshlist; - triface oldtet, newtet, neightet; - face oldsh, newsh; - point suppt, newpt[2]; - point *cons, *ppt; + triface searchtet; + point *pendpt, *parypt; enum interresult dir; - REAL norm[3]; - bool success; - int bakchecksubfaces; - int shmark; - int i, j; + flipconstraints fc; + int reduceflag; + int count; + int n, i, j; - suppt = sapex(*supsh); - if (b->verbose > 1) { - printf(" Suppress point %d in facet.\n", pointmark(suppt)); - } - // Initialize working lists, variables. - for (i = 0; i < 2; i++) { - oldtetlist[i] = (list *) NULL; - 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); - // Get the edges of C(p). They form a closed polygon. - for (i = 0; i < oldshlist->len(); i++) { - oldsh = * (face *)(* oldshlist)[i]; - cons = (point *) conlist->append(NULL); - cons[0] = sorg(oldsh); - cons[1] = sdest(oldsh); - } - // Re-triangulate the old C(p). - shmark = shellmark(*supsh); - triangulate(shmark, b->epsilon, ptlist, conlist, 0, NULL, viri, flipque); - // Get new subs of C(p), remove protected segments. - retrievenewsubs(newshlist, true); - // Substitute the old C(p) with the new C(p) - replacepolygonsubs(oldshlist, newshlist); - // Clear work lists. - ptlist->clear(); - conlist->clear(); - 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++) { - if (i == 1) sesymself(*supsh); - // Get a tet containing p. - stpivot(*supsh, oldtet); - // Is this part empty? - if (oldtet.tet == dummytet) continue; - // Allocate spaces for storing (old and new) B_i(p). - oldtetlist[i] = new list(sizeof(triface), NULL, 256); - newtetlist[i] = new list(sizeof(triface), NULL, 256); - // Form old B_i(p) in oldtetlist[i]. - assert(!isdead(&oldtet)); - oldtetlist[i]->append(&oldtet); - formstarpolyhedron(suppt, oldtetlist[i], ptlist, false); - // Infect the tets in old B_i(p) (they're going to be delete). - for (j = 0; j < oldtetlist[i]->len(); j++) { - oldtet = * (triface *)(* (oldtetlist[i]))[j]; - infect(oldtet); - } - // Preparation for re-tetrahedralzing old B_i(p). - orientnewsubs(newshlist, supsh, norm); - // Tetrahedralize old B_i(p). - success = constrainedcavity(&oldtet, newshlist, oldtetlist[i], ptlist, - frontlist, misfrontlist, newtetlist[i], gluetetlist, glueshlist, - 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]); - // Initialize newpt = suppt. - // 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. - 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); - pointdealloc(newpt[i]); - newpt[i] = (point) NULL; - assert(newtetlist[i]->len() == 0); - } - } - if (!success && noreloc) { - // Failed and no point relocation. Clean fake tets. - deallocfaketets(frontlist); - } - // Clear work lists. - ptlist->clear(); - frontlist->clear(); - misfrontlist->clear(); - // Do not clear gluetetlist. gluetetlist->clear(); - // Do not clear glueshlist. - flipque->clear(); - } + fc.remvert = startpt; + fc.checkflipeligibility = 1; - if (success) { - // p has been removed! (Still in the pool). - setpointtype(suppt, UNUSEDVERTEX); - unuverts++; - // Delete old C(p). - for (i = 0; i < oldshlist->len(); i++) { - oldsh = * (face *)(* oldshlist)[i]; - if (i == 0) { - // Update the 'hullsize' if C(p) is on the hull. - stpivot(oldsh, oldtet); - if (oldtet.tet != dummytet) { - sesymself(oldsh); - stpivot(oldsh, oldtet); - } - if (oldtet.tet == dummytet) { - // A boundary face. Update the 'hullsize'. - j = oldshlist->len() - newshlist->len(); - assert(j > 0); - hullsize -= j; - } - } - shellfacedealloc(subfaces, oldsh.sh); - } - // Delete old B_i(p). - for (i = 0; i < 2; i++) { - if (oldtetlist[i] != (list *) NULL) { - // Delete tets of the old B_i(p). - for (j = 0; j < oldtetlist[i]->len(); j++) { - oldtet = * (triface *)(* (oldtetlist[i]))[j]; - assert(!isdead(&oldtet)); - tetrahedrondealloc(oldtet.tet); - } - } - } - if (optflag) { - // Check for new bad-quality tets. - for (i = 0; i < 2; i++) { - if (newtetlist[i] != (list *) NULL) { - for (j = 0; j < newtetlist[i]->len(); j++) { - newtet = * (triface *)(* (newtetlist[i]))[j]; - if (!isdead(&newtet)) checktet4opt(&newtet, true); - } - } - } - } - // 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); + while (1) { + + count = 0; + + for (i = 0; i < endptlist->objects; i++) { + pendpt = (point *) fastlookup(endptlist, i); + if (*pendpt == dummypoint) { + continue; // Do not reduce a virtual edge. } - } - } else { - // p is not suppressed. Recover the original state. - unsupverts++; - // Restore the old C(p). - replacepolygonsubs(newshlist, oldshlist); - // Delete subs of the new C(p) - for (i = 0; i < newshlist->len(); i++) { - 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); + reduceflag = 0; + // Find the edge. + if (nonconvex) { + if (getedge(startpt, *pendpt, &searchtet)) { + dir = ACROSSVERT; + } else { + // The edge does not exist (was flipped). + dir = INTERSECT; } - sesymself(newsh); - } - shellfacedealloc(subfaces, newsh.sh); - } - // Restore old B_i(p). - for (i = 0; i < 2; i++) { - if (oldtetlist[i] != (list *) NULL) { - // Uninfect tets of old B_i(p). - for (j = 0; j < oldtetlist[i]->len(); j++) { - oldtet = * (triface *)(* (oldtetlist[i]))[j]; - assert(infected(oldtet)); - uninfect(oldtet); - } - // Has it been re-meshed? - // if (newtetlist[i]->len() > 0) { - // Restore the old B_i(p). - restorepolyhedron(oldtetlist[i]); - // Delete tets of the new B_i(p); - for (j = 0; j < newtetlist[i]->len(); j++) { - newtet = * (triface *)(* (newtetlist[i]))[j]; - // Some new tets may already be deleted (by carvecavity()). - if (!isdead(&newtet)) { - tetrahedrondealloc(newtet.tet); + } else { + point2tetorg(startpt, searchtet); + dir = finddirection(&searchtet, *pendpt); + } + if (dir == ACROSSVERT) { + if (dest(searchtet) == *pendpt) { + // Do not flip a segment. + if (!issubseg(searchtet)) { + n = removeedgebyflips(&searchtet, &fc); + if (n == 2) { + reduceflag = 1; } } - // } - // Dealloc newpt[i] if it exists. - if (newpt[i] != (point) NULL) { - pointdealloc(newpt[i]); - relverts--; - } - } - } - // 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); + } else { + assert(0); // A plc problem. } + } else { + // The edge has been flipped. + reduceflag = 1; } - } - // 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)); + if (reduceflag) { + count++; + // Move the last vertex into this slot. + j = endptlist->objects - 1; + parypt = (point *) fastlookup(endptlist, j); + *pendpt = *parypt; + endptlist->objects--; + i--; } - } - } + } // i - // Delete work lists. - delete oldshlist; - delete newshlist; - for (i = 0; i < 2; i++) { - if (oldtetlist[i] != (list *) NULL) { - delete oldtetlist[i]; - delete newtetlist[i]; + if (count == 0) { + // No edge is reduced. + break; } - } - delete gluetetlist; - delete glueshlist; - return success; + } // while (1) + + return (int) endptlist->objects; } /////////////////////////////////////////////////////////////////////////////// // // -// suppresssegpoint() Suppress a point on a segment. // +// removevertexbyflips() Remove a vertex by flips. // +// // +// This routine attempts to remove the given vertex 'rempt' (p) from the // +// tetrahedralization (T) by a sequence of flips. // // // -// The point p on a segment S will be suppressed from S by either being // -// deleted from the mesh or being relocated into the volume. // +// The algorithm used here is a simple edge reduce method. Suppose there are // +// n edges connected at p. We try to reduce the number of edges by flipping // +// any edge (not a segment) that is connecting at p. // // // -// 'supseg' is the segment S, and p = sdest(S); the other parameters are // -// working lists which are empty at the beginning and the end. // +// Unless T is a Delaunay tetrahedralization, there is no guarantee that 'p' // +// can be successfully removed. // // // /////////////////////////////////////////////////////////////////////////////// -bool tetgenmesh::suppresssegpoint(face* supseg, list* spinshlist, - list* newsegshlist, list* frontlist, list* misfrontlist, list* ptlist, - list* conlist, memorypool* viri, queue* flipque, bool noreloc, bool optflag) +int tetgenmesh::removevertexbyflips(point steinerpt) { - list **oldtetlist, **newtetlist; - list **oldshlist, **newshlist; - list *pnewshlist, *dnewshlist; - list *gluetetlist; - list *glueshlist; - triface oldtet, newtet, neightet, spintet; - face oldsh, newsh, *worksharray; - 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; - bool success; - int bakchecksubfaces; - int shmark; - int n, i, j, k; + triface *fliptets = NULL, wrktets[4]; + triface searchtet, spintet, neightet; + face parentsh, spinsh, checksh; + face leftseg, rightseg, checkseg; + point lpt = NULL, rpt = NULL, apexpt; //, *parypt; + flipconstraints fc; + enum verttype vt; + enum locateresult loc; + int valence, removeflag; + int slawson; + int t1ver; + int n, i; - // Get the Steiner point p. - assert(supseg->shver < 2); - suppt = sdest(*supseg); - // Find the segment ab split by p. - senext(*supseg, nsupseg); - spivotself(nsupseg); - assert(nsupseg.sh != dummysh); - nsupseg.shver = 0; - if (sorg(nsupseg) != suppt) sesymself(nsupseg); - assert(sorg(nsupseg) == suppt); - pa = sorg(*supseg); - pb = sdest(nsupseg); - if (b->verbose > 1) { - printf(" Remove point %d on segment (%d, %d).\n", - pointmark(suppt), pointmark(pa), pointmark(pb)); - } + vt = pointtype(steinerpt); + + if (vt == FREESEGVERTEX) { + sdecode(point2sh(steinerpt), leftseg); + assert(leftseg.sh != NULL); + leftseg.shver = 0; + if (sdest(leftseg) == steinerpt) { + senext(leftseg, rightseg); + spivotself(rightseg); + assert(rightseg.sh != NULL); + rightseg.shver = 0; + assert(sorg(rightseg) == steinerpt); + } else { + assert(sorg(leftseg) == steinerpt); + rightseg = leftseg; + senext2(rightseg, leftseg); + spivotself(leftseg); + assert(leftseg.sh != NULL); + leftseg.shver = 0; + assert(sdest(leftseg) == steinerpt); + } + lpt = sorg(leftseg); + rpt = sdest(rightseg); + if (b->verbose > 2) { + printf(" Removing Steiner point %d in segment (%d, %d).\n", + pointmark(steinerpt), pointmark(lpt), pointmark(rpt)); - // Let startsh s containing p. - 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; + } else if (vt == FREEFACETVERTEX) { + if (b->verbose > 2) { + printf(" Removing Steiner point %d in facet.\n", + pointmark(steinerpt)); + } + } else if (vt == FREEVOLVERTEX) { + if (b->verbose > 2) { + printf(" Removing Steiner point %d in volume.\n", + pointmark(steinerpt)); + } + } else if (vt == VOLVERTEX) { + if (b->verbose > 2) { + printf(" Removing a point %d in volume.\n", + pointmark(steinerpt)); + } + } else { + // It is not a Steiner point. + return 0; + } + + // Try to reduce the number of edges at 'p' by flips. + getvertexstar(1, steinerpt, cavetetlist, cavetetvertlist, NULL); + cavetetlist->restart(); // This list may be re-used. + if (cavetetvertlist->objects > 3l) { + valence = reduceedgesatvertex(steinerpt, cavetetvertlist); + } else { + valence = cavetetvertlist->objects; + } + assert(cavetetlist->objects == 0l); + cavetetvertlist->restart(); + + removeflag = 0; + + if (valence == 4) { + // Only 4 vertices (4 tets) left! 'p' is inside the convex hull of the 4 + // vertices. This case is due to that 'p' is not exactly on the segment. + point2tetorg(steinerpt, searchtet); + loc = INTETRAHEDRON; + removeflag = 1; + } else if (valence == 5) { + // There are 5 edges. + if (vt == FREESEGVERTEX) { + sstpivot1(leftseg, searchtet); + if (org(searchtet) != steinerpt) { + esymself(searchtet); + } + assert(org(searchtet) == steinerpt); + assert(dest(searchtet) == lpt); + i = 0; // Count the numbe of tet at the edge [p,lpt]. + neightet.tet = NULL; // Init the face. + spintet = searchtet; + while (1) { + i++; + if (apex(spintet) == rpt) { + // Remember the face containing the edge [lpt, rpt]. + neightet = spintet; + } + fnextself(spintet); + if (spintet.tet == searchtet.tet) break; + } + if (i == 3) { + // This case has been checked below. + } else if (i == 4) { + // There are 4 tets sharing at [p,lpt]. There must be 4 tets sharing + // at [p,rpt]. There must be a face [p, lpt, rpt]. + if (apex(neightet) == rpt) { + // The edge (segment) has been already recovered! + // Check if a 6-to-2 flip is possible (to remove 'p'). + // Let 'searchtet' be [p,d,a,b] + esym(neightet, searchtet); + enextself(searchtet); + // Check if there are exactly three tets at edge [p,d]. + wrktets[0] = searchtet; // [p,d,a,b] + for (i = 0; i < 2; i++) { + fnext(wrktets[i], wrktets[i+1]); // [p,d,b,c], [p,d,c,a] + } + if (apex(wrktets[0]) == oppo(wrktets[2])) { + loc = ONFACE; + removeflag = 1; + } + } + } + } else if (vt == FREEFACETVERTEX) { + // It is possible to do a 6-to-2 flip to remove the vertex. + point2tetorg(steinerpt, searchtet); + // Get the three faces of 'searchtet' which share at p. + // All faces has p as origin. + wrktets[0] = searchtet; + wrktets[1] = searchtet; + esymself(wrktets[1]); + enextself(wrktets[1]); + wrktets[2] = searchtet; + eprevself(wrktets[2]); + esymself(wrktets[2]); + // All internal edges of the six tets have valance either 3 or 4. + // Get one edge which has valance 3. + searchtet.tet = NULL; + for (i = 0; i < 3; i++) { + spintet = wrktets[i]; + valence = 0; while (1) { - if (!fnextself(spintet)) { - esymself(spintet); - break; - } - if (apex(spintet) == apex(neightet)) break; + valence++; + fnextself(spintet); + if (spintet.tet == wrktets[i].tet) 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; + if (valence == 3) { + // Found the edge. + searchtet = wrktets[i]; + break; + } else { + assert(valence == 4); } } - assert(!sinfected(worksharray[i])); - } // i - delete [] worksharray; + assert(searchtet.tet != NULL); + // Note, we do not detach the three subfaces at p. + // They will be removed within a 4-to-1 flip. + loc = ONFACE; + removeflag = 1; + } else { + // assert(0); DEBUG IT + } + //removeflag = 1; + } + + if (!removeflag) { + if (vt == FREESEGVERTEX) { + // Check is it possible to recover the edge [lpt,rpt]. + // The condition to check is: Whether each tet containing 'leftseg' is + // adjacent to a tet containing 'rightseg'. + sstpivot1(leftseg, searchtet); + if (org(searchtet) != steinerpt) { + esymself(searchtet); + } + assert(org(searchtet) == steinerpt); + assert(dest(searchtet) == lpt); + spintet = searchtet; + while (1) { + // Go to the bottom face of this tet. + eprev(spintet, neightet); + esymself(neightet); // [steinerpt, p1, p2, lpt] + // Get the adjacent tet. + fsymself(neightet); // [p1, steinerpt, p2, rpt] + if (oppo(neightet) != rpt) { + // Found a non-matching adjacent tet. + break; + } + fnextself(spintet); + if (spintet.tet == searchtet.tet) { + // 'searchtet' is [p,d,p1,p2]. + loc = ONEDGE; + removeflag = 1; + break; + } + } + } // if (vt == FREESEGVERTEX) } - - if (spinshlist->len() == 1) { - // This case has not handled yet. - // printf("Unhandled case: segment only belongs to one facet.\n"); - spinshlist->clear(); - unsupverts++; - return false; + + if (!removeflag) { + if (vt == FREESEGVERTEX) { + // Check if the edge [lpt, rpt] exists. + if (getedge(lpt, rpt, &searchtet)) { + // We have recovered this edge. Shift the vertex into the volume. + // We can recover this edge if the subfaces are not recovered yet. + if (!checksubfaceflag) { + // Remove the vertex from the surface mesh. + // This will re-create the segment [lpt, rpt] and re-triangulate + // all the facets at the segment. + // Detach the subsegments from their surrounding tets. + for (i = 0; i < 2; i++) { + checkseg = (i == 0) ? leftseg : rightseg; + sstpivot1(checkseg, neightet); + spintet = neightet; + while (1) { + tssdissolve1(spintet); + fnextself(spintet); + if (spintet.tet == neightet.tet) break; + } + sstdissolve1(checkseg); + } // i + slawson = 1; // Do lawson flip after removal. + spivot(rightseg, parentsh); // 'rightseg' has p as its origin. + sremovevertex(steinerpt, &parentsh, &rightseg, slawson); + // Clear the list for new subfaces. + caveshbdlist->restart(); + // Insert the new segment. + assert(org(searchtet) == lpt); + assert(dest(searchtet) == rpt); + sstbond1(rightseg, searchtet); + spintet = searchtet; + while (1) { + tsspivot1(spintet, checkseg); // FOR DEBUG ONLY + assert(checkseg.sh == NULL); // FOR DEBUG ONLY + tssbond1(spintet, rightseg); + fnextself(spintet); + if (spintet.tet == searchtet.tet) break; + } + // The Steiner point has been shifted into the volume. + setpointtype(steinerpt, FREEVOLVERTEX); + st_segref_count--; + st_volref_count++; + return 1; + } // if (!checksubfaceflag) + } // if (getedge(...)) + } // if (vt == FREESEGVERTEX) + } // if (!removeflag) + + if (!removeflag) { + return 0; } - // 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(); - oldtetlist = new list*[n]; - newtetlist = new list*[n]; - oldshlist = new list*[n]; - newshlist = new list*[n]; - newpt = new point[n]; - for (i = 0; i < n; i++) { - oldtetlist[i] = (list *) NULL; - newtetlist[i] = (list *) NULL; - oldshlist[i] = (list *) NULL; - 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); - setsorg(newseg, pa); - setsdest(newseg, pb); - // ab gets the same mark and segment type as ap. - setshellmark(newseg, shellmark(*supseg)); - setshelltype(newseg, shelltype(*supseg)); - if (b->quality && varconstraint) { - // Copy the areabound into the new subsegment. - setareabound(newseg, areabound(*supseg)); - } - // Save the old connection at a. - senext2(*supseg, prevseg); - spivotself(prevseg); - if (prevseg.sh != dummysh) { - prevseg.shver = 0; - if (sdest(prevseg) != pa) sesymself(prevseg); - assert(sdest(prevseg) == pa); - senextself(prevseg); - senext2self(newseg); - sbond(newseg, prevseg); - newseg.shver = 0; - } - // Save the old connection at b. - senext(nsupseg, nextseg); - spivotself(nextseg); - if (nextseg.sh != dummysh) { - nextseg.shver = 0; - if (sorg(nextseg) != pb) sesymself(nextseg); - assert(sorg(nextseg) == pb); - senext2self(nextseg); - senextself(newseg); - sbond(newseg, nextseg); - 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]; - // Allocate spaces for C_i(p). - oldshlist[i] = new list(sizeof(face), NULL, 256); - newshlist[i] = new list(sizeof(face), NULL, 256); - // Get the subs of C_i(p). - oldshlist[i]->append(&spinsh); - formstarpolygon(suppt, oldshlist[i], ptlist); - // Find the edges of C_i(p). It DOES NOT form a closed polygon. - for (j = 0; j < oldshlist[i]->len(); j++) { - oldsh = * (face *)(* (oldshlist[i]))[j]; - cons = (point *) conlist->append(NULL); - cons[0] = sorg(oldsh); - cons[1] = sdest(oldsh); - } - // The C_i(p) isn't closed without ab. Add it to it. - cons = (point *) conlist->append(NULL); - cons[0] = pa; - cons[1] = pb; - // Re-triangulate C_i(p). - shmark = shellmark(spinsh); - triangulate(shmark, b->epsilon, ptlist, conlist, 0, NULL, viri, flipque); - // Get new subs of C_i(p), remove protected segments. - retrievenewsubs(newshlist[i], true); - // Substitute old C_i(p) with the new C_i(p). !IT IS NOT COMPLETE! - replacepolygonsubs(oldshlist[i], newshlist[i]); - // Find the new subface s having edge ab. - for (j = 0; j < newshlist[i]->len(); j++) { - segsh1 = * (face *)(* (newshlist[i]))[j]; - for (k = 0; k < 3; k++) { - if (((sorg(segsh1) == pa) && (sdest(segsh1) == pb)) || - ((sorg(segsh1) == pb) && (sdest(segsh1) == pa))) break; - senextself(segsh1); - } - if (k < 3) break; // Found. - } - assert(j < newshlist[i]->len()); // ab must exist. - // Bond s and ab together. The C_i(p) is completedly substituted. - ssbond(segsh1, newseg); - // Save s for forming the face ring of ab. - newsegshlist->append(&segsh1); - // Clear work lists. - ptlist->clear(); - conlist->clear(); - flipque->clear(); - viri->restart(); - } - // Form the face ring of ab. - for (i = 0; i < newsegshlist->len(); i++) { - segsh1 = * (face *)(* newsegshlist)[i]; - if ((i + 1) == newsegshlist->len()) { - segsh2 = * (face *)(* newsegshlist)[0]; - } else { - segsh2 = * (face *)(* newsegshlist)[i + 1]; - } - 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. - - // Suppress p in all B(p). B_i(p) is looped wrt the right-hand rule of ab. - 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. - // Get a tet t of B_i(p). - stpivot(spinsh, oldtet); - // Is B_i(p) empty? - if (oldtet.tet == dummytet) continue; - // Allocate spaces for B_i(p). - oldtetlist[i] = new list(sizeof(triface), NULL, 256); - newtetlist[i] = new list(sizeof(triface), NULL, 256); - // Find all tets of old B_i(p). - oldtetlist[i]->append(&oldtet); - formstarpolyhedron(suppt, oldtetlist[i], ptlist, false); - // Infect tets of old B_i(p) (they're going to be deleted). - for (j = 0; j < oldtetlist[i]->len(); j++) { - oldtet = * (triface *)(* (oldtetlist[i]))[j]; - infect(oldtet); - } - // Collect new subfaces (of two facets) bounded B_i(p). - for (k = 0; k < 2; k++) { - if ((i + k) < spinshlist->len()) { - pnewshlist = newshlist[i + k]; - segsh1 = * (face *)(* spinshlist)[i + k]; - } else { - pnewshlist = newshlist[0]; - segsh1 = * (face *)(* spinshlist)[0]; + assert(org(searchtet) == steinerpt); + + if (vt == FREESEGVERTEX) { + // Detach the subsegments from their surronding tets. + for (i = 0; i < 2; i++) { + checkseg = (i == 0) ? leftseg : rightseg; + sstpivot1(checkseg, neightet); + spintet = neightet; + while (1) { + tssdissolve1(spintet); + fnextself(spintet); + if (spintet.tet == neightet.tet) break; } - /*// 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++) { - dnewshlist->append((face *)(* pnewshlist)[j]); - } - } - // Tetrahedralize B_i(p). - success = constrainedcavity(&oldtet, dnewshlist, oldtetlist[i], ptlist, - frontlist, misfrontlist, newtetlist[i], gluetetlist, glueshlist, - 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]); - // 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; + sstdissolve1(checkseg); + } // i + if (checksubfaceflag) { + // Detach the subfaces at the subsegments from their attached tets. + for (i = 0; i < 2; i++) { + checkseg = (i == 0) ? leftseg : rightseg; + spivot(checkseg, parentsh); + if (parentsh.sh != NULL) { + spinsh = parentsh; + while (1) { + stpivot(spinsh, neightet); + if (neightet.tet != NULL) { + tsdissolve(neightet); + } + sesymself(spinsh); + stpivot(spinsh, neightet); + if (neightet.tet != NULL) { + tsdissolve(neightet); + } + stdissolve(spinsh); + spivotself(spinsh); // Go to the next subface. + if (spinsh.sh == parentsh.sh) break; + } } - } else { - // Fail to relocate p. Clean fake tets and quit this option. - deallocfaketets(frontlist); - pointdealloc(newpt[i]); - newpt[i] = (point) NULL; - assert(newtetlist[i]->len() == 0); - } - } - if (!success && noreloc) { - // Failed and no point relocation. Clean fake tets. - deallocfaketets(frontlist); - } - // Clear work lists. - dnewshlist->clear(); - ptlist->clear(); - frontlist->clear(); - misfrontlist->clear(); - // Do not clear gluetetlist. // gluetetlist->clear(); - // Do not clear glueshlist. - flipque->clear(); + } // i + } // if (checksubfaceflag) } - if (success) { - // 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)); - // Delete old segments ap, pb. - shellfacedealloc(subsegs, supseg->sh); - shellfacedealloc(subsegs, nsupseg.sh); - // Delete subs of old C_i(p). - for (i = 0; i < spinshlist->len(); i++) { - for (j = 0; j < oldshlist[i]->len(); j++) { - oldsh = * (face *)(* (oldshlist[i]))[j]; - if (j == 0) { - // Update 'hullsize' if C_i(p) is on the hull. - stpivot(oldsh, oldtet); - if (oldtet.tet != dummytet) { - sesymself(oldsh); - stpivot(oldsh, oldtet); - } - if (oldtet.tet == dummytet) { - // Update 'hullsize'. - k = oldshlist[i]->len() - newshlist[i]->len(); - assert(k > 0); - hullsize -= k; - } - } - shellfacedealloc(subfaces, oldsh.sh); - } - } - // Delete tets old B_i(p). - for (i = 0; i < spinshlist->len(); i++) { - // Delete them if it is not empty. - if (oldtetlist[i] != (list *) NULL) { - for (j = 0; j < oldtetlist[i]->len(); j++) { - oldtet = * (triface *)(* (oldtetlist[i]))[j]; - assert(!isdead(&oldtet)); - tetrahedrondealloc(oldtet.tet); - } - } - } - if (optflag) { - for (i = 0; i < spinshlist->len(); i++) { - // Check for new bad-quality tets. - if (newtetlist[i] != (list *) NULL) { - for (j = 0; j < newtetlist[i]->len(); j++) { - newtet = * (triface *)(* (newtetlist[i]))[j]; - if (!isdead(&newtet)) checktet4opt(&newtet, true); - } - } - } - } - // 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); - } - } + if (loc == INTETRAHEDRON) { + // Collect the four tets containing 'p'. + fliptets = new triface[4]; + fliptets[0] = searchtet; // [p,d,a,b] + for (i = 0; i < 2; i++) { + fnext(fliptets[i], fliptets[i+1]); // [p,d,b,c], [p,d,c,a] + } + eprev(fliptets[0], fliptets[3]); + fnextself(fliptets[3]); // it is [a,p,b,c] + eprevself(fliptets[3]); + esymself(fliptets[3]); // [a,b,c,p]. + // Remove p by a 4-to-1 flip. + //flip41(fliptets, 1, 0, 0); + flip41(fliptets, 1, &fc); + //recenttet = fliptets[0]; + } else if (loc == ONFACE) { + // Let the original two tets be [a,b,c,d] and [b,a,c,e]. And p is in + // face [a,b,c]. Let 'searchtet' be the tet [p,d,a,b]. + // Collect the six tets containing 'p'. + fliptets = new triface[6]; + fliptets[0] = searchtet; // [p,d,a,b] + for (i = 0; i < 2; i++) { + fnext(fliptets[i], fliptets[i+1]); // [p,d,b,c], [p,d,c,a] + } + eprev(fliptets[0], fliptets[3]); + fnextself(fliptets[3]); // [a,p,b,e] + esymself(fliptets[3]); // [p,a,e,b] + eprevself(fliptets[3]); // [e,p,a,b] + for (i = 3; i < 5; i++) { + fnext(fliptets[i], fliptets[i+1]); // [e,p,b,c], [e,p,c,a] + } + if (vt == FREEFACETVERTEX) { + // We need to determine the location of three subfaces at p. + valence = 0; // Re-use it. + // Check if subfaces are all located in the lower three tets. + // i.e., [e,p,a,b], [e,p,b,c], and [e,p,c,a]. + for (i = 3; i < 6; i++) { + if (issubface(fliptets[i])) valence++; + } + if (valence > 0) { + assert(valence == 2); + // We must do 3-to-2 flip in the upper part. We simply re-arrange + // the six tets. + for (i = 0; i < 3; i++) { + esym(fliptets[i+3], wrktets[i]); + esym(fliptets[i], fliptets[i+3]); + fliptets[i] = wrktets[i]; + } + // Swap the last two pairs, i.e., [1]<->[[2], and [4]<->[5] + wrktets[1] = fliptets[1]; + fliptets[1] = fliptets[2]; + fliptets[2] = wrktets[1]; + wrktets[1] = fliptets[4]; + fliptets[4] = fliptets[5]; + fliptets[5] = wrktets[1]; + } + } + // Remove p by a 6-to-2 flip, which is a combination of two flips: + // a 3-to-2 (deletes the edge [e,p]), and + // a 4-to-1 (deletes the vertex p). + // First do a 3-to-2 flip on [e,p,a,b],[e,p,b,c],[e,p,c,a]. It creates + // two new tets: [a,b,c,p] and [b,a,c,e]. The new tet [a,b,c,p] is + // degenerate (has zero volume). It will be deleted in the followed + // 4-to-1 flip. + //flip32(&(fliptets[3]), 1, 0, 0); + flip32(&(fliptets[3]), 1, &fc); + // Second do a 4-to-1 flip on [p,d,a,b],[p,d,b,c],[p,d,c,a],[a,b,c,p]. + // This creates a new tet [a,b,c,d]. + //flip41(fliptets, 1, 0, 0); + flip41(fliptets, 1, &fc); + //recenttet = fliptets[0]; + } else if (loc == ONEDGE) { + // Let the original edge be [e,d] and p is in [e,d]. Assume there are n + // tets sharing at edge [e,d] originally. We number the link vertices + // of [e,d]: p_0, p_1, ..., p_n-1. 'searchtet' is [p,d,p_0,p_1]. + // Count the number of tets at edge [e,p] and [p,d] (this is n). + n = 0; + spintet = searchtet; + while (1) { + n++; + fnextself(spintet); + if (spintet.tet == searchtet.tet) break; + } + assert(n >= 3); + // Collect the 2n tets containing 'p'. + fliptets = new triface[2 * n]; + fliptets[0] = searchtet; // [p,b,p_0,p_1] + for (i = 0; i < (n - 1); i++) { + fnext(fliptets[i], fliptets[i+1]); // [p,d,p_i,p_i+1]. + } + eprev(fliptets[0], fliptets[n]); + fnextself(fliptets[n]); // [p_0,p,p_1,e] + esymself(fliptets[n]); // [p,p_0,e,p_1] + eprevself(fliptets[n]); // [e,p,p_0,p_1] + for (i = n; i < (2 * n - 1); i++) { + fnext(fliptets[i], fliptets[i+1]); // [e,p,p_i,p_i+1]. + } + // Remove p by a 2n-to-n flip, it is a sequence of n flips: + // - Do a 2-to-3 flip on + // [p_0,p_1,p,d] and + // [p,p_1,p_0,e]. + // This produces: + // [e,d,p_0,p_1], + // [e,d,p_1,p] (degenerated), and + // [e,d,p,p_0] (degenerated). + wrktets[0] = fliptets[0]; // [p,d,p_0,p_1] + eprevself(wrktets[0]); // [p_0,p,d,p_1] + esymself(wrktets[0]); // [p,p_0,p_1,d] + enextself(wrktets[0]); // [p_0,p_1,p,d] [0] + wrktets[1] = fliptets[n]; // [e,p,p_0,p_1] + enextself(wrktets[1]); // [p,p_0,e,p_1] + esymself(wrktets[1]); // [p_0,p,p_1,e] + eprevself(wrktets[1]); // [p_1,p_0,p,e] [1] + //flip23(wrktets, 1, 0, 0); + flip23(wrktets, 1, &fc); + // Save the new tet [e,d,p,p_0] (degenerated). + fliptets[n] = wrktets[2]; + // Save the new tet [e,d,p_0,p_1]. + fliptets[0] = wrktets[0]; + // - Repeat from i = 1 to n-2: (n - 2) flips + // - Do a 3-to-2 flip on + // [p,p_i,d,e], + // [p,p_i,e,p_i+1], and + // [p,p_i,p_i+1,d]. + // This produces: + // [d,e,p_i+1,p_i], and + // [e,d,p_i+1,p] (degenerated). + for (i = 1; i < (n - 1); i++) { + wrktets[0] = wrktets[1]; // [e,d,p_i,p] (degenerated). + enextself(wrktets[0]); // [d,p_i,e,p] (...) + esymself(wrktets[0]); // [p_i,d,p,e] (...) + eprevself(wrktets[0]); // [p,p_i,d,e] (degenerated) [0]. + wrktets[1] = fliptets[n+i]; // [e,p,p_i,p_i+1] + enextself(wrktets[1]); // [p,p_i,e,p_i+1] [1] + wrktets[2] = fliptets[i]; // [p,d,p_i,p_i+1] + eprevself(wrktets[2]); // [p_i,p,d,p_i+1] + esymself(wrktets[2]); // [p,p_i,p_i+1,d] [2] + //flip32(wrktets, 1, 0, 0); + flip32(wrktets, 1, &fc); + // Save the new tet [e,d,p_i,p_i+1]. // FOR DEBUG ONLY + fliptets[i] = wrktets[0]; // [d,e,p_i+1,p_i] // FOR DEBUG ONLY + esymself(fliptets[i]); // [e,d,p_i,p_i+1] // FOR DEBUG ONLY + } + // - Do a 4-to-1 flip on + // [p,p_0,e,d], [d,e,p_0,p], + // [p,p_0,d,p_n-1], [e,p_n-1,p_0,p], + // [p,p_0,p_n-1,e], [p_0,p_n-1,d,p], and + // [e,d,p_n-1,p]. + // This produces + // [e,d,p_n-1,p_0] and + // deletes p. + wrktets[3] = wrktets[1]; // [e,d,p_n-1,p] (degenerated) [3] + wrktets[0] = fliptets[n]; // [e,d,p,p_0] (degenerated) + eprevself(wrktets[0]); // [p,e,d,p_0] (...) + esymself(wrktets[0]); // [e,p,p_0,d] (...) + enextself(wrktets[0]); // [p,p_0,e,d] (degenerated) [0] + wrktets[1] = fliptets[n-1]; // [p,d,p_n-1,p_0] + esymself(wrktets[1]); // [d,p,p_0,p_n-1] + enextself(wrktets[1]); // [p,p_0,d,p_n-1] [1] + wrktets[2] = fliptets[2*n-1]; // [e,p,p_n-1,p_0] + enextself(wrktets[2]); // [p_p_n-1,e,p_0] + esymself(wrktets[2]); // [p_n-1,p,p_0,e] + enextself(wrktets[2]); // [p,p_0,p_n-1,e] [2] + //flip41(wrktets, 1, 0, 0); + flip41(wrktets, 1, &fc); + // Save the new tet [e,d,p_n-1,p_0] // FOR DEBUG ONLY + fliptets[n-1] = wrktets[0]; // [e,d,p_n-1,p_0] // FOR DEBUG ONLY + //recenttet = fliptets[0]; } else { - // p is not suppressed. Recover the original state. - unsupverts++; - // Restore old connection at a. - senext2(*supseg, prevseg); - spivotself(prevseg); - if (prevseg.sh != dummysh) { - prevseg.shver = 0; - if (sdest(prevseg) != pa) sesymself(prevseg); - assert(sdest(prevseg) == pa); - senextself(prevseg); - senext2self(*supseg); - sbond(*supseg, prevseg); - senextself(*supseg); // Restore original state. - assert(supseg->shver < 2); - } - // Restore old connection at b. - senext(nsupseg, nextseg); - spivotself(nextseg); - if (nextseg.sh != dummysh) { - nextseg.shver = 0; - if (sorg(nextseg) != pb) sesymself(nextseg); - assert(sorg(nextseg) == pb); - senext2self(nextseg); - senextself(nsupseg); - sbond(nsupseg, nextseg); - // nsupseg.shver = 0; - senext2self(nsupseg); // Restore original state - assert(nsupseg.shver < 2); - } - // Delete the new segment ab. - shellfacedealloc(subsegs, newseg.sh); - // Restore old C_i(p). - for (i = 0; i < spinshlist->len(); i++) { - replacepolygonsubs(newshlist[i], oldshlist[i]); - // Delete subs of the new C_i(p) - for (j = 0; j < newshlist[i]->len(); j++) { - newsh = * (face *)(* (newshlist[i]))[j]; - 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); + assert(0); // Unknown location. + } // if (iloc == ...) + + delete [] fliptets; + + if (vt == FREESEGVERTEX) { + // Remove the vertex from the surface mesh. + // This will re-create the segment [lpt, rpt] and re-triangulate + // all the facets at the segment. + // Only do lawson flip when subfaces are not recovery yet. + slawson = (checksubfaceflag ? 0 : 1); + spivot(rightseg, parentsh); // 'rightseg' has p as its origin. + sremovevertex(steinerpt, &parentsh, &rightseg, slawson); + + // The original segment is returned in 'rightseg'. + rightseg.shver = 0; + assert(sorg(rightseg) == lpt); + assert(sdest(rightseg) == rpt); + + // Insert the new segment. + point2tetorg(lpt, searchtet); + finddirection(&searchtet, rpt); + assert(dest(searchtet) == rpt); + sstbond1(rightseg, searchtet); + spintet = searchtet; + while (1) { + tsspivot1(spintet, checkseg); // FOR DEBUG ONLY + assert(checkseg.sh == NULL); // FOR DEBUG ONLY + tssbond1(spintet, rightseg); + fnextself(spintet); + if (spintet.tet == searchtet.tet) break; + } + + if (checksubfaceflag) { + // Insert subfaces at segment [lpt,rpt] into the tetrahedralization. + spivot(rightseg, parentsh); + if (parentsh.sh != NULL) { + spinsh = parentsh; + while (1) { + if (sorg(spinsh) != lpt) { + sesymself(spinsh); + assert(sorg(spinsh) == lpt); } - sesymself(newsh); - } - shellfacedealloc(subfaces, newsh.sh); - } - } - // Restore old B_i(p). - for (i = 0; i < spinshlist->len(); i++) { - if (oldtetlist[i] != (list *) NULL) { - // Uninfect tets of old B_i(p). - for (j = 0; j < oldtetlist[i]->len(); j++) { - oldtet = * (triface *)(* (oldtetlist[i]))[j]; - assert(infected(oldtet)); - uninfect(oldtet); - } - // Has it been re-meshed? - // if (newtetlist[i]->len() > 0) { - // Restore the old B_i(p). - restorepolyhedron(oldtetlist[i]); - // Delete tets of the new B_i(p); - for (j = 0; j < newtetlist[i]->len(); j++) { - newtet = * (triface *)(* (newtetlist[i]))[j]; - // Some new tets may already be deleted (by carvecavity()). - if (!isdead(&newtet)) { - tetrahedrondealloc(newtet.tet); + assert(sdest(spinsh) == rpt); + apexpt = sapex(spinsh); + // Find the adjacent tet of [lpt,rpt,apexpt]; + spintet = searchtet; + while (1) { + if (apex(spintet) == apexpt) { + tsbond(spintet, spinsh); + sesymself(spinsh); // Get to another side of this face. + fsym(spintet, neightet); + tsbond(neightet, spinsh); + sesymself(spinsh); // Get back to the original side. + break; } + fnextself(spintet); + assert(spintet.tet != searchtet.tet); + //if (spintet.tet == searchtet.tet) break; } - // } - // Dealloc newpt[i] if it exists. - if (newpt[i] != (point) NULL) { - pointdealloc(newpt[i]); - relverts--; - } - } - } - // 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)); + spivotself(spinsh); + if (spinsh.sh == parentsh.sh) break; } } + } // if (checksubfaceflag) + + // Clear the set of new subfaces. + caveshbdlist->restart(); + } // if (vt == FREESEGVERTEX) + + // The point has been removed. + if (pointtype(steinerpt) != UNUSEDVERTEX) { + setpointtype(steinerpt, UNUSEDVERTEX); + unuverts++; + } + if (vt != VOLVERTEX) { + // Update the correspinding counters. + if (vt == FREESEGVERTEX) { + st_segref_count--; + } else if (vt == FREEFACETVERTEX) { + st_facref_count--; + } else if (vt == FREEVOLVERTEX) { + st_volref_count--; } + if (steinerleft > 0) steinerleft++; } - // Delete work lists. - delete [] newpt; // BUG fixed. Thanks dmyan, June 23, 2007. - delete dnewshlist; - for (i = 0; i < spinshlist->len(); i++) { - delete oldshlist[i]; - delete newshlist[i]; - } - delete [] oldshlist; - delete [] newshlist; - for (i = 0; i < spinshlist->len(); i++) { - if (oldtetlist[i] != (list *) NULL) { - delete oldtetlist[i]; - delete newtetlist[i]; - } - } - delete [] oldtetlist; - delete [] newtetlist; - delete gluetetlist; - delete glueshlist; - // Clear work lists. - newsegshlist->clear(); - spinshlist->clear(); - - return success; + return 1; } /////////////////////////////////////////////////////////////////////////////// // // -// suppressvolpoint() Suppress a point inside mesh. // -// // -// The point p = org(suptet) is inside the mesh and will be suppressed from // -// the mesh. Note that p may not be suppressed. // -// // -// 'optflag' is used for mesh optimization. If it is set, after removing p, // -// test the object function on each new tet, queue bad tets. // +// suppressbdrysteinerpoint() Suppress a boundary Steiner point // // // /////////////////////////////////////////////////////////////////////////////// -bool tetgenmesh::suppressvolpoint(triface* suptet, list* frontlist, - list* misfrontlist, list* ptlist, queue* flipque, bool optflag) +int tetgenmesh::suppressbdrysteinerpoint(point steinerpt) { - 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; - bool success; - int j, k; - - // 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; - myflipque = (queue *) NULL; - if (frontlist == (list *) NULL) { - myfrontlist = new list(sizeof(triface), NULL, 256); - frontlist = myfrontlist; - mymisfrontlist = new list(sizeof(triface), NULL, 256); - misfrontlist = mymisfrontlist; - myptlist = new list(sizeof(point *), NULL, 256); - ptlist = myptlist; - myflipque = new queue(sizeof(badface)); - flipque = myflipque; - } - - suppt = org(*suptet); - oldtet = *suptet; - success = true; // Assume p can be suppressed. + face parentsh, spinsh, *parysh; + face leftseg, rightseg; + point lpt = NULL, rpt = NULL; + int i; - if (b->verbose > 1) { - printf(" Remove point %d in mesh.\n", pointmark(suppt)); + verttype vt = pointtype(steinerpt); + + if (vt == FREESEGVERTEX) { + sdecode(point2sh(steinerpt), leftseg); + leftseg.shver = 0; + if (sdest(leftseg) == steinerpt) { + senext(leftseg, rightseg); + spivotself(rightseg); + assert(rightseg.sh != NULL); + rightseg.shver = 0; + assert(sorg(rightseg) == steinerpt); + } else { + assert(sorg(leftseg) == steinerpt); + rightseg = leftseg; + senext2(rightseg, leftseg); + spivotself(leftseg); + assert(leftseg.sh != NULL); + leftseg.shver = 0; + assert(sdest(leftseg) == steinerpt); + } + lpt = sorg(leftseg); + rpt = sdest(rightseg); + if (b->verbose > 2) { + printf(" Suppressing Steiner point %d in segment (%d, %d).\n", + pointmark(steinerpt), pointmark(lpt), pointmark(rpt)); + } + // Get all subfaces at the left segment [lpt, steinerpt]. + spivot(leftseg, parentsh); + spinsh = parentsh; + while (1) { + cavesegshlist->newindex((void **) &parysh); + *parysh = spinsh; + // Orient the face consistently. + if (sorg(*parysh)!= sorg(parentsh)) sesymself(*parysh); + spivotself(spinsh); + if (spinsh.sh == NULL) break; + if (spinsh.sh == parentsh.sh) break; + } + if (cavesegshlist->objects < 2) { + // It is a single segment. Not handle it yet. + cavesegshlist->restart(); + return 0; + } + } else if (vt == FREEFACETVERTEX) { + if (b->verbose > 2) { + printf(" Suppressing Steiner point %d from facet.\n", + pointmark(steinerpt)); + } + sdecode(point2sh(steinerpt), parentsh); + // A facet Steiner point. There are exactly two sectors. + for (i = 0; i < 2; i++) { + cavesegshlist->newindex((void **) &parysh); + *parysh = parentsh; + sesymself(parentsh); + } + } else { + return 0; } - // 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); - } - } - oldtetlist->clear(); // Do not delete them. - collapverts++; - success = true; + triface searchtet, neightet, *parytet; + point pa, pb, pc, pd; + REAL v1[3], v2[3], len, u; + + REAL startpt[3] = {0,}, samplept[3] = {0,}, candpt[3] = {0,}; + REAL ori, minvol, smallvol; + int samplesize; + int it, j, k; + + int n = (int) cavesegshlist->objects; + point *newsteiners = new point[n]; + for (i = 0; i < n; i++) newsteiners[i] = NULL; + + // Search for each sector an interior vertex. + for (i = 0; i < cavesegshlist->objects; i++) { + parysh = (face *) fastlookup(cavesegshlist, i); + stpivot(*parysh, searchtet); + // Skip it if it is outside. + if (ishulltet(searchtet)) continue; + // Get the "half-ball". Tets in 'cavetetlist' all contain 'steinerpt' as + // opposite. Subfaces in 'caveshlist' all contain 'steinerpt' as apex. + // Moreover, subfaces are oriented towards the interior of the ball. + setpoint2tet(steinerpt, encode(searchtet)); + getvertexstar(0, steinerpt, cavetetlist, NULL, caveshlist); + // Calculate the searching vector. + pa = sorg(*parysh); + pb = sdest(*parysh); + pc = sapex(*parysh); + facenormal(pa, pb, pc, v1, 1, NULL); + len = sqrt(dot(v1, v1)); + assert(len > 0.0); + v1[0] /= len; + v1[1] /= len; + v1[2] /= len; + if (vt == FREESEGVERTEX) { + parysh = (face *) fastlookup(cavesegshlist, (i + 1) % n); + pd = sapex(*parysh); + facenormal(pb, pa, pd, v2, 1, NULL); + len = sqrt(dot(v2, v2)); + assert(len > 0.0); + v2[0] /= len; + v2[1] /= len; + v2[2] /= len; + // Average the two vectors. + v1[0] = 0.5 * (v1[0] + v2[0]); + v1[1] = 0.5 * (v1[1] + v2[1]); + v1[2] = 0.5 * (v1[2] + v2[2]); + } + // Search the intersection of the ray starting from 'steinerpt' to + // the search direction 'v1' and the shell of the half-ball. + // - Construct an endpoint. + len = distance(pa, pb); + v2[0] = steinerpt[0] + len * v1[0]; + v2[1] = steinerpt[1] + len * v1[1]; + v2[2] = steinerpt[2] + len * v1[2]; + for (j = 0; j < cavetetlist->objects; j++) { + parytet = (triface *) fastlookup(cavetetlist, j); + pa = org(*parytet); + pb = dest(*parytet); + pc = apex(*parytet); + // Test if the ray startpt->v2 lies in the cone: where 'steinerpt' + // is the apex, and three sides are defined by the triangle + // [pa, pb, pc]. + ori = orient3d(steinerpt, pa, pb, v2); + if (ori >= 0) { + ori = orient3d(steinerpt, pb, pc, v2); + if (ori >= 0) { + ori = orient3d(steinerpt, pc, pa, v2); + if (ori >= 0) { + // Found! Calculate the intersection. + planelineint(pa, pb, pc, steinerpt, v2, startpt, &u); + assert(u != 0.0); + break; + } + } + } + } // j + assert(j < cavetetlist->objects); // There must be an intersection. + // Close the ball by adding the subfaces. + for (j = 0; j < caveshlist->objects; j++) { + parysh = (face *) fastlookup(caveshlist, j); + stpivot(*parysh, neightet); + cavetetlist->newindex((void **) &parytet); + *parytet = neightet; } - } - 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); + // Search a best point inside the segment [startpt, steinerpt]. + it = 0; + samplesize = 100; + v1[0] = steinerpt[0] - startpt[0]; + v1[1] = steinerpt[1] - startpt[1]; + v1[2] = steinerpt[2] - startpt[2]; + minvol = -1.0; + while (it < 3) { + for (j = 1; j < samplesize - 1; j++) { + samplept[0] = startpt[0] + ((REAL) j / (REAL) samplesize) * v1[0]; + samplept[1] = startpt[1] + ((REAL) j / (REAL) samplesize) * v1[1]; + samplept[2] = startpt[2] + ((REAL) j / (REAL) samplesize) * v1[2]; + // Find the minimum volume for 'samplept'. + smallvol = -1; + for (k = 0; k < cavetetlist->objects; k++) { + parytet = (triface *) fastlookup(cavetetlist, k); + pa = org(*parytet); + pb = dest(*parytet); + pc = apex(*parytet); + ori = orient3d(pb, pa, pc, samplept); + if (ori <= 0) { + break; // An invalid tet. + } + if (smallvol == -1) { + smallvol = ori; + } else { + if (ori < smallvol) smallvol = ori; + } + } // k + if (k == cavetetlist->objects) { + // Found a valid point. Remember it. + if (minvol == -1.0) { + candpt[0] = samplept[0]; + candpt[1] = samplept[1]; + candpt[2] = samplept[2]; + minvol = smallvol; + } else { + if (minvol < smallvol) { + // It is a better location. Remember it. + candpt[0] = samplept[0]; + candpt[1] = samplept[1]; + candpt[2] = samplept[2]; + minvol = smallvol; + } else { + // No improvement of smallest volume. + // Since we are searching along the line [startpt, steinerpy], + // The smallest volume can only be decreased later. + break; + } + } + } + } // j + if (minvol > 0) break; + samplesize *= 10; + it++; + } // while (it < 3) + if (minvol == -1.0) { + // Failed to find a valid point. + cavetetlist->restart(); + caveshlist->restart(); + break; } - if (optflag) { - // Check for new bad tets. - for (j = 0; j < newtetlist->len(); j++) { - newtet = * (triface *)(* newtetlist)[j]; - if (!isdead(&newtet)) checktet4opt(&newtet, true); + // Create a new Steiner point inside this section. + makepoint(&(newsteiners[i]), FREEVOLVERTEX); + newsteiners[i][0] = candpt[0]; + newsteiners[i][1] = candpt[1]; + newsteiners[i][2] = candpt[2]; + cavetetlist->restart(); + caveshlist->restart(); + } // i + + if (i < cavesegshlist->objects) { + // Failed to suppress the vertex. + for (; i > 0; i--) { + if (newsteiners[i - 1] != NULL) { + pointdealloc(newsteiners[i - 1]); } } - } 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)); - } - } - } - - // 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; -} + delete [] newsteiners; + cavesegshlist->restart(); + return 0; + } -/////////////////////////////////////////////////////////////////////////////// -// // -// removesteiners2() Remove Steiner points. // -// // -/////////////////////////////////////////////////////////////////////////////// + // Remove p from the segment or the facet. + triface newtet, newface, spintet; + face newsh, neighsh; + face *splitseg, checkseg; + int slawson = 0; // Do not do flip afterword. + int t1ver; -void tetgenmesh::removesteiners2() -{ - 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; - int oldnum, rmstein; - int unsupbdrycount; // Count the unsuppressed boundary Steiner points. - int iter, i, j; + if (vt == FREESEGVERTEX) { + // Detach 'leftseg' and 'rightseg' from their adjacent tets. + // These two subsegments will be deleted. + sstpivot1(leftseg, neightet); + spintet = neightet; + while (1) { + tssdissolve1(spintet); + fnextself(spintet); + if (spintet.tet == neightet.tet) break; + } + sstpivot1(rightseg, neightet); + spintet = neightet; + while (1) { + tssdissolve1(spintet); + fnextself(spintet); + if (spintet.tet == neightet.tet) break; + } + } + + // Loop through all sectors bounded by facets at this segment. + // Within each sector, create a new Steiner point 'np', and replace 'p' + // by 'np' for all tets in this sector. + for (i = 0; i < cavesegshlist->objects; i++) { + parysh = (face *) fastlookup(cavesegshlist, i); + // 'parysh' is the face [lpt, steinerpt, #]. + stpivot(*parysh, neightet); + // Get all tets in this sector. + setpoint2tet(steinerpt, encode(neightet)); + getvertexstar(0, steinerpt, cavetetlist, NULL, caveshlist); + if (!ishulltet(neightet)) { + // Within each tet in the ball, replace 'p' by 'np'. + for (j = 0; j < cavetetlist->objects; j++) { + parytet = (triface *) fastlookup(cavetetlist, j); + setoppo(*parytet, newsteiners[i]); + } // j + // Point to a parent tet. + parytet = (triface *) fastlookup(cavetetlist, 0); + setpoint2tet(newsteiners[i], (tetrahedron) (parytet->tet)); + st_volref_count++; + if (steinerleft > 0) steinerleft--; + } + // Disconnect the set of boundary faces. They're temporarily open faces. + // They will be connected to the new tets after 'p' is removed. + for (j = 0; j < caveshlist->objects; j++) { + // Get a boundary face. + parysh = (face *) fastlookup(caveshlist, j); + stpivot(*parysh, neightet); + //assert(apex(neightet) == newpt); + // Clear the connection at this face. + dissolve(neightet); + tsdissolve(neightet); + } + // Clear the working lists. + cavetetlist->restart(); + caveshlist->restart(); + } // i + cavesegshlist->restart(); - if (!b->quiet) { - printf("Removing Steiner points.\n"); + if (vt == FREESEGVERTEX) { + spivot(rightseg, parentsh); // 'rightseg' has p as its origin. + splitseg = &rightseg; + } else { + if (sdest(parentsh) == steinerpt) { + senextself(parentsh); + } else if (sapex(parentsh) == steinerpt) { + senext2self(parentsh); + } + assert(sorg(parentsh) == steinerpt); + splitseg = NULL; } + sremovevertex(steinerpt, &parentsh, splitseg, slawson); - // Initialize work lists. - frontlist = new list(sizeof(triface), NULL); - misfrontlist = new list(sizeof(triface), NULL); - spinshlist = new list(sizeof(face), NULL); - newsegshlist = new list(sizeof(face), NULL); - ptlist = new list(sizeof(point *), NULL); - 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; + if (vt == FREESEGVERTEX) { + // The original segment is returned in 'rightseg'. + rightseg.shver = 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); + // For each new subface, create two new tets at each side of it. + // Both of the two new tets have its opposite be dummypoint. + for (i = 0; i < caveshbdlist->objects; i++) { + parysh = (face *) fastlookup(caveshbdlist, i); + sinfect(*parysh); // Mark it for connecting new tets. + newsh = *parysh; + pa = sorg(newsh); + pb = sdest(newsh); + pc = sapex(newsh); + maketetrahedron(&newtet); + maketetrahedron(&neightet); + setvertices(newtet, pa, pb, pc, dummypoint); + setvertices(neightet, pb, pa, pc, dummypoint); + bond(newtet, neightet); + tsbond(newtet, newsh); + sesymself(newsh); + tsbond(neightet, newsh); + } + // Temporarily increase the hullsize. + hullsize += (caveshbdlist->objects * 2l); + + if (vt == FREESEGVERTEX) { + // Connecting new tets at the recovered segment. + spivot(rightseg, parentsh); + assert(parentsh.sh != NULL); + spinsh = parentsh; + while (1) { + if (sorg(spinsh) != lpt) sesymself(spinsh); + // Get the new tet at this subface. + stpivot(spinsh, newtet); + tssbond1(newtet, rightseg); + // Go to the other face at this segment. + spivot(spinsh, neighsh); + if (sorg(neighsh) != lpt) sesymself(neighsh); + sesymself(neighsh); + stpivot(neighsh, neightet); + tssbond1(neightet, rightseg); + sstbond1(rightseg, neightet); + // Connecting two adjacent tets at this segment. + esymself(newtet); + esymself(neightet); + // Connect the two tets (at rightseg) together. + bond(newtet, neightet); + // Go to the next subface. + spivotself(spinsh); + if (spinsh.sh == parentsh.sh) break; + } + } - 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; - } - } while (apex(checktet) != pt); + // Connecting new tets at new subfaces together. + for (i = 0; i < caveshbdlist->objects; i++) { + parysh = (face *) fastlookup(caveshbdlist, i); + newsh = *parysh; + //assert(sinfected(newsh)); + // Each new subface contains two new tets. + for (k = 0; k < 2; k++) { + stpivot(newsh, newtet); + for (j = 0; j < 3; j++) { + // Check if this side is open. + esym(newtet, newface); + if (newface.tet[newface.ver & 3] == NULL) { + // An open face. Connect it to its adjacent tet. + sspivot(newsh, checkseg); + if (checkseg.sh != NULL) { + // A segment. It must not be the recovered segment. + tssbond1(newtet, checkseg); + sstbond1(checkseg, newtet); + } + spivot(newsh, neighsh); + if (neighsh.sh != NULL) { + // The adjacent subface exists. It's not a dangling segment. + if (sorg(neighsh) != sdest(newsh)) sesymself(neighsh); + stpivot(neighsh, neightet); + if (sinfected(neighsh)) { + esymself(neightet); + assert(neightet.tet[neightet.ver & 3] == NULL); } 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); + // Search for an open face at this edge. + spintet = neightet; + while (1) { + esym(spintet, searchtet); + fsym(searchtet, spintet); + if (spintet.tet == NULL) break; + assert(spintet.tet != neightet.tet); } - 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; - } - } while (apex(checktet) != pt); - } else { - // '-YY'. Remove p whatever s is a hull face or not. - remflag = true; + // Found an open face at 'searchtet'. + neightet = searchtet; } - if (remflag) { - unsupbdrycount++; - } - } 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; + } else { + // The edge (at 'newsh') is a dangling segment. + assert(checkseg.sh != NULL); + // Get an adjacent tet at this segment. + sstpivot1(checkseg, neightet); + assert(!isdeadtet(neightet)); + if (org(neightet) != sdest(newsh)) esymself(neightet); + assert((org(neightet) == sdest(newsh)) && + (dest(neightet) == sorg(newsh))); + // Search for an open face at this edge. + spintet = neightet; + while (1) { + esym(spintet, searchtet); + fsym(searchtet, spintet); + if (spintet.tet == NULL) break; + assert(spintet.tet != neightet.tet); } - if (remflag) { - unsupbdrycount++; + // Found an open face at 'searchtet'. + neightet = searchtet; + } + pc = apex(newface); + if (apex(neightet) == steinerpt) { + // Exterior case. The 'neightet' is a hull tet which contain + // 'steinerpt'. It will be deleted after 'steinerpt' is removed. + assert(pc == dummypoint); + caveoldtetlist->newindex((void **) &parytet); + *parytet = neightet; + // Connect newface to the adjacent hull tet of 'neightet', which + // has the same edge as 'newface', and does not has 'steinerpt'. + fnextself(neightet); + } else { + if (pc == dummypoint) { + if (apex(neightet) != dummypoint) { + setapex(newface, apex(neightet)); + // A hull tet has turned into an interior tet. + hullsize--; // Must update the hullsize. + } } } - } - pa = pointtraverse(); - } - } + bond(newface, neightet); + } // if (newface.tet[newface.ver & 3] == NULL) + enextself(newtet); + senextself(newsh); + } // j + sesymself(newsh); + } // k + } // i + + // Unmark all new subfaces. + for (i = 0; i < caveshbdlist->objects; i++) { + parysh = (face *) fastlookup(caveshbdlist, i); + suninfect(*parysh); + } + caveshbdlist->restart(); - if (unsupbdrycount == 0) { - break; // No unsupressed boundary points left. + if (caveoldtetlist->objects > 0l) { + // Delete hull tets which contain 'steinerpt'. + for (i = 0; i < caveoldtetlist->objects; i++) { + parytet = (triface *) fastlookup(caveoldtetlist, i); + tetrahedrondealloc(parytet->tet); } - iter++; - } while ((b->optlevel > 0) && (iter < b->optpasses)); - // Comment: default b->optpasses is 3, it can be set by -ss option. + // Must update the hullsize. + hullsize -= caveoldtetlist->objects; + caveoldtetlist->restart(); + } - 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); - // } + setpointtype(steinerpt, UNUSEDVERTEX); + unuverts++; + if (vt == FREESEGVERTEX) { + st_segref_count--; + } else { // vt == FREEFACETVERTEX + st_facref_count--; } + if (steinerleft > 0) steinerleft++; // We've removed a Steiner points. - /*// 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 ////////////////////////////////////////////////////////////// + point *parypt; + int steinercount = 0; -//// reconstruct_cxx ////////////////////////////////////////////////////////// -//// //// -//// //// + int bak_fliplinklevel = b->fliplinklevel; + b->fliplinklevel = 100000; // Unlimited flip level. -/////////////////////////////////////////////////////////////////////////////// -// // -// 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. // -// // -/////////////////////////////////////////////////////////////////////////////// + // Try to remove newly added Steiner points. + for (i = 0; i < n; i++) { + if (newsteiners[i] != NULL) { + if (!removevertexbyflips(newsteiners[i])) { + if (b->nobisect_param > 0) { // Not -Y0 + // Save it in subvertstack for removal. + subvertstack->newindex((void **) &parypt); + *parypt = newsteiners[i]; + } + steinercount++; + } + } + } -void tetgenmesh::transfernodes() -{ - point pointloop; - REAL x, y, z; - int coordindex; - int attribindex; - int mtrindex; - int i, j; + b->fliplinklevel = bak_fliplinklevel; - // 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; + if (steinercount > 0) { + if (b->verbose > 2) { + printf(" Added %d interior Steiner points.\n", steinercount); } } - // '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 [] newsteiners; + + return 1; } + /////////////////////////////////////////////////////////////////////////////// // // -// reconstructmesh() Reconstruct a tetrahedral mesh. // -// // -// 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 // -// recognized as subfaces, internal faces between two tetrahedra which have // -// different region attributes will also be recognized as subfaces. // +// suppresssteinerpoints() Suppress Steiner points. // // // -// Subsegments will be identified after subfaces are reconstructed. Edges at // -// the intersections of non-coplanar subfaces are recognized as subsegments. // -// Edges between two coplanar subfaces with different boundary markers are // -// also recognized as subsegments. // -// // -// The facet index of each subface will be set automatically after we have // -// recovered subfaces and subsegments. That is, the set of subfaces, which // -// are coplanar and have the same boundary marker will be recognized as a // -// facet and has a unique index, stored as the facet marker in each subface // -// of the set, the real boundary marker of each subface will be found in // -// 'in->facetmarkerlist' by the index. Facet index will be used in Delaunay // -// refinement for detecting two incident facets. // -// // -// Points which are not corners of tetrahedra will be inserted into the mesh.// -// Return the number of faces on the hull after the reconstruction. // +// All Steiner points have been saved in 'subvertstack' in the routines // +// carveholes() and suppresssteinerpoint(). // +// Each Steiner point is either removed or shifted into the interior. // // // /////////////////////////////////////////////////////////////////////////////// -long tetgenmesh::reconstructmesh() +int tetgenmesh::suppresssteinerpoints() { - tetrahedron **tetsperverlist; - shellface **facesperverlist; - triface tetloop, neightet, neineightet, spintet; - face subloop, neighsh, neineighsh; - face sface1, sface2; - face checkseg, subseg; - point *idx2verlist; - point torg, tdest, tapex, toppo; - point norg, napex; - list *neighshlist, *markerlist; - REAL sign, attrib, volume; - REAL da1, da2; - bool bondflag, insertsegflag; - int *idx2tetlist; - int *idx2facelist; - int *worklist; - int facetidx, marker; - int iorg, idest, iapex, ioppo; - int pivot, ipivot, isum; - int maxbandwidth; - int index, i, j, k; if (!b->quiet) { - printf("Reconstructing mesh.\n"); + printf("Suppressing Steiner points ...\n"); } - // Create a map from index to points. - makeindex2pointmap(idx2verlist); + point rempt, *parypt; - // Create the tetrahedra. - for (i = 0; i < in->numberoftetrahedra; i++) { - // Create a new tetrahedron and set its four corners, make sure that - // four corners form a positive orientation. - maketetrahedron(&tetloop); - index = i * in->numberofcorners; - // Although there may be 10 nodes, we only read the first 4. - iorg = in->tetrahedronlist[index] - in->firstnumber; - idest = in->tetrahedronlist[index + 1] - in->firstnumber; - iapex = in->tetrahedronlist[index + 2] - in->firstnumber; - ioppo = in->tetrahedronlist[index + 3] - in->firstnumber; - torg = idx2verlist[iorg]; - tdest = idx2verlist[idest]; - tapex = idx2verlist[iapex]; - toppo = idx2verlist[ioppo]; - sign = orient3d(torg, tdest, tapex, toppo); - if (sign > 0.0) { - norg = torg; torg = tdest; tdest = norg; - } else if (sign == 0.0) { - if (!b->quiet) { - printf("Warning: Tet %d is degenerate.\n", i + in->firstnumber); - } - } - setorg(tetloop, torg); - setdest(tetloop, tdest); - setapex(tetloop, tapex); - setoppo(tetloop, toppo); - // Temporarily set the vertices be type FREEVOLVERTEX, to indicate that - // they belong to the mesh. These types may be changed later. - setpointtype(torg, FREEVOLVERTEX); - setpointtype(tdest, FREEVOLVERTEX); - setpointtype(tapex, FREEVOLVERTEX); - setpointtype(toppo, FREEVOLVERTEX); - // Set element attributes if they exist. - for (j = 0; j < in->numberoftetrahedronattributes; j++) { - index = i * in->numberoftetrahedronattributes; - attrib = in->tetrahedronattributelist[index + j]; - setelemattribute(tetloop.tet, j, attrib); - } - // If -a switch is used (with no number follows) Set a volume - // constraint if it exists. - if (b->varvolume) { - if (in->tetrahedronvolumelist != (REAL *) NULL) { - volume = in->tetrahedronvolumelist[i]; - } else { - volume = -1.0; + int bak_fliplinklevel = b->fliplinklevel; + b->fliplinklevel = 100000; // Unlimited flip level. + int suppcount = 0, remcount = 0; + int i; + + // Try to suppress boundary Steiner points. + for (i = 0; i < subvertstack->objects; i++) { + parypt = (point *) fastlookup(subvertstack, i); + rempt = *parypt; + if (pointtype(rempt) != UNUSEDVERTEX) { + if ((pointtype(rempt) == FREESEGVERTEX) || + (pointtype(rempt) == FREEFACETVERTEX)) { + if (suppressbdrysteinerpoint(rempt)) { + suppcount++; + } } - setvolumebound(tetloop.tet, volume); + } + } // i + + if (suppcount > 0) { + if (b->verbose) { + printf(" Suppressed %d boundary Steiner points.\n", suppcount); } } - // Set the connection between tetrahedra. - hullsize = 0l; - // Create a map from nodes to tetrahedra. - maketetrahedronmap(idx2tetlist, tetsperverlist); - // 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(); - tetloop.tet = tetrahedrontraverse(); - while (tetloop.tet != (tetrahedron *) NULL) { - // Loop the four sides of the tetrahedron. - for (tetloop.loc = 0; tetloop.loc < 4; tetloop.loc++) { - sym(tetloop, neightet); - if (neightet.tet != dummytet) continue; // This side has finished. - torg = org(tetloop); - tdest = dest(tetloop); - tapex = apex(tetloop); - iorg = pointmark(torg) - in->firstnumber; - idest = pointmark(tdest) - in->firstnumber; - iapex = pointmark(tapex) - in->firstnumber; - 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; - 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; + if (b->nobisect_param > 0) { // -Y1 + for (i = 0; i < subvertstack->objects; i++) { + parypt = (point *) fastlookup(subvertstack, i); + rempt = *parypt; + if (pointtype(rempt) != UNUSEDVERTEX) { + if (pointtype(rempt) == FREEVOLVERTEX) { + if (removevertexbyflips(rempt)) { + remcount++; } } } - if (!bondflag) { - hullsize++; // It's a hull face. - // Bond this side to outer space. - dummytet[0] = encode(tetloop); - if ((in->pointmarkerlist != (int *) NULL) && !b->coarse) { - // Set its three corners's markers be boundary (hull) vertices. - if (in->pointmarkerlist[iorg] == 0) { - in->pointmarkerlist[iorg] = 1; - } - if (in->pointmarkerlist[idest] == 0) { - in->pointmarkerlist[idest] = 1; - } - if (in->pointmarkerlist[iapex] == 0) { - in->pointmarkerlist[iapex] = 1; + } + } + + if (remcount > 0) { + if (b->verbose) { + printf(" Removed %d interior Steiner points.\n", remcount); + } + } + + b->fliplinklevel = bak_fliplinklevel; + + if (b->nobisect_param > 1) { // -Y2 + // Smooth interior Steiner points. + optparameters opm; + triface *parytet; + point *ppt; + REAL ori; + int smtcount, count, ivcount; + int nt, j; + + // Point smooth options. + opm.max_min_volume = 1; + opm.numofsearchdirs = 20; + opm.searchstep = 0.001; + opm.maxiter = 30; // Limit the maximum iterations. + + smtcount = 0; + + do { + + nt = 0; + + while (1) { + count = 0; + ivcount = 0; // Clear the inverted count. + + for (i = 0; i < subvertstack->objects; i++) { + parypt = (point *) fastlookup(subvertstack, i); + rempt = *parypt; + if (pointtype(rempt) == FREEVOLVERTEX) { + getvertexstar(1, rempt, cavetetlist, NULL, NULL); + // Calculate the initial smallest volume (maybe zero or negative). + for (j = 0; j < cavetetlist->objects; j++) { + parytet = (triface *) fastlookup(cavetetlist, j); + ppt = (point *) &(parytet->tet[4]); + ori = orient3dfast(ppt[1], ppt[0], ppt[2], ppt[3]); + if (j == 0) { + opm.initval = ori; + } else { + if (opm.initval > ori) opm.initval = ori; + } + } + if (smoothpoint(rempt, cavetetlist, 1, &opm)) { + count++; + } + if (opm.imprval <= 0.0) { + ivcount++; // The mesh contains inverted elements. + } + cavetetlist->restart(); } + } // i + + smtcount += count; + + if (count == 0) { + // No point has been smoothed. + break; + } + + nt++; + if (nt > 2) { + break; // Already three iterations. } + } // while + + if (ivcount > 0) { + // There are inverted elements! + if (opm.maxiter > 0) { + // Set unlimited smoothing steps. Try again. + opm.numofsearchdirs = 30; + opm.searchstep = 0.0001; + opm.maxiter = -1; + continue; + } + } + + break; + } while (1); // Additional loop for (ivcount > 0) + + if (ivcount > 0) { + printf("BUG Report! The mesh contain inverted elements.\n"); + } + + if (b->verbose) { + if (smtcount > 0) { + printf(" Smoothed %d Steiner points.\n", smtcount); } - worklist[iorg] = 0; - worklist[idest] = 0; - worklist[iapex] = 0; } - tetloop.tet = tetrahedrontraverse(); + } // -Y2 + + subvertstack->restart(); + + return 1; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// recoverboundary() Recover segments and facets. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::recoverboundary(clock_t& tv) +{ + arraypool *misseglist, *misshlist; + arraypool *bdrysteinerptlist; + face searchsh, *parysh; + face searchseg, *paryseg; + point rempt, *parypt; + long ms; // The number of missing segments/subfaces. + int nit; // The number of iterations. + int s, i; + + // Counters. + long bak_segref_count, bak_facref_count, bak_volref_count; + + if (!b->quiet) { + printf("Recovering boundaries...\n"); } + if (b->verbose) { - printf(" Maximal vertex degree = %d.\n", maxbandwidth); + printf(" Recovering segments.\n"); } - // 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 - // interior faces which separate two different materials. + // Segments will be introduced. + checksubsegflag = 1; + + misseglist = new arraypool(sizeof(face), 8); + bdrysteinerptlist = new arraypool(sizeof(point), 8); - // Phase (1). Is there a list of user-provided subfaces? - if (in->trifacelist != (int *) NULL) { - // Recover subfaces from 'in->trifacelist'. - for (i = 0; i < in->numberoftrifaces; i++) { - index = i * 3; - iorg = in->trifacelist[index] - in->firstnumber; - idest = in->trifacelist[index + 1] - in->firstnumber; - iapex = in->trifacelist[index + 2] - in->firstnumber; - // Look for the location of this subface. - 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; - } + // In random order. + subsegs->traversalinit(); + for (i = 0; i < subsegs->items; i++) { + s = randomnation(i + 1); + // Move the s-th seg to the i-th. + subsegstack->newindex((void **) &paryseg); + *paryseg = * (face *) fastlookup(subsegstack, s); + // Put i-th seg to be the s-th. + searchseg.sh = shellfacetraverse(subsegs); + paryseg = (face *) fastlookup(subsegstack, s); + *paryseg = searchseg; + } + + // The init number of missing segments. + ms = subsegs->items; + nit = 0; + if (b->fliplinklevel < 0) { + autofliplinklevel = 1; // Init value. + } + + // First, trying to recover segments by only doing flips. + while (1) { + recoversegments(misseglist, 0, 0); + + if (misseglist->objects > 0) { + if (b->fliplinklevel >= 0) { + break; } else { - if ((idx2tetlist[iorg + 1] - idx2tetlist[iorg]) > - (idx2tetlist[iapex + 1] - idx2tetlist[iapex])) { - pivot = iapex; + if (misseglist->objects >= ms) { + nit++; + if (nit >= 3) { + //break; + // Do the last round with unbounded flip link level. + b->fliplinklevel = 100000; + } } else { - pivot = iorg; - } - } - bondflag = false; - // Search its neighbor in the adjacent tets of torg. - for (j = idx2tetlist[pivot]; j < idx2tetlist[pivot + 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) { - bondflag = true; // Find! - break; + ms = misseglist->objects; + if (nit > 0) { + nit--; } } - } - if (bondflag) { - // Create a new subface and insert it into the mesh. - makeshellface(subfaces, &subloop); - torg = idx2verlist[iorg]; - tdest = idx2verlist[idest]; - tapex = idx2verlist[iapex]; - setsorg(subloop, torg); - setsdest(subloop, tdest); - setsapex(subloop, tapex); - // Set the vertices be FREESUBVERTEX to indicate they belong to a - // facet of the domain. They may be changed later. - setpointtype(torg, FREESUBVERTEX); - setpointtype(tdest, FREESUBVERTEX); - setpointtype(tapex, FREESUBVERTEX); - if (in->trifacemarkerlist != (int *) NULL) { - setshellmark(subloop, in->trifacemarkerlist[i]); - } - adjustedgering(neightet, CCW); - findedge(&subloop, org(neightet), dest(neightet)); - tsbond(neightet, subloop); - sym(neightet, neineightet); - if (neineightet.tet != dummytet) { - sesymself(subloop); - tsbond(neineightet, subloop); - } - } else { - if (!b->quiet) { - printf("Warning: Subface %d is discarded.\n", i + in->firstnumber); + for (i = 0; i < misseglist->objects; i++) { + subsegstack->newindex((void **) &paryseg); + *paryseg = * (face *) fastlookup(misseglist, i); } + misseglist->restart(); + autofliplinklevel+=b->fliplinklevelinc; } - worklist[iorg] = 0; - worklist[idest] = 0; - worklist[iapex] = 0; + } else { + // All segments are recovered. + break; } - } + } // while (1) - // Phase (2). Indentify subfaces from the mesh. - tetrahedrons->traversalinit(); - tetloop.tet = tetrahedrontraverse(); - while (tetloop.tet != (tetrahedron *) NULL) { - // Loop the four sides of the tetrahedron. - for (tetloop.loc = 0; tetloop.loc < 4; tetloop.loc++) { - tspivot(tetloop, subloop); - if (subloop.sh != dummysh) continue; - bondflag = false; - sym(tetloop, neightet); - if (neightet.tet == dummytet) { - // It's a hull face. Insert a subface at here. - bondflag = true; + if (b->verbose) { + printf(" %ld (%ld) segments are recovered (missing).\n", + subsegs->items - misseglist->objects, misseglist->objects); + } + + if (misseglist->objects > 0) { + // Second, trying to recover segments by doing more flips (fullsearch). + while (misseglist->objects > 0) { + ms = misseglist->objects; + for (i = 0; i < misseglist->objects; i++) { + subsegstack->newindex((void **) &paryseg); + *paryseg = * (face *) fastlookup(misseglist, i); + } + misseglist->restart(); + + recoversegments(misseglist, 1, 0); + + if (misseglist->objects < ms) { + // The number of missing segments is reduced. + continue; } else { - // It's an interior face. Insert a subface if two tetrahedra have - // different attributes (i.e., they belong to two regions). - if (in->numberoftetrahedronattributes > 0) { - if (elemattribute(neightet.tet, - in->numberoftetrahedronattributes - 1) != - elemattribute(tetloop.tet, - in->numberoftetrahedronattributes - 1)) { - bondflag = true; - } - } - } - if (bondflag) { - adjustedgering(tetloop, CCW); - makeshellface(subfaces, &subloop); - torg = org(tetloop); - tdest = dest(tetloop); - tapex = apex(tetloop); - setsorg(subloop, torg); - setsdest(subloop, tdest); - setsapex(subloop, tapex); - // Set the vertices be FREESUBVERTEX to indicate they belong to a - // facet of the domain. They may be changed later. - setpointtype(torg, FREESUBVERTEX); - setpointtype(tdest, FREESUBVERTEX); - setpointtype(tapex, FREESUBVERTEX); - tsbond(tetloop, subloop); - if (neightet.tet != dummytet) { - sesymself(subloop); - tsbond(neightet, subloop); - } + break; } } - tetloop.tet = tetrahedrontraverse(); + if (b->verbose) { + printf(" %ld (%ld) segments are recovered (missing).\n", + subsegs->items - misseglist->objects, misseglist->objects); + } } - // Set the connection between subfaces. A subsegment may have more than - // two subfaces sharing it, 'neighshlist' stores all subfaces sharing - // one edge. - neighshlist = new list(sizeof(face), NULL); - // Create a map from nodes to subfaces. - makesubfacemap(idx2facelist, facesperverlist); + if (misseglist->objects > 0) { + // Third, trying to recover segments by doing more flips (fullsearch) + // and adding Steiner points in the volume. + while (misseglist->objects > 0) { + ms = misseglist->objects; + for (i = 0; i < misseglist->objects; i++) { + subsegstack->newindex((void **) &paryseg); + *paryseg = * (face *) fastlookup(misseglist, i); + } + misseglist->restart(); - // Loop over the set of subfaces, setup the connection between subfaces. - subfaces->traversalinit(); - subloop.sh = shellfacetraverse(subfaces); - while (subloop.sh != (shellface *) NULL) { - for (i = 0; i < 3; i++) { - spivot(subloop, neighsh); - if (neighsh.sh == dummysh) { - // This side is 'empty', operate on it. - torg = sorg(subloop); - tdest = sdest(subloop); - tapex = sapex(subloop); - neighshlist->append(&subloop); - iorg = pointmark(torg) - in->firstnumber; - // Search its neighbor in the adjacent list of torg. - for (j = idx2facelist[iorg]; j < idx2facelist[iorg + 1]; j++) { - neighsh.sh = facesperverlist[j]; - if (neighsh.sh == subloop.sh) continue; - neighsh.shver = 0; - if (isfacehasedge(&neighsh, torg, tdest)) { - findedge(&neighsh, torg, tdest); - // Insert 'neighsh' into 'neighshlist'. - if (neighshlist->len() < 2) { - neighshlist->append(&neighsh); - } else { - for (index = 0; index < neighshlist->len() - 1; index++) { - sface1 = * (face *)(* neighshlist)[index]; - sface2 = * (face *)(* neighshlist)[index + 1]; - da1 = facedihedral(torg, tdest, sapex(sface1), sapex(neighsh)); - da2 = facedihedral(torg, tdest, sapex(sface1), sapex(sface2)); - if (da1 < da2) { - break; // Insert it after index. - } - } - neighshlist->insert(index + 1, &neighsh); - } - } - } - // Bond the subfaces in 'neighshlist'. - if (neighshlist->len() > 1) { - neighsh = * (face *)(* neighshlist)[0]; - for (j = 1; j <= neighshlist->len(); j++) { - if (j < neighshlist->len()) { - neineighsh = * (face *)(* neighshlist)[j]; - } else { - neineighsh = * (face *)(* neighshlist)[0]; - } - sbond1(neighsh, neineighsh); - neighsh = neineighsh; - } - } else { - // No neighbor subface be found, bond 'subloop' to itself. - sdissolve(subloop); // sbond(subloop, subloop); - } - neighshlist->clear(); + recoversegments(misseglist, 1, 1); + + if (misseglist->objects < ms) { + // The number of missing segments is reduced. + continue; + } else { + break; } - senextself(subloop); } - subloop.sh = shellfacetraverse(subfaces); + if (b->verbose) { + printf(" Added %ld Steiner points in volume.\n", st_volref_count); + } } - - // Segments will be introudced. Each segment has a unique marker (1-based). - marker = 1; - subfaces->traversalinit(); - subloop.sh = shellfacetraverse(subfaces); - while (subloop.sh != (shellface *) NULL) { - for (i = 0; i < 3; i++) { - sspivot(subloop, subseg); - if (subseg.sh == dummysh) { - // This side has no subsegment bonded, check it. - torg = sorg(subloop); - tdest = sdest(subloop); - tapex = sapex(subloop); - spivot(subloop, neighsh); - spivot(neighsh, neineighsh); - insertsegflag = false; - if (subloop.sh == neighsh.sh || subloop.sh != neineighsh.sh) { - // This side is either self-bonded or more than two subfaces, - // insert a subsegment at this side. - insertsegflag = true; - } else { - // Only two subfaces case. -#ifdef SELF_CHECK - assert(subloop.sh != neighsh.sh); -#endif - napex = sapex(neighsh); - sign = orient3d(torg, tdest, tapex, napex); - if (iscoplanar(torg, tdest, tapex, napex, sign, b->epsilon)) { - // Although they are coplanar, we still need to check if they - // have the same boundary marker. - insertsegflag = (shellmark(subloop) != shellmark(neighsh)); - } else { - // Non-coplanar. - insertsegflag = true; - } - } - if (insertsegflag) { - // Create a subsegment at this side. - makeshellface(subsegs, &subseg); - setsorg(subseg, torg); - setsdest(subseg, tdest); - // The two vertices have been marked as FREESUBVERTEX. Now mark - // them as NACUTEVERTEX. - setpointtype(torg, NACUTEVERTEX); - setpointtype(tdest, NACUTEVERTEX); - setshellmark(subseg, marker); - marker++; - // Bond all subfaces to this subsegment. - neighsh = subloop; - do { - ssbond(neighsh, subseg); - spivotself(neighsh); - if (neighsh.sh == dummysh) { - break; // Only one facet case. - } - } while (neighsh.sh != subloop.sh); - } + if (misseglist->objects > 0) { + // Last, trying to recover segments by doing more flips (fullsearch), + // and adding Steiner points in the volume, and splitting segments. + long bak_inpoly_count = st_volref_count; //st_inpoly_count; + for (i = 0; i < misseglist->objects; i++) { + subsegstack->newindex((void **) &paryseg); + *paryseg = * (face *) fastlookup(misseglist, i); + } + misseglist->restart(); + + recoversegments(misseglist, 1, 2); + + if (b->verbose) { + printf(" Added %ld Steiner points in segments.\n", st_segref_count); + if (st_volref_count > bak_inpoly_count) { + printf(" Added another %ld Steiner points in volume.\n", + st_volref_count - bak_inpoly_count); } - senextself(subloop); } - subloop.sh = shellfacetraverse(subfaces); + assert(misseglist->objects == 0l); } - // 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); - - subfaces->traversalinit(); - subloop.sh = shellfacetraverse(subfaces); - while (subloop.sh != (shellface *) NULL) { - // Only operate on uninfected subface, after operating, infect it. - if (!sinfected(subloop)) { - // A new facet is found. - marker = shellmark(subloop); - markerlist->append(&marker); - facetidx = markerlist->len(); // 'facetidx' starts from 1. - setshellmark(subloop, facetidx); - sinfect(subloop); - neighshlist->append(&subloop); - // Find out all subfaces of this facet (bounded by subsegments). - for (i = 0; i < neighshlist->len(); i++) { - neighsh = * (face *) (* neighshlist)[i]; - for (j = 0; j < 3; j++) { - sspivot(neighsh, subseg); - if (subseg.sh == dummysh) { - spivot(neighsh, neineighsh); - if (!sinfected(neineighsh)) { - // 'neineighsh' is in the same facet as 'subloop'. -#ifdef SELF_CHECK - assert(shellmark(neineighsh) == marker); -#endif - setshellmark(neineighsh, facetidx); - sinfect(neineighsh); - neighshlist->append(&neineighsh); - } - } - senextself(neighsh); - } + if (st_segref_count > 0) { + // Try to remove the Steiner points added in segments. + bak_segref_count = st_segref_count; + bak_volref_count = st_volref_count; + for (i = 0; i < subvertstack->objects; i++) { + // Get the Steiner point. + parypt = (point *) fastlookup(subvertstack, i); + rempt = *parypt; + if (!removevertexbyflips(rempt)) { + // Save it in list. + bdrysteinerptlist->newindex((void **) &parypt); + *parypt = rempt; } - neighshlist->clear(); } - subloop.sh = shellfacetraverse(subfaces); - } - // Uninfect all subfaces. - subfaces->traversalinit(); - subloop.sh = shellfacetraverse(subfaces); - while (subloop.sh != (shellface *) NULL) { -#ifdef SELF_CHECK - assert(sinfected(subloop)); -#endif - suninfect(subloop); - subloop.sh = shellfacetraverse(subfaces); - } - // Save the facet markers in 'in->facetmarkerlist'. - in->numberoffacets = markerlist->len(); - in->facetmarkerlist = new int[in->numberoffacets]; - for (i = 0; i < in->numberoffacets; i++) { - marker = * (int *) (* markerlist)[i]; - in->facetmarkerlist[i] = marker; - } - // Initialize the 'facetabovepointlist'. - facetabovepointarray = new point[in->numberoffacets + 1]; - for (i = 0; i < in->numberoffacets + 1; i++) { - facetabovepointarray[i] = (point) NULL; - } - - // The mesh contains boundary now. - checksubfaces = 1; - // The mesh is nonconvex now. - nonconvex = 1; - - /*// Is there periodic boundary confitions? - if (checkpbcs) { - tetgenio::pbcgroup *pg; - pbcdata *pd; - // Initialize the global array 'subpbcgrouptable'. - createsubpbcgrouptable(); - // Loop for each pbcgroup i. - for (i = 0; i < in->numberofpbcgroups; i++) { - pg = &(in->pbcgrouplist[i]); - pd = &(subpbcgrouptable[i]); - // Find all subfaces of pd, set each subface's group id be i. - for (j = 0; j < 2; j++) { - subfaces->traversalinit(); - subloop.sh = shellfacetraverse(subfaces); - while (subloop.sh != (shellface *) NULL) { - facetidx = shellmark(subloop); - marker = in->facetmarkerlist[facetidx - 1]; - if (marker == pd->fmark[j]) { - setshellpbcgroup(subloop, i); - pd->ss[j] = subloop; - } - subloop.sh = shellfacetraverse(subfaces); + if (b->verbose) { + if (st_segref_count < bak_segref_count) { + if (bak_volref_count < st_volref_count) { + printf(" Suppressed %ld Steiner points in segments.\n", + st_volref_count - bak_volref_count); } - } - if (pg->pointpairlist != (int *) NULL) { - // Set the connections between pbc point pairs. - for (j = 0; j < pg->numberofpointpairs; j++) { - iorg = pg->pointpairlist[j * 2] - in->firstnumber; - idest = pg->pointpairlist[j * 2 + 1] - in->firstnumber; - torg = idx2verlist[iorg]; - tdest = idx2verlist[idest]; - setpoint2pbcpt(torg, tdest); - setpoint2pbcpt(tdest, torg); + if ((st_segref_count + (st_volref_count - bak_volref_count)) < + bak_segref_count) { + printf(" Removed %ld Steiner points in segments.\n", + bak_segref_count - + (st_segref_count + (st_volref_count - bak_volref_count))); } } } - // Create the global array 'segpbcgrouptable'. - createsegpbcgrouptable(); - }*/ + subvertstack->restart(); + } - delete markerlist; - delete neighshlist; - delete [] worklist; - delete [] idx2tetlist; - delete [] tetsperverlist; - delete [] idx2facelist; - delete [] facesperverlist; - delete [] idx2verlist; - - return hullsize; -} -/////////////////////////////////////////////////////////////////////////////// -// // -// insertconstrainedpoints() Insert a list of constrained points. // -// // -/////////////////////////////////////////////////////////////////////////////// + tv = clock(); -void tetgenmesh::insertconstrainedpoints(tetgenio *addio) -{ - queue *flipqueue; - triface searchtet; - face checksh, checkseg; - point newpoint; - enum locateresult loc; - REAL *attr; - bool insertflag; - int covertices, outvertices; - int index; - int i, j; - - if (!b->quiet) { - printf("Insert additional points into mesh.\n"); + if (b->verbose) { + printf(" Recovering facets.\n"); } - // Initialize 'flipqueue'. - flipqueue = new queue(sizeof(badface)); - recenttet.tet = dummytet; - covertices = outvertices = 0; + // Subfaces will be introduced. + checksubfaceflag = 1; - index = 0; - for (i = 0; i < addio->numberofpoints; i++) { - // Create a newpoint. - makepoint(&newpoint); - newpoint[0] = addio->pointlist[index++]; - newpoint[1] = addio->pointlist[index++]; - newpoint[2] = addio->pointlist[index++]; - // Read the add point attributes if current points have attributes. - if ((addio->numberofpointattributes > 0) && - (in->numberofpointattributes > 0)) { - attr = addio->pointattributelist + addio->numberofpointattributes * i; - for (j = 0; j < in->numberofpointattributes; j++) { - if (j < addio->numberofpointattributes) { - newpoint[3 + j] = attr[j]; - } - } - } - // Find the location of the inserted point. - searchtet = recenttet; - loc = locate(newpoint, &searchtet); - if (loc != ONVERTEX) { - loc = adjustlocate(newpoint, &searchtet, loc, b->epsilon2); - } - if (loc == OUTSIDE) { - loc = hullwalk(newpoint, &searchtet); - if (loc == OUTSIDE) { - // Perform a brute-force search. - tetrahedrons->traversalinit(); - searchtet.tet = tetrahedrontraverse(); - while (searchtet.tet != (tetrahedron *) NULL) { - loc = adjustlocate(newpoint, &searchtet, OUTSIDE, b->epsilon2); - if (loc != OUTSIDE) break; - searchtet.tet = tetrahedrontraverse(); - } - } - } - // Insert the point if it not lies outside or on a vertex. - insertflag = true; - switch (loc) { - case INTETRAHEDRON: - setpointtype(newpoint, FREEVOLVERTEX); - splittetrahedron(newpoint, &searchtet, flipqueue); - break; - case ONFACE: - tspivot(searchtet, checksh); - if (checksh.sh != dummysh) { - // It is a boundary face. Don't insert it if -Y option is used. - if (b->nobisect) { - insertflag = false; - } else { - setpointtype(newpoint, FREESUBVERTEX); - setpoint2sh(newpoint, sencode(checksh)); - } - } else { - setpointtype(newpoint, FREEVOLVERTEX); - } - if (insertflag) { - splittetface(newpoint, &searchtet, flipqueue); - } - break; - case ENCSEGMENT: - case ONEDGE: - tsspivot(&searchtet, &checkseg); - if (checkseg.sh != dummysh) { - if (b->nobisect) { - insertflag = false; - } else { - setpointtype(newpoint, FREESEGVERTEX); - setpoint2seg(newpoint, sencode(checkseg)); - } + misshlist = new arraypool(sizeof(face), 8); + + // 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 **) &parysh); + *parysh = * (face *) fastlookup(subfacstack, s); + // Put i-th subface to be the s-th. + searchsh.sh = shellfacetraverse(subfaces); + parysh = (face *) fastlookup(subfacstack, s); + *parysh = searchsh; + } + + ms = subfaces->items; + nit = 0; + b->fliplinklevel = -1; // Init. + if (b->fliplinklevel < 0) { + autofliplinklevel = 1; // Init value. + } + + while (1) { + recoversubfaces(misshlist, 0); + + if (misshlist->objects > 0) { + if (b->fliplinklevel >= 0) { + break; } else { - tspivot(searchtet, checksh); - if (checksh.sh != dummysh) { - if (b->nobisect) { - insertflag = false; - } else { - setpointtype(newpoint, FREESUBVERTEX); - setpoint2sh(newpoint, sencode(checksh)); + if (misshlist->objects >= ms) { + nit++; + if (nit >= 3) { + //break; + // Do the last round with unbounded flip link level. + b->fliplinklevel = 100000; } } else { - setpointtype(newpoint, FREEVOLVERTEX); + ms = misshlist->objects; + if (nit > 0) { + nit--; + } } + for (i = 0; i < misshlist->objects; i++) { + subfacstack->newindex((void **) &parysh); + *parysh = * (face *) fastlookup(misshlist, i); + } + misshlist->restart(); + autofliplinklevel+=b->fliplinklevelinc; } - if (insertflag) { - splittetedge(newpoint, &searchtet, flipqueue); - } - break; - case ONVERTEX: - insertflag = false; - covertices++; - break; - case OUTSIDE: - insertflag = false; - outvertices++; - break; - } - // Remember the tetrahedron for next point searching. - recenttet = searchtet; - if (!insertflag) { - pointdealloc(newpoint); } else { - lawson3d(flipqueue); + // All subfaces are recovered. + break; } - } + } // while (1) if (b->verbose) { - if (covertices > 0) { - printf(" %d constrained points already exist.\n", covertices); - } - if (outvertices > 0) { - printf(" %d constrained points lie outside the mesh.\n", outvertices); - } - printf(" %d constrained points have been inserted.\n", - addio->numberofpoints - covertices - outvertices); + printf(" %ld (%ld) subfaces are recovered (missing).\n", + subfaces->items - misshlist->objects, misshlist->objects); } - delete flipqueue; -} + if (misshlist->objects > 0) { + // There are missing subfaces. Add Steiner points. + for (i = 0; i < misshlist->objects; i++) { + subfacstack->newindex((void **) &parysh); + *parysh = * (face *) fastlookup(misshlist, i); + } + misshlist->restart(); -/////////////////////////////////////////////////////////////////////////////// -// // -// p1interpolatebgm() Set pt size by p^1 interpolation in background mesh.// -// // -// On input, 'bgmtet' is a suggesting tet in background mesh for searching // -// 'pt'. It returns the tet containing 'pt'. // -// // -/////////////////////////////////////////////////////////////////////////////// + recoversubfaces(NULL, 1); -bool tetgenmesh::p1interpolatebgm(point pt, triface* bgmtet, long *scount) -{ - point bgmpt[4]; - enum locateresult loc; - REAL vol, volpt[4], weights[4]; - int i; + if (b->verbose) { + printf(" Added %ld Steiner points in facets.\n", st_facref_count); + } + } - loc = bgm->preciselocate(pt, bgmtet, bgm->tetrahedrons->items); - if (loc == OUTSIDE) { - loc = bgm->hullwalk(pt, bgmtet); - if (loc == OUTSIDE) { - // Perform a brute-force search. - if (!b->quiet && b->verbose) { - printf("Warning: Global point location.\n"); + + if (st_facref_count > 0) { + // Try to remove the Steiner points added in facets. + bak_facref_count = st_facref_count; + for (i = 0; i < subvertstack->objects; i++) { + // Get the Steiner point. + parypt = (point *) fastlookup(subvertstack, i); + rempt = *parypt; + if (!removevertexbyflips(*parypt)) { + // Save it in list. + bdrysteinerptlist->newindex((void **) &parypt); + *parypt = rempt; } - if (scount) (*scount)++; - bgm->tetrahedrons->traversalinit(); // in bgm - bgmtet->tet = bgm->tetrahedrontraverse(); // in bgm - while (bgmtet->tet != (tetrahedron *) NULL) { - loc = bgm->adjustlocate(pt, bgmtet, OUTSIDE, b->epsilon); - if (loc != OUTSIDE) break; - bgmtet->tet = bgm->tetrahedrontraverse(); // in bgm + } + if (b->verbose) { + if (st_facref_count < bak_facref_count) { + printf(" Removed %ld Steiner points in facets.\n", + bak_facref_count - st_facref_count); } } + subvertstack->restart(); } - if (loc != OUTSIDE) { - // Let p remember t. - setpoint2bgmtet(pt, encode(*bgmtet)); // in m - // get the corners of t. - for (i = 0; i < 4; i++) bgmpt[i] = (point) bgmtet->tet[4 + i]; - // Calculate the weighted coordinates of p in t. - vol = orient3d(bgmpt[0], bgmpt[1], bgmpt[2], bgmpt[3]); - volpt[0] = orient3d(pt, bgmpt[1], bgmpt[2], bgmpt[3]); - volpt[1] = orient3d(bgmpt[0], pt, bgmpt[2], bgmpt[3]); - volpt[2] = orient3d(bgmpt[0], bgmpt[1], pt, bgmpt[3]); - volpt[3] = orient3d(bgmpt[0], bgmpt[1], bgmpt[2], pt); - for (i = 0; i < 4; i++) weights[i] = fabs(volpt[i] / vol); - // Interpolate the solution for p. - for (i = 0; i < bgm->in->numberofpointmtrs; i++) { - pt[pointmtrindex + i] = weights[0] * bgmpt[0][bgm->pointmtrindex + i] - + weights[1] * bgmpt[1][bgm->pointmtrindex + i] - + weights[2] * bgmpt[2][bgm->pointmtrindex + i] - + weights[3] * bgmpt[3][bgm->pointmtrindex + i]; + + + if (bdrysteinerptlist->objects > 0) { + if (b->verbose) { + printf(" %ld Steiner points remained in boundary.\n", + bdrysteinerptlist->objects); } - } else { - setpoint2bgmtet(pt, (tetrahedron) NULL); // in m - } - return loc != OUTSIDE; + } // if + + + // Accumulate the dynamic memory. + totalworkmemory += (misseglist->totalmemory + misshlist->totalmemory + + bdrysteinerptlist->totalmemory); + + delete bdrysteinerptlist; + delete misseglist; + delete misshlist; } +//// //// +//// //// +//// steiner_cxx ////////////////////////////////////////////////////////////// + + +//// reconstruct_cxx ////////////////////////////////////////////////////////// +//// //// +//// //// + /////////////////////////////////////////////////////////////////////////////// // // -// interpolatesizemap() Interpolate the point sizes in the given size map.// -// // -// The size map is specified on each node of the background mesh. The points // -// of current mesh get their sizes by interpolating. // -// // -// This function operation on two meshes simultaneously, the current mesh m, // -// and the background mesh bgm. After this function, each point p in m will // -// have a pointer to a tet of bgm. // +// carveholes() Remove tetrahedra not in the mesh domain. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::interpolatesizemap() + +void tetgenmesh::carveholes() { - list *adjtetlist; - triface tetloop, neightet, bgmtet; - point searchpt; - long scount; - int *worklist; - int sepcount; - int i; + arraypool *tetarray, *hullarray; + triface tetloop, neightet, *parytet, *parytet1; + triface *regiontets = NULL; + face checksh, *parysh; + face checkseg; + point ptloop, *parypt; + int t1ver; + int i, j, k; - if (b->verbose) { - printf(" Interpolating size map.\n"); + if (!b->quiet) { + if (b->convex) { + printf("Marking exterior tetrahedra ...\n"); + } else { + printf("Removing exterior tetrahedra ...\n"); + } } - worklist = new int[points->items + 1]; - for (i = 0; i < points->items + 1; i++) worklist[i] = 0; - sepcount = 0; - scount = 0l; + // Initialize the pool of exterior tets. + tetarray = new arraypool(sizeof(triface), 10); + hullarray = new arraypool(sizeof(triface), 10); + // Collect unprotected tets and hull tets. tetrahedrons->traversalinit(); - tetloop.tet = tetrahedrontraverse(); + tetloop.ver = 11; // The face opposite to dummypoint. + tetloop.tet = alltetrahedrontraverse(); while (tetloop.tet != (tetrahedron *) NULL) { - if (!infected(tetloop)) { - // Find a new subdomain. - adjtetlist = new list(sizeof(triface), NULL, 1024); - infect(tetloop); - // Search the four corners in background mesh. - for (i = 0; i < 4; i++) { - searchpt = (point) tetloop.tet[4 + i]; - // Mark the point for avoiding multiple searchings. - // assert(worklist[pointmark(searchpt)] == 0); - worklist[pointmark(searchpt)] = 1; - // Does it contain a pointer to bgm tet? - bgm->decode(point2bgmtet(searchpt), bgmtet); - if (bgm->isdead(&bgmtet)) { - bgmtet = bgm->recenttet; - } - if (p1interpolatebgm(searchpt, &bgmtet, &scount)) { - bgm->recenttet = bgmtet; - } - } // for (i = 0; i < 4; i++) - // Collect all tets in this region. - adjtetlist->append(&tetloop); - // Collect the tets in the subdomain. - for (i = 0; i < adjtetlist->len(); i++) { - tetloop = * (triface *)(* adjtetlist)[i]; - for (tetloop.loc = 0; tetloop.loc < 4; tetloop.loc++) { - sym(tetloop, neightet); - if ((neightet.tet != dummytet) && !infected(neightet)) { - // Only need to search for the opposite point. - searchpt = oppo(neightet); - if (worklist[pointmark(searchpt)] == 0) { - worklist[pointmark(searchpt)] = 1; - decode(point2bgmtet(searchpt), bgmtet); - if (bgm->isdead(&bgmtet)) { - bgmtet = bgm->recenttet; + if (ishulltet(tetloop)) { + // Is this side protected by a subface? + if (!issubface(tetloop)) { + // Collect an unprotected hull tet and tet. + infect(tetloop); + hullarray->newindex((void **) &parytet); + *parytet = tetloop; + // tetloop's face number is 11 & 3 = 3. + decode(tetloop.tet[3], neightet); + if (!infected(neightet)) { + infect(neightet); + tetarray->newindex((void **) &parytet); + *parytet = neightet; + } + } + } + tetloop.tet = alltetrahedrontraverse(); + } + + if (in->numberofholes > 0) { + // Mark as infected any tets inside volume holes. + for (i = 0; i < 3 * in->numberofholes; i += 3) { + // Search a tet containing the i-th hole point. + neightet.tet = NULL; + randomsample(&(in->holelist[i]), &neightet); + if (locate(&(in->holelist[i]), &neightet) != OUTSIDE) { + // The tet 'neightet' contain this point. + if (!infected(neightet)) { + infect(neightet); + tetarray->newindex((void **) &parytet); + *parytet = neightet; + // Add its adjacent tet if it is not protected. + if (!issubface(neightet)) { + decode(neightet.tet[neightet.ver & 3], tetloop); + if (!infected(tetloop)) { + infect(tetloop); + if (ishulltet(tetloop)) { + hullarray->newindex((void **) &parytet); + } else { + tetarray->newindex((void **) &parytet); } - if (p1interpolatebgm(searchpt, &bgmtet, &scount)) { - bgm->recenttet = bgmtet; + *parytet = tetloop; + } + } + else { + // It is protected. Check if its adjacent tet is a hull tet. + decode(neightet.tet[neightet.ver & 3], tetloop); + if (ishulltet(tetloop)) { + // It is hull tet, add it into the list. Moreover, the subface + // is dead, i.e., both sides are in exterior. + if (!infected(tetloop)) { + infect(tetloop); + hullarray->newindex((void **) &parytet); + *parytet = tetloop; } } - infect(neightet); - adjtetlist->append(&neightet); + if (infected(tetloop)) { + // Both sides of this subface are in exterior. + tspivot(neightet, checksh); + sinfect(checksh); // Only queue it once. + subfacstack->newindex((void **) &parysh); + *parysh = checksh; + } } + } // if (!infected(neightet)) + } else { + // A hole point locates outside of the convex hull. + if (!b->quiet) { + printf("Warning: The %d-th hole point ", i/3 + 1); + printf("lies outside the convex hull.\n"); } } - // Increase the number of separated domains. - sepcount++; - delete adjtetlist; - } // if (!infect()) - tetloop.tet = tetrahedrontraverse(); + } // i + } // if (in->numberofholes > 0) + + if (b->regionattrib && (in->numberofregions > 0)) { // -A option. + // Record the tetrahedra that contains the region points for assigning + // region attributes after the holes have been carved. + regiontets = new triface[in->numberofregions]; + // Mark as marktested any tetrahedra inside volume regions. + for (i = 0; i < 5 * in->numberofregions; i += 5) { + // Search a tet containing the i-th region point. + neightet.tet = NULL; + randomsample(&(in->regionlist[i]), &neightet); + if (locate(&(in->regionlist[i]), &neightet) != OUTSIDE) { + regiontets[i/5] = neightet; + } else { + if (!b->quiet) { + printf("Warning: The %d-th region point ", i/5+1); + printf("lies outside the convex hull.\n"); + } + regiontets[i/5].tet = NULL; + } + } } - // Unmark all tets. - tetrahedrons->traversalinit(); - tetloop.tet = tetrahedrontraverse(); - while (tetloop.tet != (tetrahedron *) NULL) { - assert(infected(tetloop)); - uninfect(tetloop); - tetloop.tet = tetrahedrontraverse(); - } - delete [] worklist; + // Collect all exterior tets (in concave place and in holes). + for (i = 0; i < tetarray->objects; i++) { + parytet = (triface *) fastlookup(tetarray, i); + j = (parytet->ver & 3); // j is the current face number. + // Check the other three adjacent tets. + for (k = 1; k < 4; k++) { + decode(parytet->tet[(j + k) % 4], neightet); + // neightet may be a hull tet. + if (!infected(neightet)) { + // Is neightet protected by a subface. + if (!issubface(neightet)) { + // Not proected. Collect it. (It must not be a hull tet). + infect(neightet); + tetarray->newindex((void **) &parytet1); + *parytet1 = neightet; + } else { + // Protected. Check if it is a hull tet. + if (ishulltet(neightet)) { + // A hull tet. Collect it. + infect(neightet); + hullarray->newindex((void **) &parytet1); + *parytet1 = neightet; + // Both sides of this subface are exterior. + tspivot(neightet, checksh); + // Queue this subface (to be deleted later). + assert(!sinfected(checksh)); + sinfect(checksh); // Only queue it once. + subfacstack->newindex((void **) &parysh); + *parysh = checksh; + } + } + } else { + // Both sides of this face are in exterior. + // If there is a subface. It should be collected. + if (issubface(neightet)) { + tspivot(neightet, checksh); + if (!sinfected(checksh)) { + sinfect(checksh); + subfacstack->newindex((void **) &parysh); + *parysh = checksh; + } + } + } + } // j, k + } // i -#ifdef SELF_CHECK - if (b->verbose && scount > 0l) { - printf(" %ld brute-force searches.\n", scount); + if (b->regionattrib && (in->numberofregions > 0)) { + // Re-check saved region tets to see if they lie outside. + for (i = 0; i < in->numberofregions; i++) { + if (infected(regiontets[i])) { + if (b->verbose) { + printf("Warning: The %d-th region point ", i+1); + printf("lies in the exterior of the domain.\n"); + } + regiontets[i].tet = NULL; + } + } } - if (b->verbose && sepcount > 0) { - printf(" %d separate domains.\n", sepcount); + + // Collect vertices which point to infected tets. These vertices + // may get deleted after the removal of exterior tets. + // If -Y1 option is used, collect all Steiner points for removal. + // The lists 'cavetetvertlist' and 'subvertstack' are re-used. + points->traversalinit(); + ptloop = pointtraverse(); + while (ptloop != NULL) { + if ((pointtype(ptloop) != UNUSEDVERTEX) && + (pointtype(ptloop) != DUPLICATEDVERTEX)) { + decode(point2tet(ptloop), neightet); + if (infected(neightet)) { + cavetetvertlist->newindex((void **) &parypt); + *parypt = ptloop; + } + if (b->nobisect && (b->nobisect_param > 0)) { // -Y1 + // Queue it if it is a Steiner point. + if (pointmark(ptloop) > + (in->numberofpoints - (in->firstnumber ? 0 : 1))) { + subvertstack->newindex((void **) &parypt); + *parypt = ptloop; + } + } + } + ptloop = pointtraverse(); } -#endif -} -/////////////////////////////////////////////////////////////////////////////// -// // -// duplicatebgmesh() Duplicate current mesh to background mesh. // -// // -// Current mesh 'this' is copied into 'this->bgm'.Both meshes share the same // -// input tetgenio object, 'this->in', same tetgenbehavior object 'this->b'. // -// // -/////////////////////////////////////////////////////////////////////////////// + if (!b->convex && (tetarray->objects > 0l)) { // No -c option. + // Remove exterior tets. Hull tets are updated. + arraypool *newhullfacearray; + triface hulltet, casface; + point pa, pb, pc; -void tetgenmesh::duplicatebgmesh() -{ - triface tetloop, btetloop; - triface symtet, bsymtet; - face bhullsh, bneighsh; - point *idx2bplist, *tetptbaklist; - point ploop, bploop; - int idx, i; + newhullfacearray = new arraypool(sizeof(triface), 10); - if (!b->quiet) { - printf("Duplicating background mesh.\n"); - } - - // The background mesh itself has no background mesh. - // assert(bgm->bgm == (tetgenmesh *) NULL); - // The space for metric tensor should be allocated. - // assert(bgm->sizeoftensor > 0); - - // Copy point list. - idx2bplist = new point[points->items + 1]; - idx = in->firstnumber; - points->traversalinit(); - ploop = pointtraverse(); - while (ploop != (point) NULL) { - bgm->makepoint(&bploop); - // Copy coordinates, attributes. - for (i = 0; i < 3 + in->numberofpointattributes; i++) { - bploop[i] = ploop[i]; - } - // Transfer the metric tensor. - for (i = 0; i < bgm->sizeoftensor; i++) { - bploop[bgm->pointmtrindex + i] = ploop[pointmtrindex + i]; - // Metric tensor should have a positive value. - 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); - } - } - // Remember the point for searching. - idx2bplist[idx++] = bploop; - ploop = pointtraverse(); - } - - // Copy tetrahedra list. - tetptbaklist = new point[tetrahedrons->items + 1]; - idx = in->firstnumber; - tetrahedrons->traversalinit(); - tetloop.tet = tetrahedrontraverse(); - while (tetloop.tet != (tetrahedron *) NULL) { - bgm->maketetrahedron(&btetloop); - // Set the four corners. - for (i = 0; i < 4; i++) { - ploop = (point) tetloop.tet[4 + i]; - bploop = idx2bplist[pointmark(ploop)]; - btetloop.tet[4 + i] = (tetrahedron) bploop; - } - // Remember the tet for setting neighbor connections. - tetptbaklist[idx++] = (point) tetloop.tet[4]; - tetloop.tet[4] = (tetrahedron) btetloop.tet; - tetloop.tet = tetrahedrontraverse(); - } - - // Set the connections between background tetrahedra. Create background - // hull subfaces. Create the map of point-to-bgmtet. - idx = in->firstnumber; - tetrahedrons->traversalinit(); - tetloop.tet = tetrahedrontraverse(); - while (tetloop.tet != (tetrahedron *) NULL) { - // Get the corresponding background tet. - btetloop.tet = (tetrahedron *) tetloop.tet[4]; - // Set the four neighbors. - for (tetloop.loc = 0; tetloop.loc < 4; tetloop.loc++) { - btetloop.loc = tetloop.loc; - sym(tetloop, symtet); - if ((symtet.tet != dummytet) && (symtet.tet > tetloop.tet)) { - // Operate on the un-connected interior face. - bsymtet.tet = (tetrahedron *) symtet.tet[4]; // The saved bgm tet. - bsymtet.loc = symtet.loc; - bgm->bond(btetloop, bsymtet); - } else if (symtet.tet == dummytet) { - // Create a subface in background mesh. - bgm->makeshellface(bgm->subfaces, &bhullsh); - bgm->adjustedgering(btetloop, CCW); // face to inside. - bgm->setsorg(bhullsh, bgm->org(btetloop)); - bgm->setsdest(bhullsh, bgm->dest(btetloop)); - bgm->setsapex(bhullsh, bgm->apex(btetloop)); - bgm->tsbond(btetloop, bhullsh); - // Remember a hull face for point location. - bgm->dummytet[0] = bgm->encode(btetloop); - } - } - // Restore the backup tet point. - tetloop.tet[4] = (tetrahedron) tetptbaklist[idx++]; - // Make the point-to-bgmtet map for size interpolation. - btetloop.loc = 0; - for (i = 0; i < 4; i++) { - ploop = (point) tetloop.tet[4 + i]; - setpoint2bgmtet(ploop, bgm->encode(btetloop)); - } - // Go to the next tet, btet. - tetloop.tet = tetrahedrontraverse(); - } - - // Connect bgm hull subfaces. Note: all hull subfaces form a 2-manifold. - bgm->subfaces->traversalinit(); - bhullsh.sh = bgm->shellfacetraverse(bgm->subfaces); - while (bhullsh.sh != (shellface *) NULL) { - bhullsh.shver = 0; - bgm->stpivot(bhullsh, btetloop); - assert(btetloop.tet != bgm->dummytet); - bgm->adjustedgering(btetloop, CCW); - for (i = 0; i < 3; i++) { - bgm->spivot(bhullsh, bneighsh); - if (bneighsh.sh == bgm->dummysh) { - // This side is open, operate on it. - bsymtet = btetloop; - while (bgm->fnextself(bsymtet)); - bgm->tspivot(bsymtet, bneighsh); - bgm->findedge(&bneighsh, bgm->sdest(bhullsh), bgm->sorg(bhullsh)); - bgm->sbond(bhullsh, bneighsh); - } - bgm->enextself(btetloop); - bgm->senextself(bhullsh); - } - bhullsh.sh = bgm->shellfacetraverse(bgm->subfaces); - } - - delete [] tetptbaklist; - delete [] idx2bplist; -} - -//// //// -//// //// -//// reconstruct_cxx ////////////////////////////////////////////////////////// - -//// refine_cxx /////////////////////////////////////////////////////////////// -//// //// -//// //// - -/////////////////////////////////////////////////////////////////////////////// -// // -// marksharpsegments() Mark sharp segments. // -// // -// A segment s is called sharp if it is in one of the two cases: // -// (1) There is a segment s' intersecting with s. The internal angle (*) // -// between s and s' is acute. // -// (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. // -// // -// (*) The internal angle (or dihedral) bewteen two features means the angle // -// inside the mesh domain. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::marksharpsegments(REAL sharpangle) -{ - triface adjtet; - face startsh, spinsh, neighsh; - face segloop, prevseg, nextseg; - point eorg, edest; - REAL ang, smallang; - bool issharp; - int sharpsegcount; - - if (b->verbose > 0) { - printf(" Marking sharp segments.\n"); - } + // Create and save new hull tets. + for (i = 0; i < tetarray->objects; i++) { + parytet = (triface *) fastlookup(tetarray, i); + for (j = 0; j < 4; j++) { + decode(parytet->tet[j], tetloop); + if (!infected(tetloop)) { + // Found a new hull face (must be a subface). + tspivot(tetloop, checksh); + maketetrahedron(&hulltet); + pa = org(tetloop); + pb = dest(tetloop); + pc = apex(tetloop); + setvertices(hulltet, pb, pa, pc, dummypoint); + bond(tetloop, hulltet); + // Update the subface-to-tet map. + sesymself(checksh); + tsbond(hulltet, checksh); + // Update the segment-to-tet map. + for (k = 0; k < 3; k++) { + if (issubseg(tetloop)) { + tsspivot1(tetloop, checkseg); + tssbond1(hulltet, checkseg); + sstbond1(checkseg, hulltet); + } + enextself(tetloop); + eprevself(hulltet); + } + // Update the point-to-tet map. + setpoint2tet(pa, (tetrahedron) tetloop.tet); + setpoint2tet(pb, (tetrahedron) tetloop.tet); + setpoint2tet(pc, (tetrahedron) tetloop.tet); + // Save the exterior tet at this hull face. It still holds pointer + // to the adjacent interior tet. Use it to connect new hull tets. + newhullfacearray->newindex((void **) &parytet1); + parytet1->tet = parytet->tet; + parytet1->ver = j; + } // if (!infected(tetloop)) + } // j + } // i - smallang = sharpangle * PI / 180.; - sharpsegcount = 0; - eorg = edest = (point) NULL; // To avoid compiler warnings. - - // A segment s may have been split into many subsegments. Operate the one - // which contains the origin of s. Then mark the rest of subsegments. - subsegs->traversalinit(); - segloop.sh = shellfacetraverse(subsegs); - while (segloop.sh != (shellface *) NULL) { - segloop.shver = 0; - senext2(segloop, prevseg); - spivotself(prevseg); - if (prevseg.sh == dummysh) { - // Operate on this seg s. - issharp = false; - spivot(segloop, startsh); - if (startsh.sh != dummysh) { - // First check if two facets form an acute dihedral angle at s. - eorg = sorg(segloop); - edest = sdest(segloop); - spinsh = startsh; - do { - if (sorg(spinsh) != eorg) { - sesymself(spinsh); + // Connect new hull tets. + for (i = 0; i < newhullfacearray->objects; i++) { + parytet = (triface *) fastlookup(newhullfacearray, i); + fsym(*parytet, neightet); + // Get the new hull tet. + fsym(neightet, hulltet); + for (j = 0; j < 3; j++) { + esym(hulltet, casface); + if (casface.tet[casface.ver & 3] == NULL) { + // Since the boundary of the domain may not be a manifold, we + // find the adjacent hull face by traversing the tets in the + // exterior (which are all infected tets). + neightet = *parytet; + while (1) { + fnextself(neightet); + if (!infected(neightet)) break; } - // Only do test when the spinsh is faceing inward. - stpivot(spinsh, adjtet); - if (adjtet.tet != dummytet) { - // 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)) { - // 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. - if (!issharp) issharp = (ang < smallang); - // Remember the smallest facet dihedral angle. - minfacetdihed = minfacetdihed < ang ? minfacetdihed : ang; + if (!ishulltet(neightet)) { + // An interior tet. Get the new hull tet. + fsymself(neightet); + esymself(neightet); + } + // Bond them together. + bond(casface, neightet); + } + enextself(hulltet); + enextself(*parytet); + } // j + } // i + + if (subfacstack->objects > 0l) { + // Remove all subfaces which do not attach to any tetrahedron. + // Segments which are not attached to any subfaces and tets + // are deleted too. + face casingout, casingin; + long delsegcount = 0l; + + for (i = 0; i < subfacstack->objects; i++) { + parysh = (face *) fastlookup(subfacstack, i); + if (i == 0) { + if (b->verbose) { + printf("Warning: Removing an open face (%d, %d, %d)\n", + pointmark(sorg(*parysh)), pointmark(sdest(*parysh)), + pointmark(sapex(*parysh))); + } + } + // Dissolve this subface from face links. + for (j = 0; j < 3; j++) { + spivot(*parysh, casingout); + sspivot(*parysh, checkseg); + if (casingout.sh != NULL) { + casingin = casingout; + while (1) { + spivot(casingin, checksh); + if (checksh.sh == parysh->sh) break; + casingin = checksh; } - } - // 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. - spinsh = startsh; - do { - if (sorg(spinsh) != eorg) { - sesymself(spinsh); + if (casingin.sh != casingout.sh) { + // Update the link: ... -> casingin -> casingout ->... + sbond1(casingin, casingout); + } else { + // Only one subface at this edge is left. + sdissolve(casingout); } - // Calculate the angle between s and s' of this facet. - neighsh = spinsh; - // Rotate edges around 'eorg' until meeting another seg s'. Such - // seg (s') must exist since the facet is segment-bounded. - // The sum of the angles of faces at 'eorg' gives the internal - // angle between the two segments. - ang = 0.0; - do { - ang += interiorangle(eorg, sdest(neighsh), sapex(neighsh), NULL); - senext2self(neighsh); - sspivot(neighsh, nextseg); - if (nextseg.sh != dummysh) break; - // Go to the next coplanar subface. - spivotself(neighsh); - assert(neighsh.sh != dummysh); - if (sorg(neighsh) != eorg) { - sesymself(neighsh); + if (checkseg.sh != NULL) { + // Make sure the segment does not connect to a dead one. + ssbond(casingout, checkseg); + } + } else { + if (checkseg.sh != NULL) { + // The segment is also dead. + if (delsegcount == 0) { + if (b->verbose) { + printf("Warning: Removing a dangling segment (%d, %d)\n", + pointmark(sorg(checkseg)), pointmark(sdest(checkseg))); + } } - } while (true); - // Only do check if a sharp angle has not been found. - if (!issharp) issharp = (ang < smallang); - // Remember the smallest input face angle. - 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); + shellfacedealloc(subsegs, checkseg.sh); + delsegcount++; + } } - assert(sorg(nextseg) == edest); - edest = sdest(nextseg); - // Go the next connected subsegment at edest. - senextself(nextseg); - spivotself(nextseg); + senextself(*parysh); + } // j + // Delete this subface. + shellfacedealloc(subfaces, parysh->sh); + } // i + if (b->verbose) { + printf(" Deleted %ld subfaces.\n", subfacstack->objects); + if (delsegcount > 0) { + printf(" Deleted %ld segments.\n", delsegcount); } - sharpsegcount++; } - } - segloop.sh = shellfacetraverse(subsegs); - } + subfacstack->restart(); + } // if (subfacstack->objects > 0l) - // So far we have marked all segments which have an acute dihedral angle - // or whose ORIGINs have an acute angle. In the un-marked subsegments, - // there are possible ones whose DESTINATIONs have an acute angle. - subsegs->traversalinit(); - segloop.sh = shellfacetraverse(subsegs); - while (segloop.sh != (shellface *) NULL) { - // Only operate if s is non-sharp and contains the dest. - segloop.shver = 0; - senext(segloop, nextseg); - spivotself(nextseg); - // if ((nextseg.sh == dummysh) && (shelltype(segloop) != SHARP)) { - if (nextseg.sh == dummysh) { - // issharp = false; - issharp = (shelltype(segloop) == SHARP); - spivot(segloop, startsh); - if (startsh.sh != dummysh) { - // Check if s forms an acute angle with another seg. - eorg = sdest(segloop); - spinsh = startsh; - do { - if (sorg(spinsh) != eorg) { - sesymself(spinsh); - } - // Calculate the angle between s and s' of this facet. - neighsh = spinsh; - ang = 0.0; - do { - ang += interiorangle(eorg, sdest(neighsh), sapex(neighsh), NULL); - senext2self(neighsh); - sspivot(neighsh, nextseg); - if (nextseg.sh != dummysh) break; - // Go to the next coplanar subface. - spivotself(neighsh); - assert(neighsh.sh != dummysh); - if (sorg(neighsh) != eorg) { - sesymself(neighsh); + if (cavetetvertlist->objects > 0l) { + // Some vertices may lie in exterior. Marke them as UNUSEDVERTEX. + long delvertcount = unuverts; + long delsteinercount = 0l; + + for (i = 0; i < cavetetvertlist->objects; i++) { + parypt = (point *) fastlookup(cavetetvertlist, i); + decode(point2tet(*parypt), neightet); + if (infected(neightet)) { + // Found an exterior vertex. + if (pointmark(*parypt) > + (in->numberofpoints - (in->firstnumber ? 0 : 1))) { + // A Steiner point. + if (pointtype(*parypt) == FREESEGVERTEX) { + st_segref_count--; + } else if (pointtype(*parypt) == FREEFACETVERTEX) { + st_facref_count--; + } else { + assert(pointtype(*parypt) == FREEVOLVERTEX); + st_volref_count--; } - } while (true); - // Only do check if a sharp angle has not been found. - if (!issharp) issharp = (ang < smallang); - // Remember the smallest input face angle. - 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. - senext2self(prevseg); - spivotself(prevseg); - } - sharpsegcount++; + delsteinercount++; + if (steinerleft > 0) steinerleft++; + } + setpointtype(*parypt, UNUSEDVERTEX); + unuverts++; + } } - } - segloop.sh = shellfacetraverse(subsegs); - } - if ((b->verbose > 0) && (sharpsegcount > 0)) { - printf(" %d sharp segments.\n", sharpsegcount); - } -} + if (b->verbose) { + if (unuverts > delvertcount) { + if (delsteinercount > 0l) { + if (unuverts > (delvertcount + delsteinercount)) { + printf(" Removed %ld exterior input vertices.\n", + unuverts - delvertcount - delsteinercount); + } + printf(" Removed %ld exterior Steiner vertices.\n", + delsteinercount); + } else { + printf(" Removed %ld exterior input vertices.\n", + unuverts - delvertcount); + } + } + } + cavetetvertlist->restart(); + // Comment: 'subvertstack' will be cleaned in routine + // suppresssteinerpoints(). + } // if (cavetetvertlist->objects > 0l) -/////////////////////////////////////////////////////////////////////////////// -// // -// decidefeaturepointsizes() Decide the sizes for all feature points. // -// // -// A feature point is a point on a sharp segment. Every feature point p will // -// be assigned a positive size which is the radius of the protecting ball. // -// // -// The size of a feature point may be specified by one of the following ways:// -// (1) directly specifying on an input vertex (by using .mtr file); // -// (2) imposing a fixed maximal volume constraint ('-a__' option); // -// (3) imposing a maximal volume constraint in a region ('-a' option); // -// (4) imposing a maximal area constraint on a facet (in .var file); // -// (5) imposing a maximal length constraint on a segment (in .var file); // -// (6) combining (1) - (5). // -// (7) automatically deriving a size if none of (1) - (6) is available. // -// In case (7),the size of p is set to be the smallest edge length among all // -// edges connecting at p. The final size of p is the minimum of (1) - (7). // -// // -/////////////////////////////////////////////////////////////////////////////// + // Update the hull size. + hullsize += (newhullfacearray->objects - hullarray->objects); -void tetgenmesh::decidefeaturepointsizes() -{ - list *tetlist, *verlist; - shellface **segsperverlist; - triface starttet; - face shloop; - face checkseg, prevseg, nextseg, testseg; - point ploop, adjpt, e1, e2; - REAL lfs_0, len, vol, maxlen, varlen; - bool isfeature; - int *idx2seglist; - int featurecount; - int idx, i, j; + // Delete all exterior tets and old hull tets. + for (i = 0; i < tetarray->objects; i++) { + parytet = (triface *) fastlookup(tetarray, i); + tetrahedrondealloc(parytet->tet); + } + tetarray->restart(); - if (b->verbose > 0) { - printf(" Deciding feature-point sizes.\n"); - } + for (i = 0; i < hullarray->objects; i++) { + parytet = (triface *) fastlookup(hullarray, i); + tetrahedrondealloc(parytet->tet); + } + hullarray->restart(); - // Constructing a map from vertices to segments. - makesegmentmap(idx2seglist, segsperverlist); - // Initialize working lists. - tetlist = new list(sizeof(triface), NULL, 256); - verlist = new list(sizeof(point *), NULL, 256); + delete newhullfacearray; + } // if (!b->convex && (tetarray->objects > 0l)) - if (b->fixedvolume) { - // A fixed volume constraint is imposed. This gives an upper bound of - // the maximal radius of the protect ball of a vertex. - maxlen = pow(6.0 * b->maxvolume, 1.0/3.0); - } + if (b->convex && (tetarray->objects > 0l)) { // With -c option + // In this case, all exterior tets get a region marker '-1'. + assert(b->regionattrib > 0); // -A option must be enabled. + int attrnum = numelemattrib - 1; - // First only assign a size of p if p is not a Steiner point. The size of - // a Steiner point will be interpolated later from the endpoints of the - // segment on which it lies. - featurecount = 0; - points->traversalinit(); - ploop = pointtraverse(); - while (ploop != (point) NULL) { - if (pointtype(ploop) != FREESEGVERTEX) { - // Is p a feature point? - isfeature = false; - idx = pointmark(ploop) - in->firstnumber; - for (i = idx2seglist[idx]; i < idx2seglist[idx + 1] && !isfeature; i++) { - checkseg.sh = segsperverlist[i]; - isfeature = (shelltype(checkseg) == SHARP); - } - // Decide the size of p if it is on a sharp segment. - if (isfeature) { - // Find a tet containing p; - sstpivot(&checkseg, &starttet); - // Form star(p). - tetlist->append(&starttet); - formstarpolyhedron(ploop, tetlist, verlist, true); - // Decide the size for p if no input size is given on input. - if (ploop[pointmtrindex] == 0.0) { - // Calculate lfs_0(p). - lfs_0 = longest; - for (i = 0; i < verlist->len(); i++) { - adjpt = * (point *)(* verlist)[i]; - if (pointtype(adjpt) == FREESEGVERTEX) { - // A Steiner point q. Find the seg it lies on. - sdecode(point2seg(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.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.shver = 0; - if (sorg(nextseg) != e2) { - sesymself(nextseg); - } - assert(sorg(nextseg) == e2); - e2 = sdest(nextseg); - } while (true); - // 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. - adjpt = e2; - } else if (ploop == e2) { - // Set q to be the org of this seg. - adjpt = e1; - } - } - len = distance(ploop, adjpt); - if (lfs_0 > len) lfs_0 = len; - } - ploop[pointmtrindex] = lfs_0; - } - if (b->fixedvolume) { - // A fixed volume constraint is imposed. Adjust H(p) <= maxlen. - if (ploop[pointmtrindex] > maxlen) { - ploop[pointmtrindex] = maxlen; - } - } - if (b->varvolume) { - // Variant volume constraints are imposed. Adjust H(p) <= varlen. - for (i = 0; i < tetlist->len(); i++) { - starttet = * (triface *)(* tetlist)[i]; - vol = volumebound(starttet.tet); - if (vol > 0.0) { - varlen = pow(6 * vol, 1.0/3.0); - if (ploop[pointmtrindex] > varlen) { - ploop[pointmtrindex] = varlen; - } - } - } - } - // Clear working lists. - tetlist->clear(); - verlist->clear(); - featurecount++; - } else { - // NO feature point, set the size of p be zero. - ploop[pointmtrindex] = 0.0; - } - } // if (pointtype(ploop) != FREESEGVERTEX) { - ploop = pointtraverse(); - } + for (i = 0; i < tetarray->objects; i++) { + parytet = (triface *) fastlookup(tetarray, i); + setelemattribute(parytet->tet, attrnum, -1); + } + tetarray->restart(); - if (b->verbose > 1) { - printf(" %d feature points.\n", featurecount); - } + for (i = 0; i < hullarray->objects; i++) { + parytet = (triface *) fastlookup(hullarray, i); + uninfect(*parytet); + } + hullarray->restart(); - if (!b->refine) { - // Second only assign sizes for all Steiner points. A Steiner point p - // inserted on a sharp segment s is assigned a size by interpolating - // the sizes of the original endpoints of s. - featurecount = 0; - points->traversalinit(); - ploop = pointtraverse(); - while (ploop != (point) NULL) { - if (pointtype(ploop) == FREESEGVERTEX) { - if (ploop[pointmtrindex] == 0.0) { - sdecode(point2seg(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); - len = distance(e1, e2); - lfs_0 = distance(e1, ploop); - // The following assert() happens when -Y option is used. - if (b->nobisect == 0) { - assert(lfs_0 < len); - } - ploop[pointmtrindex] = e1[pointmtrindex] - + (lfs_0 / len) * (e2[pointmtrindex] - e1[pointmtrindex]); - featurecount++; - } else { - // NO feature point, set the size of p be zero. - ploop[pointmtrindex] = 0.0; - } // if (shelltype(checkseg) == SHARP) - } // if (ploop[pointmtrindex] == 0.0) - } // if (pointtype(ploop) != FREESEGVERTEX) - ploop = pointtraverse(); + if (subfacstack->objects > 0l) { + for (i = 0; i < subfacstack->objects; i++) { + parysh = (face *) fastlookup(subfacstack, i); + suninfect(*parysh); + } + subfacstack->restart(); } - if ((b->verbose > 1) && (featurecount > 0)) { - printf(" %d Steiner feature points.\n", featurecount); + + if (cavetetvertlist->objects > 0l) { + cavetetvertlist->restart(); } - } + } // if (b->convex && (tetarray->objects > 0l)) - if (varconstraint) { - // A .var file exists. Adjust feature sizes. - if (in->facetconstraintlist) { - // Have facet area constrains. - subfaces->traversalinit(); - shloop.sh = shellfacetraverse(subfaces); - while (shloop.sh != (shellface *) NULL) { - varlen = areabound(shloop); - if (varlen > 0.0) { - // Check if the three corners are feature points. - varlen = sqrt(varlen); - for (j = 0; j < 3; j++) { - ploop = (point) shloop.sh[3 + j]; - isfeature = false; - idx = pointmark(ploop) - in->firstnumber; - for (i = idx2seglist[idx]; i < idx2seglist[idx + 1] && !isfeature; - i++) { - checkseg.sh = segsperverlist[i]; - isfeature = (shelltype(checkseg) == SHARP); + if (b->regionattrib) { // With -A option. + if (!b->quiet) { + printf("Spreading region attributes.\n"); + } + REAL volume; + int attr, maxattr = 0; // Choose a small number here. + int attrnum = numelemattrib - 1; + // Comment: The element region marker is at the end of the list of + // the element attributes. + int regioncount = 0; + + // If has user-defined region attributes. + if (in->numberofregions > 0) { + // Spread region attributes. + for (i = 0; i < 5 * in->numberofregions; i += 5) { + if (regiontets[i/5].tet != NULL) { + attr = (int) in->regionlist[i + 3]; + if (attr > maxattr) { + maxattr = attr; + } + volume = in->regionlist[i + 4]; + tetarray->restart(); // Re-use this array. + infect(regiontets[i/5]); + tetarray->newindex((void **) &parytet); + *parytet = regiontets[i/5]; + // Collect and set attrs for all tets of this region. + for (j = 0; j < tetarray->objects; j++) { + parytet = (triface *) fastlookup(tetarray, j); + tetloop = *parytet; + setelemattribute(tetloop.tet, attrnum, attr); + if (b->varvolume) { // If has -a option. + setvolumebound(tetloop.tet, volume); } - if (isfeature) { - assert(ploop[pointmtrindex] > 0.0); - if (ploop[pointmtrindex] > varlen) { - ploop[pointmtrindex] = varlen; + for (k = 0; k < 4; k++) { + decode(tetloop.tet[k], neightet); + // Is the adjacent already checked? + if (!infected(neightet)) { + // Is this side protected by a subface? + if (!issubface(neightet)) { + infect(neightet); + tetarray->newindex((void **) &parytet); + *parytet = neightet; + } } - } - } // for (j = 0; j < 3; j++) - } - shloop.sh = shellfacetraverse(subfaces); - } + } // k + } // j + regioncount++; + } // if (regiontets[i/5].tet != NULL) + } // i } - if (in->segmentconstraintlist) { - // Have facet area constrains. - subsegs->traversalinit(); - shloop.sh = shellfacetraverse(subsegs); - while (shloop.sh != (shellface *) NULL) { - varlen = areabound(shloop); - if (varlen > 0.0) { - // Check if the two endpoints are feature points. - for (j = 0; j < 2; j++) { - ploop = (point) shloop.sh[3 + j]; - isfeature = false; - idx = pointmark(ploop) - in->firstnumber; - for (i = idx2seglist[idx]; i < idx2seglist[idx + 1] && !isfeature; - i++) { - checkseg.sh = segsperverlist[i]; - isfeature = (shelltype(checkseg) == SHARP); - } - if (isfeature) { - assert(ploop[pointmtrindex] > 0.0); - if (ploop[pointmtrindex] > varlen) { - ploop[pointmtrindex] = varlen; + + // Set attributes for all tetrahedra. + attr = maxattr + 1; + tetrahedrons->traversalinit(); + tetloop.tet = tetrahedrontraverse(); + while (tetloop.tet != (tetrahedron *) NULL) { + if (!infected(tetloop)) { + // An unmarked region. + tetarray->restart(); // Re-use this array. + infect(tetloop); + tetarray->newindex((void **) &parytet); + *parytet = tetloop; + // Find and mark all tets. + for (j = 0; j < tetarray->objects; j++) { + parytet = (triface *) fastlookup(tetarray, j); + tetloop = *parytet; + setelemattribute(tetloop.tet, attrnum, attr); + for (k = 0; k < 4; k++) { + decode(tetloop.tet[k], neightet); + // Is the adjacent tet already checked? + if (!infected(neightet)) { + // Is this side protected by a subface? + if (!issubface(neightet)) { + infect(neightet); + tetarray->newindex((void **) &parytet); + *parytet = neightet; } } - } // for (j = 0; j < 2; j++) - } - shloop.sh = shellfacetraverse(subsegs); + } // k + } // j + attr++; // Increase the attribute. + regioncount++; } + tetloop.tet = tetrahedrontraverse(); } - } // if (varconstraint) + // Until here, every tet has a region attribute. - delete [] segsperverlist; - delete [] idx2seglist; - delete tetlist; - delete verlist; -} + // Uninfect processed tets. + tetrahedrons->traversalinit(); + tetloop.tet = tetrahedrontraverse(); + while (tetloop.tet != (tetrahedron *) NULL) { + uninfect(tetloop); + tetloop.tet = tetrahedrontraverse(); + } -/////////////////////////////////////////////////////////////////////////////// -// // -// enqueueencsub() Add an encroached subface into the queue. // -// // -/////////////////////////////////////////////////////////////////////////////// + if (b->verbose) { + //assert(regioncount > 0); + if (regioncount > 1) { + printf(" Found %d subdomains.\n", regioncount); + } else { + printf(" Found %d domain.\n", regioncount); + } + } + } // if (b->regionattrib) -void tetgenmesh::enqueueencsub(face* testsub, point encpt, int quenumber, - REAL* cent) -{ - badface *encsub; - int i; + if (regiontets != NULL) { + delete [] regiontets; + } + delete tetarray; + delete hullarray; - 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); + if (!b->convex) { // No -c option + // The mesh is non-convex now. + nonconvex = 1; + + // Push all hull tets into 'flipstack'. + tetrahedrons->traversalinit(); + tetloop.ver = 11; // The face opposite to dummypoint. + tetloop.tet = alltetrahedrontraverse(); + while (tetloop.tet != (tetrahedron *) NULL) { + if ((point) tetloop.tet[7] == dummypoint) { + fsym(tetloop, neightet); + flippush(flipstack, &neightet); } + tetloop.tet = alltetrahedrontraverse(); } - } else { - if (b->verbose > 2) { - printf(" Ignore an encroached subface (%d, %d, %d).\n", - pointmark(sorg(*testsub)), pointmark(sdest(*testsub)), - pointmark(sapex(*testsub))); - } - } -} -/////////////////////////////////////////////////////////////////////////////// -// // -// dequeueencsub() Remove an enc-subface from the front of the queue. // -// // -/////////////////////////////////////////////////////////////////////////////// + flipconstraints fc; + fc.enqflag = 2; + long sliver_peel_count = lawsonflip3d(&fc); -tetgenmesh::badface* tetgenmesh::dequeueencsub(int* pquenumber) -{ - badface *result; - int quenumber; - - // Look for a nonempty queue. - for (quenumber = 2; quenumber >= 0; quenumber--) { - result = subquefront[quenumber]; - if (result != (badface *) NULL) { - // Remove the badface from the queue. - subquefront[quenumber] = result->nextitem; - // Maintain a pointer to the NULL pointer at the end of the queue. - if (subquefront[quenumber] == (badface *) NULL) { - subquetail[quenumber] = &subquefront[quenumber]; - } - *pquenumber = quenumber; - return result; + if (sliver_peel_count > 0l) { + if (b->verbose) { + printf(" Removed %ld hull slivers.\n", sliver_peel_count); + } } - } - return (badface *) NULL; + unflipqueue->restart(); + } // if (!b->convex) } /////////////////////////////////////////////////////////////////////////////// // // -// enqueuebadtet() Add a tetrahedron into the queue. // +// reconstructmesh() Reconstruct a tetrahedral mesh. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::enqueuebadtet(triface* testtet, REAL ratio2, REAL* cent) +void tetgenmesh::reconstructmesh() { - badface *newbadtet; - int queuenumber; - int i; + tetrahedron *ver2tetarray; + point *idx2verlist; + triface tetloop, checktet, prevchktet; + triface hulltet, face1, face2; + tetrahedron tptr; + face subloop, neighsh, nextsh; + face segloop; + shellface sptr; + point p[4], q[3]; + REAL ori, attrib, volume; + REAL angtol, ang; + int eextras, marker = 0; + int bondflag; + int t1ver; + int idx, i, j, k; - // Allocate space for the bad tetrahedron. - newbadtet = (badface *) badtetrahedrons->alloc(); - newbadtet->tt = *testtet; - newbadtet->key = ratio2; - if (cent != NULL) { - for (i = 0; i < 3; i++) newbadtet->cent[i] = cent[i]; - } else { - for (i = 0; i < 3; i++) newbadtet->cent[i] = 0.0; - } - newbadtet->forg = org(*testtet); - newbadtet->fdest = dest(*testtet); - newbadtet->fapex = apex(*testtet); - newbadtet->foppo = oppo(*testtet); - newbadtet->nextitem = (badface *) NULL; - // Determine the appropriate queue to put the bad tetrahedron into. - if (ratio2 > b->goodratio) { - // queuenumber = (int) ((ratio2 - b->goodratio) / 0.5); - queuenumber = (int) (64.0 - 64.0 / ratio2); - // 'queuenumber' may overflow (negative) caused by a very large ratio. - if ((queuenumber > 63) || (queuenumber < 0)) { - queuenumber = 63; - } - } else { - // It's not a bad ratio; put the tet in the lowest-priority queue. - queuenumber = 0; + if (!b->quiet) { + printf("Reconstructing mesh ...\n"); } - // Are we inserting into an empty queue? - if (tetquefront[queuenumber] == (badface *) NULL) { - // Yes. Will this become the highest-priority queue? - if (queuenumber > firstnonemptyq) { - // Yes, this is the highest-priority queue. - nextnonemptyq[queuenumber] = firstnonemptyq; - firstnonemptyq = queuenumber; - } else { - // No. Find the queue with next higher priority. - i = queuenumber + 1; - while (tetquefront[i] == (badface *) NULL) { - i++; - } - // Mark the newly nonempty queue as following a higher-priority queue. - nextnonemptyq[queuenumber] = nextnonemptyq[i]; - nextnonemptyq[i] = queuenumber; - } - // Put the bad tetrahedron at the beginning of the (empty) queue. - tetquefront[queuenumber] = newbadtet; + if (b->convex) { // -c option. + // Assume the mesh is convex. Exterior tets have region attribute -1. + assert(in->numberoftetrahedronattributes > 0); } else { - // Add the bad tetrahedron to the end of an already nonempty queue. - tetquetail[queuenumber]->nextitem = newbadtet; + // Assume the mesh is non-convex. + nonconvex = 1; } - // Maintain a pointer to the last tetrahedron of the queue. - tetquetail[queuenumber] = newbadtet; - if (b->verbose > 2) { - printf(" Queueing bad tet: (%d, %d, %d, %d), ratio %g, qnum %d.\n", - pointmark(newbadtet->forg), pointmark(newbadtet->fdest), - pointmark(newbadtet->fapex), pointmark(newbadtet->foppo), - sqrt(ratio2), queuenumber); + // Create a map from indices to vertices. + makeindex2pointmap(idx2verlist); + // 'idx2verlist' has length 'in->numberofpoints + 1'. + if (in->firstnumber == 1) { + idx2verlist[0] = dummypoint; // Let 0th-entry be dummypoint. } -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// dequeuebadtet() Remove a tetrahedron from the front of the queue. // -// // -/////////////////////////////////////////////////////////////////////////////// -tetgenmesh::badface* tetgenmesh::topbadtetra() -{ - // Keep a record of which queue was accessed in case dequeuebadtetra() - // is called later. - recentq = firstnonemptyq; - // If no queues are nonempty, return NULL. - if (firstnonemptyq < 0) { - return (badface *) NULL; - } else { - // Return the first tetrahedron of the highest-priority queue. - return tetquefront[firstnonemptyq]; + // Allocate an array that maps each vertex to its adjacent tets. + ver2tetarray = new tetrahedron[in->numberofpoints + 1]; + //for (i = 0; i < in->numberofpoints + 1; i++) { + for (i = in->firstnumber; i < in->numberofpoints + in->firstnumber; i++) { + setpointtype(idx2verlist[i], VOLVERTEX); // initial type. + ver2tetarray[i] = NULL; } -} - -void tetgenmesh::dequeuebadtet() -{ - badface *deadbadtet; - int i; - // If queues were empty last time topbadtetra() was called, do nothing. - if (recentq >= 0) { - // Find the tetrahedron last returned by topbadtetra(). - deadbadtet = tetquefront[recentq]; - // Remove the tetrahedron from the queue. - tetquefront[recentq] = deadbadtet->nextitem; - // If this queue is now empty, update the list of nonempty queues. - if (deadbadtet == tetquetail[recentq]) { - // Was this the highest-priority queue? - if (firstnonemptyq == recentq) { - // Yes; find the queue with next lower priority. - firstnonemptyq = nextnonemptyq[firstnonemptyq]; - } else { - // No; find the queue with next higher priority. - i = recentq + 1; - while (tetquefront[i] == (badface *) NULL) { - i++; - } - nextnonemptyq[i] = nextnonemptyq[recentq]; - } + // Create the tetrahedra and connect those that share a common face. + for (i = 0; i < in->numberoftetrahedra; i++) { + // Get the four vertices. + idx = i * in->numberofcorners; + for (j = 0; j < 4; j++) { + p[j] = idx2verlist[in->tetrahedronlist[idx++]]; } - // Return the bad tetrahedron to the pool. - badfacedealloc(badtetrahedrons, deadbadtet); - } -} + // Check the orientation. + ori = orient3d(p[0], p[1], p[2], p[3]); + if (ori > 0.0) { + // Swap the first two vertices. + q[0] = p[0]; p[0] = p[1]; p[1] = q[0]; + } else if (ori == 0.0) { + if (!b->quiet) { + printf("Warning: Tet #%d is degenerate.\n", i + in->firstnumber); + } + } + // Create a new tetrahedron. + maketetrahedron(&tetloop); // tetloop.ver = 11. + setvertices(tetloop, p[0], p[1], p[2], p[3]); + // Set element attributes if they exist. + for (j = 0; j < in->numberoftetrahedronattributes; j++) { + idx = i * in->numberoftetrahedronattributes; + attrib = in->tetrahedronattributelist[idx + j]; + setelemattribute(tetloop.tet, j, attrib); + } + // If -a switch is used (with no number follows) Set a volume + // constraint if it exists. + if (b->varvolume) { + if (in->tetrahedronvolumelist != (REAL *) NULL) { + volume = in->tetrahedronvolumelist[i]; + } else { + volume = -1.0; + } + setvolumebound(tetloop.tet, volume); + } + // Try connecting this tet to others that share the common faces. + for (tetloop.ver = 0; tetloop.ver < 4; tetloop.ver++) { + p[3] = oppo(tetloop); + // Look for other tets having this vertex. + idx = pointmark(p[3]); + tptr = ver2tetarray[idx]; + // Link the current tet to the next one in the stack. + tetloop.tet[8 + tetloop.ver] = tptr; + // Push the current tet onto the stack. + ver2tetarray[idx] = encode(tetloop); + decode(tptr, checktet); + if (checktet.tet != NULL) { + p[0] = org(tetloop); // a + p[1] = dest(tetloop); // b + p[2] = apex(tetloop); // c + prevchktet = tetloop; + do { + q[0] = org(checktet); // a' + q[1] = dest(checktet); // b' + q[2] = apex(checktet); // c' + // Check the three faces at 'd' in 'checktet'. + bondflag = 0; + for (j = 0; j < 3; j++) { + // Go to the face [b',a',d], or [c',b',d], or [a',c',d]. + esym(checktet, face2); + if (face2.tet[face2.ver & 3] == NULL) { + k = ((j + 1) % 3); + if (q[k] == p[0]) { // b', c', a' = a + if (q[j] == p[1]) { // a', b', c' = b + // [#,#,d] is matched to [b,a,d]. + esym(tetloop, face1); + bond(face1, face2); + bondflag++; + } + } + if (q[k] == p[1]) { // b',c',a' = b + if (q[j] == p[2]) { // a',b',c' = c + // [#,#,d] is matched to [c,b,d]. + enext(tetloop, face1); + esymself(face1); + bond(face1, face2); + bondflag++; + } + } + if (q[k] == p[2]) { // b',c',a' = c + if (q[j] == p[0]) { // a',b',c' = a + // [#,#,d] is matched to [a,c,d]. + eprev(tetloop, face1); + esymself(face1); + bond(face1, face2); + bondflag++; + } + } + } else { + bondflag++; + } + enextself(checktet); + } // j + // Go to the next tet in the link. + tptr = checktet.tet[8 + checktet.ver]; + if (bondflag == 3) { + // All three faces at d in 'checktet' have been connected. + // It can be removed from the link. + prevchktet.tet[8 + prevchktet.ver] = tptr; + } else { + // Bakup the previous tet in the link. + prevchktet = checktet; + } + decode(tptr, checktet); + } while (checktet.tet != NULL); + } // if (checktet.tet != NULL) + } // for (tetloop.ver = 0; ... + } // i -/////////////////////////////////////////////////////////////////////////////// -// // -// 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. // -// // -// 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. // -// If and 'enqflag' is TRUE, add it into 'badsubsegs' if s is encroached. // -// // -// If 'prefpt' != NULL, it returns the reference point (defined in my paper) // -// if it exists. This point is will be used to split s. // -// // -/////////////////////////////////////////////////////////////////////////////// + // Remember a tet of the mesh. + recenttet = tetloop; -bool tetgenmesh::checkseg4encroach(face* testseg, point testpt, point* prefpt, - bool enqflag) -{ - badface *encsubseg; - triface starttet, spintet; - point eorg, edest, eapex, encpt; - REAL cent[3], radius, dist, diff; - REAL maxradius; - bool enq; - int hitbdry; - - enq = false; - eorg = sorg(*testseg); - edest = sdest(*testseg); - cent[0] = 0.5 * (eorg[0] + edest[0]); - cent[1] = 0.5 * (eorg[1] + edest[1]); - cent[2] = 0.5 * (eorg[2] + edest[2]); - radius = distance(cent, eorg); - - if (varconstraint && (areabound(*testseg) > 0.0)) { - enq = (2.0 * radius) > areabound(*testseg); - } - - if (!enq) { - maxradius = 0.0; - if (testpt == (point) NULL) { - // Check if it is encroached by traversing all faces containing it. - sstpivot(testseg, &starttet); - eapex = apex(starttet); - spintet = starttet; - hitbdry = 0; - do { - dist = distance(cent, apex(spintet)); - diff = dist - radius; - if (fabs(diff) / radius <= b->epsilon) diff = 0.0; // Rounding. - if (diff <= 0.0) { - // s is encroached. - enq = true; - if (prefpt != (point *) NULL) { - // Find the reference point. - encpt = apex(spintet); - circumsphere(eorg, edest, encpt, NULL, NULL, &dist); - if (dist > maxradius) { - // Rememebr this point. - *prefpt = encpt; - maxradius = dist; + // Create hull tets, create the point-to-tet map, and clean up the + // temporary spaces used in each tet. + hullsize = tetrahedrons->items; + + tetrahedrons->traversalinit(); + tetloop.tet = tetrahedrontraverse(); + while (tetloop.tet != (tetrahedron *) NULL) { + tptr = encode(tetloop); + for (tetloop.ver = 0; tetloop.ver < 4; tetloop.ver++) { + if (tetloop.tet[tetloop.ver] == NULL) { + // Create a hull tet. + maketetrahedron(&hulltet); + p[0] = org(tetloop); + p[1] = dest(tetloop); + p[2] = apex(tetloop); + setvertices(hulltet, p[1], p[0], p[2], dummypoint); + bond(tetloop, hulltet); + // Try connecting this to others that share common hull edges. + for (j = 0; j < 3; j++) { + fsym(hulltet, face2); + while (1) { + if (face2.tet == NULL) break; + esymself(face2); + if (apex(face2) == dummypoint) break; + fsymself(face2); + } + if (face2.tet != NULL) { + // Found an adjacent hull tet. + assert(face2.tet[face2.ver & 3] == NULL); + esym(hulltet, face1); + bond(face1, face2); + } + enextself(hulltet); + } + //hullsize++; + } + // Create the point-to-tet map. + setpoint2tet((point) (tetloop.tet[4 + tetloop.ver]), tptr); + // Clean the temporary used space. + tetloop.tet[8 + tetloop.ver] = NULL; + } + tetloop.tet = tetrahedrontraverse(); + } + + hullsize = tetrahedrons->items - hullsize; + + // Subfaces will be inserted into the mesh. + if (in->trifacelist != NULL) { + // A .face file is given. It may contain boundary faces. Insert them. + for (i = 0; i < in->numberoftrifaces; i++) { + // Is it a subface? + if (in->trifacemarkerlist != NULL) { + marker = in->trifacemarkerlist[i]; + } else { + // Face markers are not available. Assume all of them are subfaces. + marker = 1; + } + if (marker > 0) { + idx = i * 3; + for (j = 0; j < 3; j++) { + p[j] = idx2verlist[in->trifacelist[idx++]]; + } + // Search the subface. + bondflag = 0; + // Make sure all vertices are in the mesh. Avoid crash. + for (j = 0; j < 3; j++) { + decode(point2tet(p[j]), checktet); + if (checktet.tet == NULL) break; + } + if ((j == 3) && getedge(p[0], p[1], &checktet)) { + tetloop = checktet; + q[2] = apex(checktet); + while (1) { + if (apex(tetloop) == p[2]) { + // Found the face. + // Check if there exist a subface already? + tspivot(tetloop, neighsh); + if (neighsh.sh != NULL) { + // Found a duplicated subface. + // This happens when the mesh was generated by other mesher. + bondflag = 0; + } else { + bondflag = 1; + } + break; } - } else { - break; + fnextself(tetloop); + if (apex(tetloop) == q[2]) break; } } - if (!fnextself(spintet)) { - hitbdry++; - if (hitbdry < 2) { - esym(starttet, spintet); - if (!fnextself(spintet)) { - hitbdry++; - } + if (bondflag) { + // Create a new subface. + makeshellface(subfaces, &subloop); + setshvertices(subloop, p[0], p[1], p[2]); + // Create the point-to-subface map. + sptr = sencode(subloop); + for (j = 0; j < 3; j++) { + setpointtype(p[j], FACETVERTEX); // initial type. + setpoint2sh(p[j], sptr); + } + if (in->trifacemarkerlist != NULL) { + setshellmark(subloop, in->trifacemarkerlist[i]); + } + // Insert the subface into the mesh. + tsbond(tetloop, subloop); + fsymself(tetloop); + sesymself(subloop); + tsbond(tetloop, subloop); + } else { + if (!b->quiet) { + if (neighsh.sh == NULL) { + printf("Warning: Subface #%d [%d,%d,%d] is missing.\n", + i + in->firstnumber, pointmark(p[0]), pointmark(p[1]), + pointmark(p[2])); + } else { + printf("Warning: Ignore a dunplicated subface #%d [%d,%d,%d].\n", + i + in->firstnumber, pointmark(p[0]), pointmark(p[1]), + pointmark(p[2])); + } + } + } // if (bondflag) + } // if (marker > 0) + } // i + } // if (in->trifacelist) + + // Indentify subfaces from the mesh. + // Create subfaces for hull faces (if they're not subface yet) and + // interior faces which separate two different materials. + eextras = in->numberoftetrahedronattributes; + tetrahedrons->traversalinit(); + tetloop.tet = tetrahedrontraverse(); + while (tetloop.tet != (tetrahedron *) NULL) { + for (tetloop.ver = 0; tetloop.ver < 4; tetloop.ver++) { + tspivot(tetloop, neighsh); + if (neighsh.sh == NULL) { + bondflag = 0; + fsym(tetloop, checktet); + if (ishulltet(checktet)) { + // A hull face. + if (!b->convex) { + bondflag = 1; // Insert a hull subface. + } + } else { + if (eextras > 0) { + if (elemattribute(tetloop.tet, eextras - 1) != + elemattribute(checktet.tet, eextras - 1)) { + bondflag = 1; // Insert an interior interface. + } } } - } while (apex(spintet) != eapex && (hitbdry < 2)); - } else { - // Only check if 'testseg' is encroached by 'testpt'. - dist = distance(cent, testpt); - diff = dist - radius; - if (fabs(diff) / radius <= b->epsilon) diff = 0.0; // Rounding. - enq = (diff <= 0.0); + if (bondflag) { + // Create a new subface. + makeshellface(subfaces, &subloop); + p[0] = org(tetloop); + p[1] = dest(tetloop); + p[2] = apex(tetloop); + setshvertices(subloop, p[0], p[1], p[2]); + // Create the point-to-subface map. + sptr = sencode(subloop); + for (j = 0; j < 3; j++) { + setpointtype(p[j], FACETVERTEX); // initial type. + setpoint2sh(p[j], sptr); + } + setshellmark(subloop, 0); // Default marker. + // Insert the subface into the mesh. + tsbond(tetloop, subloop); + sesymself(subloop); + tsbond(checktet, subloop); + } // if (bondflag) + } // if (neighsh.sh == NULL) + } + tetloop.tet = tetrahedrontraverse(); + } + + // Connect subfaces together. + subfaces->traversalinit(); + subloop.shver = 0; + subloop.sh = shellfacetraverse(subfaces); + while (subloop.sh != (shellface *) NULL) { + for (i = 0; i < 3; i++) { + spivot(subloop, neighsh); + if (neighsh.sh == NULL) { + // Form a subface ring by linking all subfaces at this edge. + // Traversing all faces of the tets at this edge. + stpivot(subloop, tetloop); + q[2] = apex(tetloop); + neighsh = subloop; + while (1) { + fnextself(tetloop); + tspivot(tetloop, nextsh); + if (nextsh.sh != NULL) { + // Link neighsh <= nextsh. + sbond1(neighsh, nextsh); + neighsh = nextsh; + } + if (apex(tetloop) == q[2]) { + assert(nextsh.sh == subloop.sh); // It's a ring. + break; + } + } // while (1) + } // if (neighsh.sh == NULL) + senextself(subloop); } + subloop.sh = shellfacetraverse(subfaces); } - 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); + + // Segments will be introduced. + if (in->edgelist != NULL) { + // A .edge file is given. It may contain boundary edges. Insert them. + for (i = 0; i < in->numberofedges; i++) { + // Is it a segment? + if (in->edgemarkerlist != NULL) { + marker = in->edgemarkerlist[i]; + } else { + // Edge markers are not available. Assume all of them are segments. + marker = 1; } - } 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 (marker != 0) { + // Insert a segment. + idx = i * 2; + for (j = 0; j < 2; j++) { + p[j] = idx2verlist[in->edgelist[idx++]]; + } + // Make sure all vertices are in the mesh. Avoid crash. + for (j = 0; j < 2; j++) { + decode(point2tet(p[j]), checktet); + if (checktet.tet == NULL) break; + } + // Search the segment. + if ((j == 2) && getedge(p[0], p[1], &checktet)) { + // Create a new subface. + makeshellface(subsegs, &segloop); + setshvertices(segloop, p[0], p[1], NULL); + // Create the point-to-segment map. + sptr = sencode(segloop); + for (j = 0; j < 2; j++) { + setpointtype(p[j], RIDGEVERTEX); // initial type. + setpoint2sh(p[j], sptr); + } + if (in->edgemarkerlist != NULL) { + setshellmark(segloop, marker); + } + // Insert the segment into the mesh. + tetloop = checktet; + q[2] = apex(checktet); + subloop.sh = NULL; + while (1) { + tssbond1(tetloop, segloop); + tspivot(tetloop, subloop); + if (subloop.sh != NULL) { + ssbond1(subloop, segloop); + sbond1(segloop, subloop); + } + fnextself(tetloop); + if (apex(tetloop) == q[2]) break; + } // while (1) + // Remember an adjacent tet for this segment. + sstbond1(segloop, tetloop); + } else { + if (!b->quiet) { + printf("Warning: Segment #%d [%d,%d] is missing.\n", + i + in->firstnumber, pointmark(p[0]), pointmark(p[1])); + } + } + } // if (marker != 0) + } // i + } // if (in->edgelist) + + // Identify segments from the mesh. + // Create segments for non-manifold edges (which are shared by more + // than two subfaces), and for non-coplanar edges, i.e., two subfaces + // form an dihedral angle > 'b->facet_ang_tol' (degree). + angtol = b->facet_ang_tol / 180.0 * PI; + subfaces->traversalinit(); + subloop.shver = 0; + subloop.sh = shellfacetraverse(subfaces); + while (subloop.sh != (shellface *) NULL) { + for (i = 0; i < 3; i++) { + sspivot(subloop, segloop); + if (segloop.sh == NULL) { + // Check if this edge is a segment. + bondflag = 0; + // Counter the number of subfaces at this edge. + idx = 0; + nextsh = subloop; + while (1) { + idx++; + spivotself(nextsh); + if (nextsh.sh == subloop.sh) break; + } + if (idx != 2) { + // It's a non-manifold edge. Insert a segment. + p[0] = sorg(subloop); + p[1] = sdest(subloop); + bondflag = 1; + } else { + spivot(subloop, neighsh); + if (shellmark(subloop) != shellmark(neighsh)) { + // It's an interior interface. Insert a segment. + p[0] = sorg(subloop); + p[1] = sdest(subloop); + bondflag = 1; + } else { + if (!b->convex) { + // Check the dihedral angle formed by the two subfaces. + p[0] = sorg(subloop); + p[1] = sdest(subloop); + p[2] = sapex(subloop); + p[3] = sapex(neighsh); + ang = facedihedral(p[0], p[1], p[2], p[3]); + if (ang > PI) ang = 2 * PI - ang; + if (ang < angtol) { + bondflag = 1; + } + } + } + } + if (bondflag) { + // Create a new segment. + makeshellface(subsegs, &segloop); + setshvertices(segloop, p[0], p[1], NULL); + // Create the point-to-segment map. + sptr = sencode(segloop); + for (j = 0; j < 2; j++) { + setpointtype(p[j], RIDGEVERTEX); // initial type. + setpoint2sh(p[j], sptr); + } + setshellmark(segloop, 0); // Initially has no marker. + // Insert the subface into the mesh. + stpivot(subloop, tetloop); + q[2] = apex(tetloop); + while (1) { + tssbond1(tetloop, segloop); + tspivot(tetloop, neighsh); + if (neighsh.sh != NULL) { + ssbond1(neighsh, segloop); + } + fnextself(tetloop); + if (apex(tetloop) == q[2]) break; + } // while (1) + // Remember an adjacent tet for this segment. + sstbond1(segloop, tetloop); + sbond1(segloop, subloop); + } // if (bondflag) + } // if (neighsh.sh == NULL) + senextself(subloop); + } // i + subloop.sh = shellfacetraverse(subfaces); + } + + // Remember the number of input segments. + insegments = subsegs->items; + + if (!b->nobisect || checkconstraints) { + // Mark Steiner points on segments and facets. + // - all vertices which remaining type FEACTVERTEX become + // Steiner points in facets (= FREEFACERVERTEX). + // - vertices on segment need to be checked. + face* segperverlist; + int* idx2seglist; + face parentseg, nextseg; + verttype vt; + REAL area, len, l1, l2; + int fmarker; + + makepoint2submap(subsegs, idx2seglist, segperverlist); + + points->traversalinit(); + point ptloop = pointtraverse(); + while (ptloop != NULL) { + vt = pointtype(ptloop); + if (vt == VOLVERTEX) { + setpointtype(ptloop, FREEVOLVERTEX); + st_volref_count++; + } else if (vt == FACETVERTEX) { + setpointtype(ptloop, FREEFACETVERTEX); + st_facref_count++; + } else if (vt == RIDGEVERTEX) { + idx = pointmark(ptloop) - in->firstnumber; + if ((idx2seglist[idx + 1] - idx2seglist[idx]) == 2) { + i = idx2seglist[idx]; + parentseg = segperverlist[i]; + nextseg = segperverlist[i + 1]; + sesymself(nextseg); + p[0] = sorg(nextseg); + p[1] = sdest(parentseg); + // Check if three points p[0], ptloop, p[2] are (nearly) collinear. + len = distance(p[0], p[1]); + l1 = distance(p[0], ptloop); + l2 = distance(ptloop, p[1]); + if (((l1 + l2 - len) / len) < b->epsilon) { + // They are (nearly) collinear. + setpointtype(ptloop, FREESEGVERTEX); + // Connect nextseg and parentseg together at ptloop. + senextself(nextseg); + senext2self(parentseg); + sbond(nextseg, parentseg); + st_segref_count++; + } + } + } + ptloop = pointtraverse(); + } + + // Are there area constraints? + if (b->quality && (in->facetconstraintlist != (REAL *) NULL)) { + // Set maximum area constraints on facets. + for (i = 0; i < in->numberoffacetconstraints; i++) { + fmarker = (int) in->facetconstraintlist[i * 2]; + area = in->facetconstraintlist[i * 2 + 1]; + subfaces->traversalinit(); + subloop.sh = shellfacetraverse(subfaces); + while (subloop.sh != NULL) { + if (shellmark(subloop) == fmarker) { + setareabound(subloop, area); + } + subloop.sh = shellfacetraverse(subfaces); + } } } + + // Are there length constraints? + if (b->quality && (in->segmentconstraintlist != (REAL *) NULL)) { + // Set maximum length constraints on segments. + int e1, e2; + for (i = 0; i < in->numberofsegmentconstraints; i++) { + e1 = (int) in->segmentconstraintlist[i * 3]; + e2 = (int) in->segmentconstraintlist[i * 3 + 1]; + len = in->segmentconstraintlist[i * 3 + 2]; + // Search for edge [e1, e2]. + idx = e1 - in->firstnumber; + for (j = idx2seglist[idx]; j < idx2seglist[idx + 1]; j++) { + parentseg = segperverlist[j]; + if (pointmark(sdest(parentseg)) == e2) { + setareabound(parentseg, len); + break; + } + } + } + } + + delete [] idx2seglist; + delete [] segperverlist; } - - return enq; + + + // Set global flags. + checksubsegflag = 1; + checksubfaceflag = 1; + + delete [] idx2verlist; + delete [] ver2tetarray; } /////////////////////////////////////////////////////////////////////////////// // // -// checksub4encroach() Check a subface to see if it is encroached. // +// scoutpoint() Search a point in mesh. // // // -// A subface f is encroached if there is a vertex inside or on its diametral // -// circumsphere. // +// This function searches the point in a mesh whose domain may be not convex.// +// In case of a convex domain, the locate() function is sufficient. // // // -// If 'testpt (p) != NULL', test if 'testsub' (f) is encroached by it, else, // -// test if f is encroached by one of the two opposites of the adjacent tets. // -// Return TRUE if f is encroached and queue it if 'enqflag' is set. // +// If 'randflag' is used, randomly select a start searching tet. Otherwise, // +// start searching directly from 'searchtet'. // // // /////////////////////////////////////////////////////////////////////////////// -bool tetgenmesh::checksub4encroach(face* testsub, point testpt, bool enqflag) +int tetgenmesh::scoutpoint(point searchpt, triface *searchtet, int randflag) { - triface abuttet; - point pa, pb, pc, encpt; - REAL A[4][4], rhs[4], D; - REAL cent[3], area; - REAL radius, dist, diff; - bool enq; - int indx[4]; - int quenumber; - - enq = false; - radius = 0.0; - encpt = (point) NULL; + point pa, pb, pc, pd; + enum locateresult loc = OUTSIDE; + REAL vol, ori1, ori2 = 0, ori3 = 0, ori4 = 0; + int t1ver; - pa = sorg(*testsub); - pb = sdest(*testsub); - pc = sapex(*testsub); - - // 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]; // vector V1 (pa->pb) - A[1][0] = pc[0] - pa[0]; - A[1][1] = pc[1] - pa[1]; - A[1][2] = pc[2] - pa[2]; // vector V2 (pa->pc) - cross(A[0], A[1], A[2]); // vector V3 (V1 X V2) - if (varconstraint && (areabound(*testsub) > 0.0)) { - // Check if the subface has too big area. - area = 0.5 * sqrt(dot(A[2], A[2])); - enq = area > areabound(*testsub); - if (enq) { - quenumber = 2; // A queue of subfaces having too big area. + // Randomly select a good starting tet. + if (randflag) { + randomsample(searchpt, searchtet); + } else { + if (searchtet->tet == NULL) { + *searchtet = recenttet; } } + loc = locate(searchpt, searchtet); - // 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]); - rhs[2] = 0.0; - // Solve the 3 by 3 equations use LU decomposition with partial pivoting - // and backward and forward substitute.. - if (lu_decmp(A, 3, indx, &D, 0)) { - lu_solve(A, 3, indx, rhs, 0); - cent[0] = pa[0] + rhs[0]; - cent[1] = pa[1] + rhs[1]; - cent[2] = pa[2] + rhs[2]; - radius = sqrt(rhs[0] * rhs[0] + rhs[1] * rhs[1] + rhs[2] * rhs[2]); - } - - if (!enq) { - // Check if the subface is encroached. - if (testpt == (point) NULL) { - stpivot(*testsub, abuttet); - if (abuttet.tet != dummytet) { - dist = distance(cent, oppo(abuttet)); - diff = dist - radius; - if (fabs(diff) / radius <= b->epsilon) diff = 0.0; // Rounding. - enq = (diff <= 0.0); - if (enq) encpt = oppo(abuttet); - } - if (!enq) { - sesymself(*testsub); - stpivot(*testsub, abuttet); - if (abuttet.tet != dummytet) { - dist = distance(cent, oppo(abuttet)); - diff = dist - radius; - if (fabs(diff) / radius <= b->epsilon) diff = 0.0; // Rounding. - enq = (diff <= 0.0); - if (enq) encpt = oppo(abuttet); - } - } - } else { - dist = distance(cent, testpt); - diff = dist - radius; - if (fabs(diff) / radius <= b->epsilon) diff = 0.0; // Rounding. - enq = (diff <= 0.0); + if (loc == OUTSIDE) { + if (b->convex) { // -c option + // The point lies outside of the convex hull. + return (int) loc; } - if (enq) { - quenumber = 0; // A queue of encroached subfaces. + // Test if it lies nearly on the hull face. + // Reuse vol, ori1. + pa = org(*searchtet); + pb = dest(*searchtet); + pc = apex(*searchtet); + vol = triarea(pa, pb, pc); + ori1 = orient3dfast(pa, pb, pc, searchpt); + if (fabs(ori1 / vol) < b->epsilon) { + loc = ONFACE; // On face (or on edge, or on vertex). + fsymself(*searchtet); } } - if (enq && enqflag) { - enqueueencsub(testsub, encpt, quenumber, cent); + if (loc != OUTSIDE) { + // Round the result of location. + pa = org(*searchtet); + pb = dest(*searchtet); + pc = apex(*searchtet); + pd = oppo(*searchtet); + vol = orient3dfast(pa, pb, pc, pd); + ori1 = orient3dfast(pa, pb, pc, searchpt); + ori2 = orient3dfast(pb, pa, pd, searchpt); + ori3 = orient3dfast(pc, pb, pd, searchpt); + ori4 = orient3dfast(pa, pc, pd, searchpt); + if (fabs(ori1 / vol) < b->epsilon) ori1 = 0; + if (fabs(ori2 / vol) < b->epsilon) ori2 = 0; + if (fabs(ori3 / vol) < b->epsilon) ori3 = 0; + if (fabs(ori4 / vol) < b->epsilon) ori4 = 0; + } else { // if (loc == OUTSIDE) { + // Do a brute force search for the point (with rounding). + tetrahedrons->traversalinit(); + searchtet->tet = tetrahedrontraverse(); + while (searchtet->tet != NULL) { + pa = org(*searchtet); + pb = dest(*searchtet); + pc = apex(*searchtet); + pd = oppo(*searchtet); + + vol = orient3dfast(pa, pb, pc, pd); + if (vol < 0) { + ori1 = orient3dfast(pa, pb, pc, searchpt); + if (fabs(ori1 / vol) < b->epsilon) ori1 = 0; // Rounding. + if (ori1 <= 0) { + ori2 = orient3dfast(pb, pa, pd, searchpt); + if (fabs(ori2 / vol) < b->epsilon) ori2 = 0; + if (ori2 <= 0) { + ori3 = orient3dfast(pc, pb, pd, searchpt); + if (fabs(ori3 / vol) < b->epsilon) ori3 = 0; + if (ori3 <= 0) { + ori4 = orient3dfast(pa, pc, pd, searchpt); + if (fabs(ori4 / vol) < b->epsilon) ori4 = 0; + if (ori4 <= 0) { + // Found the tet. Return its location. + break; + } // ori4 + } // ori3 + } // ori2 + } // ori1 + } + + searchtet->tet = tetrahedrontraverse(); + } // while (searchtet->tet != NULL) + nonregularcount++; // Re-use this counter. + } + + if (searchtet->tet != NULL) { + // Return the point location. + if (ori1 == 0) { // on face [a,b,c] + if (ori2 == 0) { // on edge [a,b]. + if (ori3 == 0) { // on vertex [b]. + assert(ori4 != 0); + enextself(*searchtet); // [b,c,a,d] + loc = ONVERTEX; + } else { + if (ori4 == 0) { // on vertex [a] + loc = ONVERTEX; // [a,b,c,d] + } else { + loc = ONEDGE; // [a,b,c,d] + } + } + } else { // ori2 != 0 + if (ori3 == 0) { // on edge [b,c] + if (ori4 == 0) { // on vertex [c] + eprevself(*searchtet); // [c,a,b,d] + loc = ONVERTEX; + } else { + enextself(*searchtet); // [b,c,a,d] + loc = ONEDGE; + } + } else { // ori3 != 0 + if (ori4 == 0) { // on edge [c,a] + eprevself(*searchtet); // [c,a,b,d] + loc = ONEDGE; + } else { + loc = ONFACE; + } + } + } + } else { // ori1 != 0 + if (ori2 == 0) { // on face [b,a,d] + esymself(*searchtet); // [b,a,d,c] + if (ori3 == 0) { // on edge [b,d] + eprevself(*searchtet); // [d,b,a,c] + if (ori4 == 0) { // on vertex [d] + loc = ONVERTEX; + } else { + loc = ONEDGE; + } + } else { // ori3 != 0 + if (ori4 == 0) { // on edge [a,d] + enextself(*searchtet); // [a,d,b,c] + loc = ONEDGE; + } else { + loc = ONFACE; + } + } + } else { // ori2 != 0 + if (ori3 == 0) { // on face [c,b,d] + enextself(*searchtet); + esymself(*searchtet); + if (ori4 == 0) { // on edge [c,d] + eprevself(*searchtet); + loc = ONEDGE; + } else { + loc = ONFACE; + } + } else { + if (ori4 == 0) { // on face [a,c,d] + eprevself(*searchtet); + esymself(*searchtet); + loc = ONFACE; + } else { // inside tet [a,b,c,d] + loc = INTETRAHEDRON; + } // ori4 + } // ori3 + } // ori2 + } // ori1 + } else { + loc = OUTSIDE; } - return enq; + return (int) loc; } /////////////////////////////////////////////////////////////////////////////// // // -// checktet4badqual() Test a tetrahedron for quality measures. // +// getpointmeshsize() Interpolate the mesh size at given point. // // // -// Tests a tetrahedron to see if it satisfies the minimum ratio condition // -// and the maximum volume condition. Tetrahedra that aren't upto spec are // -// added to the bad tetrahedron queue. // +// 'iloc' indicates the location of the point w.r.t. 'searchtet'. The size // +// is obtained by linear interpolation on the vertices of the tet. // // // /////////////////////////////////////////////////////////////////////////////// -bool tetgenmesh::checktet4badqual(triface* testtet, bool enqflag) +REAL tetgenmesh::getpointmeshsize(point searchpt, triface *searchtet, int iloc) { - point pa, pb, pc, pd, pe1, pe2; - REAL vda[3], vdb[3], vdc[3]; - REAL vab[3], vbc[3], vca[3]; - REAL N[4][3], A[4][4], rhs[4], D; - REAL elen[6], circumcent[3]; - REAL bicent[3], offcent[3]; - REAL volume, L, cosd; - REAL radius2, smlen2, ratio2; - REAL dist, sdist, split; - bool enq; - int indx[4]; - int sidx, i, j; - - pa = (point) testtet->tet[4]; - pb = (point) testtet->tet[5]; - pc = (point) testtet->tet[6]; - pd = (point) testtet->tet[7]; + point *pts, pa, pb, pc; + REAL volume, vol[4], wei[4]; + REAL size; + int i; - // Get the edge vectors vda: d->a, vdb: d->b, vdc: d->c. - // Set the matrix A = [vda, vdb, vdc]^T. - for (i = 0; i < 3; i++) A[0][i] = vda[i] = pa[i] - pd[i]; - 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]; - // Get the rest edge vectors - for (i = 0; i < 3; i++) vab[i] = pb[i] - pa[i]; - for (i = 0; i < 3; i++) vbc[i] = pc[i] - pb[i]; - for (i = 0; i < 3; i++) vca[i] = pa[i] - pc[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; - 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); - // Find the square of the shortest edge length. - elen[0] = dot(vda, vda); - elen[1] = dot(vdb, vdb); - elen[2] = dot(vdc, vdc); - elen[3] = dot(vab, vab); - elen[4] = dot(vbc, vbc); - elen[5] = dot(vca, vca); - smlen2 = elen[0]; sidx = 0; - for (i = 1; i < 6; i++) { - if (smlen2 > elen[i]) { smlen2 = elen[i]; sidx = i; } - } - // Calculate the square of radius-edge ratio. - ratio2 = radius2 / smlen2; - // Check whether the ratio is smaller than permitted. - enq = ratio2 > b->goodratio; - if (!enq) { - // abcd has good ratio. - // ratio2 = 0.0; - // if (b->offcenter) { - // Test if it is a sliver. - // 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. + size = 0; + + if (iloc == (int) INTETRAHEDRON) { + pts = (point *) &(searchtet->tet[4]); + assert(pts[3] != dummypoint); + // Only do interpolation if all vertices have non-zero sizes. + if ((pts[0][pointmtrindex] > 0) && (pts[1][pointmtrindex] > 0) && + (pts[2][pointmtrindex] > 0) && (pts[3][pointmtrindex] > 0)) { + // P1 interpolation. + volume = orient3dfast(pts[0], pts[1], pts[2], pts[3]); + vol[0] = orient3dfast(searchpt, pts[1], pts[2], pts[3]); + vol[1] = orient3dfast(pts[0], searchpt, pts[2], pts[3]); + vol[2] = orient3dfast(pts[0], pts[1], searchpt, pts[3]); + vol[3] = orient3dfast(pts[0], pts[1], pts[2], searchpt); for (i = 0; i < 4; i++) { - L = sqrt(dot(N[i], N[i])); - if (L > 0.0) { - for (j = 0; j < 3; j++) N[i][j] /= L; - } + wei[i] = fabs(vol[i] / volume); + size += (wei[i] * pts[i][pointmtrindex]); } - // N[0] is the normal of face bcd. Test the dihedral angles at edge - // cd, bd, and bc to see if they are too small or too big. - for (i = 1; i < 4 && !enq; i++) { - cosd = -dot(N[0], N[i]); // Edge cd, bd, bc. - enq = cosd > cosmindihed; - } - if (!enq) { - for (i = 2; i < 4 && !enq; i++) { - cosd = -dot(N[1], N[i]); // Edge ad, ac - enq = cosd > cosmindihed; - } - if (!enq) { - cosd = -dot(N[2], N[3]); // Edge ab - enq = cosd > cosmindihed; - } - } - // } - } else if (b->offcenter) { - // abcd has bad-quality. Use off-center instead of circumcenter. - switch (sidx) { - case 0: // edge da. - pe1 = pd; pe2 = pa; break; - case 1: // edge db. - pe1 = pd; pe2 = pb; break; - case 2: // edge dc. - pe1 = pd; pe2 = pc; break; - case 3: // edge ab. - pe1 = pa; pe2 = pb; break; - case 4: // edge bc. - pe1 = pb; pe2 = pc; break; - case 5: // edge ca. - pe1 = pc; pe2 = pa; break; - default: - pe1 = pe2 = (point) NULL; // Avoid a compile warning. - } - // The shortest edge is e1->e2. - for (i = 0; i < 3; i++) bicent[i] = 0.5 * (pe1[i] + pe2[i]); - dist = distance(bicent, circumcent); - // sdist = sqrt(smlen2) * sin(PI / 3.0); // A icoso-triangle. - // The following formulae is from - sdist = b->alpha3 * (b->minratio+sqrt(b->goodratio-0.25))* sqrt(smlen2); - split = sdist / dist; - if (split > 1.0) split = 1.0; - // Get the off-center. - for (i = 0; i < 3; i++) { - offcent[i] = bicent[i] + split * (circumcent[i] - bicent[i]); - } - } - - if (!enq && (b->varvolume || b->fixedvolume)) { - // Check if the tet has too big volume. - enq = b->fixedvolume && (volume > b->maxvolume); - if (!enq && b->varvolume) { - enq = (volume > volumebound(testtet->tet)) && - (volumebound(testtet->tet) > 0.0); } - } - - 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; - } - } - // *** Experiment ! - // enq = (i == 4); // Does c lies outside all sparse-ball? - } // if (b->metric) - } - - if (enq && enqflag) { - if (b->offcenter && (ratio2 > b->goodratio)) { - for (i = 0; i < 3; i++) circumcent[i] = offcent[i]; + } else if (iloc == (int) ONFACE) { + pa = org(*searchtet); + pb = dest(*searchtet); + pc = apex(*searchtet); + if ((pa[pointmtrindex] > 0) && (pb[pointmtrindex] > 0) && + (pc[pointmtrindex] > 0)) { + volume = triarea(pa, pb, pc); + vol[0] = triarea(searchpt, pb, pc); + vol[1] = triarea(pa, searchpt, pc); + vol[2] = triarea(pa, pb, searchpt); + size = (vol[0] / volume) * pa[pointmtrindex] + + (vol[1] / volume) * pb[pointmtrindex] + + (vol[2] / volume) * pc[pointmtrindex]; + } + } else if (iloc == (int) ONEDGE) { + pa = org(*searchtet); + pb = dest(*searchtet); + if ((pa[pointmtrindex] > 0) && (pb[pointmtrindex] > 0)) { + volume = distance(pa, pb); + vol[0] = distance(searchpt, pb); + vol[1] = distance(pa, searchpt); + size = (vol[0] / volume) * pa[pointmtrindex] + + (vol[1] / volume) * pb[pointmtrindex]; + } + } else if (iloc == (int) ONVERTEX) { + pa = org(*searchtet); + if (pa[pointmtrindex] > 0) { + size = pa[pointmtrindex]; } - enqueuebadtet(testtet, ratio2, circumcent); } - return enq; + return size; } /////////////////////////////////////////////////////////////////////////////// // // -// acceptsegpt() Check if a segment point can be inserted or not. // -// // -// Segment(ab) is indicated to be split by a point p (\in ab). This routine // -// decides whether p can be inserted or not. // -// // -// p can not be inserted either the '-Y' option is used and ab is a hull // -// segment or '-YY' option is used. // -// // -// p can be inserted if it is in one of the following cases: // -// (1) if L = |a - b| is too long wrt the edge constraint; or // -// (2) if |x - p| > \alpha_2 H(x) for x = a, b; or // -// (3) if 'refpt' != NULL. // +// interpolatemeshsize() Interpolate the mesh size from a background mesh // +// (source) to the current mesh (destination). // // // /////////////////////////////////////////////////////////////////////////////// -bool tetgenmesh::acceptsegpt(point segpt, point refpt, face* splitseg) +void tetgenmesh::interpolatemeshsize() { - point p[2]; - REAL L, lfs; - int i, j; + triface searchtet; + point ploop; + REAL minval = 0.0, maxval = 0.0; + int iloc; + int count; - // This segment must have not been checked (and rejected) yet. - assert(!smarktested(*splitseg)); + if (!b->quiet) { + printf("Interpolating mesh size ...\n"); + } - if (b->nobisect == 1) { - // '-Y'. It can not be split if it is on the hull. - triface spintet; - point pc; - sstpivot(splitseg, &spintet); - assert(spintet.tet != dummytet); - pc = apex(spintet); - do { - if (!fnextself(spintet)) { - // Meet a boundary face - s is on the hull. - return false; + long bak_nonregularcount = nonregularcount; + nonregularcount = 0l; // Count the number of (slow) global searches. + long baksmaples = bgm->samples; + bgm->samples = 3l; + count = 0; // Count the number of interpolated points. + + // Interpolate sizes for all points in the current mesh. + points->traversalinit(); + ploop = pointtraverse(); + while (ploop != NULL) { + // Search a tet in bgm which containing this point. + searchtet.tet = NULL; + iloc = bgm->scoutpoint(ploop, &searchtet, 1); // randflag = 1 + if (iloc != (int) OUTSIDE) { + // Interpolate the mesh size. + ploop[pointmtrindex] = bgm->getpointmeshsize(ploop, &searchtet, iloc); + setpoint2bgmtet(ploop, bgm->encode(searchtet)); + if (count == 0) { + // This is the first interpolated point. + minval = maxval = ploop[pointmtrindex]; + } else { + if (ploop[pointmtrindex] < minval) { + minval = ploop[pointmtrindex]; + } + if (ploop[pointmtrindex] > maxval) { + maxval = ploop[pointmtrindex]; + } + } + count++; + } else { + if (!b->quiet) { + printf("Warnning: Failed to locate point %d in source mesh.\n", + pointmark(ploop)); } - } while (pc != apex(spintet)); - } else if (b->nobisect > 1) { - // '-YY'. Do not split it. - return false; - } - - p[0] = sorg(*splitseg); - p[1] = sdest(*splitseg); - if (varconstraint && (areabound(*splitseg) > 0)) { - lfs = areabound(*splitseg); - L = distance(p[0], p[1]); - if (L > lfs) { - return true; // case (1) } + ploop = pointtraverse(); } - j = 0; // Use j to count the number of inside balls. - for (i = 0; i < 2; i++) { - // Check if p is inside the protect ball of q. - if (p[i][pointmtrindex] > 0.0) { - lfs = b->alpha2 * p[i][pointmtrindex]; - L = distance(p[i], segpt); - if (L < lfs) j++; // p is inside ball. + if (b->verbose) { + printf(" Interoplated %d points.\n", count); + if (nonregularcount > 0l) { + printf(" Performed %ld brute-force searches.\n", nonregularcount); } - } - if (j == 0) return true; // case (3). - - // If 'refpt' != NULL, force p to be inserted. - if (refpt != (point) NULL) { - cdtenforcesegpts++; - return true; + printf(" Size rangle [%.17g, %.17g].\n", minval, maxval); } - // Do not split it. - rejsegpts++; - return false; + bgm->samples = baksmaples; + nonregularcount = bak_nonregularcount; } /////////////////////////////////////////////////////////////////////////////// // // -// acceptfacpt() Check if a facet point can be inserted or not. // +// insertconstrainedpoints() Insert a list of points into the mesh. // // // -// 'subceillist' is CBC(p). 'verlist' (V) is empty on input, it returns the // -// set of vertices of CBC(p). // -// // -// p can not be inserted either the '-Y' option is used and the facet is on // -// the hull or '-YY' option is used. // -// // -// p can be inserted if |p - v| > \alpha_2 H(v), for all v \in V. // +// Assumption: The bounding box of the insert point set should be no larger // +// than the bounding box of the mesh. (Required by point sorting). // // // /////////////////////////////////////////////////////////////////////////////// -bool tetgenmesh::acceptfacpt(point facpt, list* subceillist, list* verlist) +void tetgenmesh::insertconstrainedpoints(point *insertarray, int arylen, + int rejflag) { - face *testsh; - point p[2], ploop; - REAL L, lfs; - int idx, i, j; + triface searchtet, spintet; + face splitsh; + face splitseg; + insertvertexflags ivf; + flipconstraints fc; + int randflag = 0; + int t1ver; + int i; - if (b->nobisect == 1) { - // '-Y'. p can not be inserted if CBC(p) is on the hull. - triface testtet; - testsh = (face *)(* subceillist)[0]; - stpivot(*testsh, testtet); - if (testtet.tet != dummytet) { - sesymself(*testsh); - stpivot(*testsh, testtet); - } - if (testtet.tet == dummytet) return false; - } else if (b->nobisect > 1) { - // '-YY'. Do not split s. - return false; + if (b->verbose) { + printf(" Inserting %d constrained points\n", arylen); } - // Collect the vertices of CBC(p), save them in V. - for (i = 0; i < subceillist->len(); i++) { - testsh = (face *)(* subceillist)[i]; - p[0] = sorg(*testsh); - p[1] = sdest(*testsh); - for (j = 0; j < 2; j++) { - idx = pointmark(p[j]); - if (idx >= 0) { - setpointmark(p[j], -idx - 1); - verlist->append(&(p[j])); + if (b->no_sort) { // -b/1 option. + if (b->verbose) { + printf(" Using the input order.\n"); + } + } else { + if (b->verbose) { + printf(" Permuting vertices.\n"); + } + point swappoint; + int randindex; + srand(arylen); + for (i = 0; i < arylen; i++) { + randindex = rand() % (i + 1); + swappoint = insertarray[i]; + insertarray[i] = insertarray[randindex]; + insertarray[randindex] = swappoint; + } + if (b->brio_hilbert) { // -b1 option + if (b->verbose) { + printf(" Sorting vertices.\n"); + } + hilbert_init(in->mesh_dim); + int ngroup = 0; + brio_multiscale_sort(insertarray, arylen, b->brio_threshold, + b->brio_ratio, &ngroup); + } else { // -b0 option. + randflag = 1; + } // if (!b->brio_hilbert) + } // if (!b->no_sort) + + long bak_nonregularcount = nonregularcount; + nonregularcount = 0l; + long baksmaples = samples; + samples = 3l; // Use at least 3 samples. Updated in randomsample(). + + long bak_seg_count = st_segref_count; + long bak_fac_count = st_facref_count; + long bak_vol_count = st_volref_count; + + // Initialize the insertion parameters. + if (b->incrflip) { // -l option + // Use incremental flip algorithm. + ivf.bowywat = 0; + ivf.lawson = 1; + ivf.validflag = 0; // No need to validate the cavity. + fc.enqflag = 2; + } else { + // Use Bowyer-Watson algorithm. + ivf.bowywat = 1; + ivf.lawson = 0; + ivf.validflag = 1; // Validate the B-W cavity. + } + ivf.rejflag = rejflag; + ivf.chkencflag = 0; + ivf.sloc = (int) INSTAR; + ivf.sbowywat = 3; + ivf.splitbdflag = 1; + ivf.respectbdflag = 1; + ivf.assignmeshsize = b->metric; + + encseglist = new arraypool(sizeof(face), 8); + encshlist = new arraypool(sizeof(badface), 8); + + // Insert the points. + for (i = 0; i < arylen; i++) { + // Find the location of the inserted point. + // Do not use 'recenttet', since the mesh may be non-convex. + searchtet.tet = NULL; + ivf.iloc = scoutpoint(insertarray[i], &searchtet, randflag); + + // Decide the right type for this point. + setpointtype(insertarray[i], FREEVOLVERTEX); // Default. + splitsh.sh = NULL; + splitseg.sh = NULL; + if (ivf.iloc == (int) ONEDGE) { + if (issubseg(searchtet)) { + tsspivot1(searchtet, splitseg); + setpointtype(insertarray[i], FREESEGVERTEX); + //ivf.rejflag = 0; + } else { + // Check if it is a subface edge. + spintet = searchtet; + while (1) { + if (issubface(spintet)) { + tspivot(spintet, splitsh); + setpointtype(insertarray[i], FREEFACETVERTEX); + //ivf.rejflag |= 1; + break; + } + fnextself(spintet); + if (spintet.tet == searchtet.tet) break; + } + } + } else if (ivf.iloc == (int) ONFACE) { + if (issubface(searchtet)) { + tspivot(searchtet, splitsh); + setpointtype(insertarray[i], FREEFACETVERTEX); + //ivf.rejflag |= 1; } } - } - j = 0; // Use j to count the number of inside balls. - for (i = 0; i < verlist->len(); i++) { - ploop = * (point *)(* verlist)[i]; - // Uninfect q. - idx = pointmark(ploop); - setpointmark(ploop, -(idx + 1)); - // Check if p is inside the protect ball of q. - if (ploop[pointmtrindex] > 0.0) { - lfs = b->alpha2 * ploop[pointmtrindex]; - L = distance(ploop, facpt); - if (L < lfs) j++; // p is inside ball. + // Now insert the point. + if (insertpoint(insertarray[i], &searchtet, &splitsh, &splitseg, &ivf)) { + if (flipstack != NULL) { + // There are queued faces. Use flips to recover Delaunayness. + lawsonflip3d(&fc); + // There may be unflippable edges. Ignore them. + unflipqueue->restart(); + } + // Update the Steiner counters. + if (pointtype(insertarray[i]) == FREESEGVERTEX) { + st_segref_count++; + } else if (pointtype(insertarray[i]) == FREEFACETVERTEX) { + st_facref_count++; + } else { + st_volref_count++; + } + } else { + // Point is not inserted. + //pointdealloc(insertarray[i]); + setpointtype(insertarray[i], UNUSEDVERTEX); + unuverts++; + encseglist->restart(); + encshlist->restart(); } - } - verlist->clear(); + } // i - if (j == 0) return true; // case (3). + delete encseglist; + delete encshlist; - rejsubpts++; - return false; -} + if (b->verbose) { + printf(" Inserted %ld (%ld, %ld, %ld) vertices.\n", + st_segref_count + st_facref_count + st_volref_count - + (bak_seg_count + bak_fac_count + bak_vol_count), + st_segref_count - bak_seg_count, st_facref_count - bak_fac_count, + st_volref_count - bak_vol_count); + if (nonregularcount > 0l) { + printf(" Performed %ld brute-force searches.\n", nonregularcount); + } + } -/////////////////////////////////////////////////////////////////////////////// -// // -// acceptvolpt() Check if a volume point can be inserted or not. // -// // -// 'ceillist' is B(p). 'verlist' (V) is empty on input, it returns the set // -// of vertices of B(p). // -// // -// p can be split if |p - v| > \alpha_2 H(v), for all v \in V. // -// // -/////////////////////////////////////////////////////////////////////////////// + nonregularcount = bak_nonregularcount; + samples = baksmaples; +} -bool tetgenmesh::acceptvolpt(point volpt, list* ceillist, list* verlist) +void tetgenmesh::insertconstrainedpoints(tetgenio *addio) { - triface* testtet; - point p[3], ploop; - REAL L, lfs; - int idx, i, j; + point *insertarray, newpt; + REAL x, y, z, w; + int index, attribindex, mtrindex; + int arylen, i, j; - // Collect the vertices of CBC(p), save them in V. - for (i = 0; i < ceillist->len(); i++) { - testtet = (triface *)(* ceillist)[i]; - p[0] = org(*testtet); - p[1] = dest(*testtet); - p[2] = apex(*testtet); - for (j = 0; j < 3; j++) { - idx = pointmark(p[j]); - if (idx >= 0) { - setpointmark(p[j], -idx - 1); - verlist->append(&(p[j])); - } - } + if (!b->quiet) { + printf("Inserting constrained points ...\n"); } - j = 0; // Use j to counte the number of inside balls. - for (i = 0; i < verlist->len(); i++) { - ploop = * (point *)(* verlist)[i]; - // Uninfect q. - idx = pointmark(ploop); - setpointmark(ploop, -(idx + 1)); - // Check if p is inside the protect ball of q. - if (ploop[pointmtrindex] > 0.0) { - lfs = b->alpha2 * ploop[pointmtrindex]; - L = distance(ploop, volpt); - if (L < lfs) j++; // p is inside the protect ball. + insertarray = new point[addio->numberofpoints]; + arylen = 0; + index = 0; + attribindex = 0; + mtrindex = 0; + + for (i = 0; i < addio->numberofpoints; i++) { + x = addio->pointlist[index++]; + y = addio->pointlist[index++]; + z = addio->pointlist[index++]; + // Test if this point lies inside the bounding box. + if ((x < xmin) || (x > xmax) || (y < ymin) || (y > ymax) || + (z < zmin) || (z > zmax)) { + if (b->verbose) { + printf("Warning: Point #%d lies outside the bounding box. Ignored\n", + i + in->firstnumber); + } + continue; + } + makepoint(&newpt, UNUSEDVERTEX); + newpt[0] = x; + newpt[1] = y; + newpt[2] = z; + // Read the point attributes. (Including point weights.) + for (j = 0; j < addio->numberofpointattributes; j++) { + newpt[3 + j] = addio->pointattributelist[attribindex++]; + } + // Read the point metric tensor. + for (j = 0; j < addio->numberofpointmtrs; j++) { + newpt[pointmtrindex + j] = addio->pointmtrlist[mtrindex++]; + } + if (b->weighted) { // -w option + if (addio->numberofpointattributes > 0) { + // The first point attribute is its weight. + w = newpt[3]; + } else { + // No given weight available. Default choose the maximum + // absolute value among its coordinates. + w = fabs(x); + if (w < fabs(y)) w = fabs(y); + if (w < fabs(z)) w = fabs(z); + } + if (b->weighted_param == 0) { + newpt[3] = x * x + y * y + z * z - w; // Weighted DT. + } else { // -w1 option + newpt[3] = w; // Regular tetrahedralization. + } } + insertarray[arylen] = newpt; + arylen++; + } // i + + // Insert the points. + int rejflag = 0; // Do not check encroachment. + if (b->metric) { // -m option. + rejflag |= 4; // Reject it if it lies in some protecting balls. } - verlist->clear(); - - if (j == 0) return true; // case (2). - rejtetpts++; - return false; + insertconstrainedpoints(insertarray, arylen, rejflag); + + delete [] insertarray; } /////////////////////////////////////////////////////////////////////////////// // // -// getsplitpoint() Get the inserting point in a segment. // +// meshcoarsening() Deleting (selected) vertices. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::getsplitpoint(point e1, point e2, point refpt, point newpt) +void tetgenmesh::collectremovepoints(arraypool *remptlist) { - point ei, ej; - REAL split, L, d1, d2; - bool acutea, acuteb; - int i; + point ptloop, *parypt; + verttype vt; - if (refpt != (point) NULL) { - // Use the CDT rules to split the segment. - acutea = (pointtype(e1) == ACUTEVERTEX); - acuteb = (pointtype(e2) == ACUTEVERTEX); - if (acutea ^ acuteb) { - // Only one endpoint is acute. Use rule-2 or rule-3. - ei = acutea ? e1 : e2; - ej = acutea ? e2 : e1; - L = distance(ei, ej); - // Apply rule-2. - d1 = distance(ei, refpt); - split = d1 / L; - for (i = 0; i < 3; i++) newpt[i] = ei[i] + split * (ej[i] - ei[i]); - // Check if rule-3 is needed. - d2 = distance(refpt, newpt); - if (d2 > (L - d1)) { - // Apply rule-3. - if ((d1 - d2) > (0.5 * d1)) { - split = (d1 - d2) / L; - } else { - split = 0.5 * d1 / L; - } - for (i = 0; i < 3; i++) newpt[i] = ei[i] + split * (ej[i] - ei[i]); - if (b->verbose > 1) { - printf(" Found by rule-3:"); + // If a mesh sizing function is given. Collect vertices whose mesh size + // is greater than its smallest edge length. + if (b->metric) { // -m option + REAL len, smlen; + int i; + points->traversalinit(); + ptloop = pointtraverse(); + while (ptloop != NULL) { + if (ptloop[pointmtrindex] > 0) { + // Get the smallest edge length at this vertex. + getvertexstar(1, ptloop, cavetetlist, cavetetvertlist, NULL); + parypt = (point *) fastlookup(cavetetvertlist, 0); + smlen = distance(ptloop, *parypt); + for (i = 1; i < cavetetvertlist->objects; i++) { + parypt = (point *) fastlookup(cavetetvertlist, i); + len = distance(ptloop, *parypt); + if (len < smlen) { + smlen = len; + } } - r3count++; - } else { - if (b->verbose > 1) { - printf(" Found by rule-2:"); + cavetetvertlist->restart(); + cavetetlist->restart(); + if (smlen < ptloop[pointmtrindex]) { + pinfect(ptloop); + remptlist->newindex((void **) &parypt); + *parypt = ptloop; } - r2count++; - } - if (b->verbose > 1) { - printf(" center %d, split = %.12g.\n", pointmark(ei), split); } - } 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]); + ptloop = pointtraverse(); + } + if (b->verbose > 1) { + printf(" Coarsen %ld oversized points.\n", remptlist->objects); } - } else { - // Split the segment at its midpoint. - for (i = 0; i < 3; i++) newpt[i] = 0.5 * (e1[i] + e2[i]); } -} -/////////////////////////////////////////////////////////////////////////////// -// // -// setnewpointsize() Set the size for a new point. // -// // -// The size of the new point p is interpolated either from a background mesh // -// (b->bgmesh) or from the two input endpoints. // -// // -/////////////////////////////////////////////////////////////////////////////// + // If 'in->pointmarkerlist' exists, Collect vertices with markers '-1'. + if (in->pointmarkerlist != NULL) { + long bak_count = remptlist->objects; + points->traversalinit(); + ptloop = pointtraverse(); + int index = 0; + while (ptloop != NULL) { + if (index < in->numberofpoints) { + if (in->pointmarkerlist[index] == -1) { + pinfect(ptloop); + remptlist->newindex((void **) &parypt); + *parypt = ptloop; + } + } else { + // Remaining are not input points. Stop here. + break; + } + index++; + ptloop = pointtraverse(); + } + if (b->verbose > 1) { + printf(" Coarsen %ld marked points.\n", remptlist->objects - bak_count); + } + } // if (in->pointmarkerlist != NULL) -void tetgenmesh::setnewpointsize(point newpt, point e1, point e2) -{ - if (b->metric) { - // Interpolate the point size in a background mesh. - triface bgmtet; - // Get a tet in background mesh for locating p. - decode(point2bgmtet(e1), bgmtet); - p1interpolatebgm(newpt, &bgmtet, NULL); - } else { - if (e2 != (point) NULL) { - // Interpolate the size between the two endpoints. - REAL split, l, d; - l = distance(e1, e2); - d = distance(e1, newpt); - split = d / l; -#ifdef SELF_CHECK - // Check if e1 and e2 are endpoints of a sharp segment. - assert(e1[pointmtrindex] > 0.0); - assert(e2[pointmtrindex] > 0.0); -#endif - newpt[pointmtrindex] = (1.0 - split) * e1[pointmtrindex] - + split * e2[pointmtrindex]; + if (b->coarsen_param > 0) { // -R1/# + // Remove a coarsen_percent number of interior points. + assert((b->coarsen_percent > 0) && (b->coarsen_percent <= 1.0)); + if (b->verbose > 1) { + printf(" Coarsen %g percent of interior points.\n", + b->coarsen_percent * 100.0); + } + arraypool *intptlist = new arraypool(sizeof(point *), 10); + // Count the total number of interior points. + points->traversalinit(); + ptloop = pointtraverse(); + while (ptloop != NULL) { + vt = pointtype(ptloop); + if ((vt == VOLVERTEX) || (vt == FREEVOLVERTEX) || + (vt == FREEFACETVERTEX) || (vt == FREESEGVERTEX)) { + intptlist->newindex((void **) &parypt); + *parypt = ptloop; + } + ptloop = pointtraverse(); + } + if (intptlist->objects > 0l) { + // Sort the list of points randomly. + point *parypt_i, swappt; + int randindex, i; + srand(intptlist->objects); + for (i = 0; i < intptlist->objects; i++) { + randindex = rand() % (i + 1); // randomnation(i + 1); + parypt_i = (point *) fastlookup(intptlist, i); + parypt = (point *) fastlookup(intptlist, randindex); + // Swap this two points. + swappt = *parypt_i; + *parypt_i = *parypt; + *parypt = swappt; + } + int remcount = (int) ((REAL) intptlist->objects * b->coarsen_percent); + // Return the first remcount points. + for (i = 0; i < remcount; i++) { + parypt_i = (point *) fastlookup(intptlist, i); + if (!pinfected(*parypt_i)) { + pinfected(*parypt_i); + remptlist->newindex((void **) &parypt); + *parypt = *parypt_i; + } + } } + delete intptlist; } -} -/////////////////////////////////////////////////////////////////////////////// -// // -// splitencseg() Split an enc-seg and recover the Delaunayness by flips. // -// // -/////////////////////////////////////////////////////////////////////////////// + // Unmark all collected vertices. + for (int i = 0; i < remptlist->objects; i++) { + parypt = (point *) fastlookup(remptlist, i); + puninfect(*parypt); + } +} -bool tetgenmesh::splitencseg(point newpt, face* splitseg, list* tetlist, - list* sublist, list* verlist, queue* flipque, bool chkencsub, bool chkbadtet, - bool optflag) +void tetgenmesh::meshcoarsening() { - list *mytetlist; - queue *myflipque; - triface starttet; - face startsh, spinsh, checksh; - int i; + arraypool *remptlist; - if (optflag) { - mytetlist = new list(sizeof(triface), NULL, 1024); - myflipque = new queue(sizeof(badface)); - tetlist = mytetlist; - flipque = myflipque; + if (!b->quiet) { + printf("Mesh coarsening ...\n"); } - // Use the base orientation (important in this routine). - 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; + // Collect the set of points to be removed + remptlist = new arraypool(sizeof(point *), 10); + collectremovepoints(remptlist); + + if (remptlist->objects == 0l) { + delete remptlist; + return; } - 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)) { - checkseg4encroach(splitseg, NULL, NULL, true); - //} - if (i == 1) break; // Two new segs have been checked. - senextself(*splitseg); - spivotself(*splitseg); -#ifdef SELF_CHECK - assert(splitseg->sh != (shellface *) NULL); -#endif - splitseg->shver = 0; - } - // Check the new subfaces to see if they're encroached (not by p). - if (chkencsub) { - spivot(*splitseg, startsh); - spinsh = startsh; - do { - sublist->append(&spinsh); - formstarpolygon(newpt, sublist, verlist); - for (i = 0; i < sublist->len(); i++) { - checksh = * (face *)(* sublist)[i]; - //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 (b->verbose) { + if (remptlist->objects > 0l) { + printf(" Removing %ld points...\n", remptlist->objects); } - } // if (!optflag) + } - // Collect the new tets connecting at p. - sstpivot(splitseg, &starttet); - tetlist->append(&starttet); - formstarpolyhedron(newpt, tetlist, verlist, true); + point *parypt, *plastpt; + long ms = remptlist->objects; + int nit = 0; + int bak_fliplinklevel = b->fliplinklevel; + b->fliplinklevel = -1; + autofliplinklevel = 1; // Init value. + int i; - if (!optflag) { - // Check if p encroaches adjacent segments. - tallencsegs(newpt, 1, &tetlist); - if (chkencsub) { - // Check if p encroaches adjacent subfaces. - tallencsubs(newpt, 1, &tetlist); + while (1) { + + if (b->verbose > 1) { + printf(" Removing points [%s level = %2d] #: %ld.\n", + (b->fliplinklevel > 0) ? "fixed" : "auto", + (b->fliplinklevel > 0) ? b->fliplinklevel : autofliplinklevel, + remptlist->objects); } - if (chkbadtet) { - // Check if there are new bad quality tets at p. - for (i = 0; i < tetlist->len(); i++) { - starttet = * (triface *)(* tetlist)[i]; - checktet4badqual(&starttet, true); + + // Remove the list of points. + for (i = 0; i < remptlist->objects; i++) { + parypt = (point *) fastlookup(remptlist, i); + assert(pointtype(*parypt) != UNUSEDVERTEX); + if (removevertexbyflips(*parypt)) { + // Move the last entry to the current place. + plastpt = (point *) fastlookup(remptlist, remptlist->objects - 1); + *parypt = *plastpt; + remptlist->objects--; + i--; } } - tetlist->clear(); - } else { - // Check if new tets are non-optimal. - for (i = 0; i < tetlist->len(); i++) { - starttet = * (triface *)(* tetlist)[i]; - checktet4opt(&starttet, true); + + if (remptlist->objects > 0l) { + if (b->fliplinklevel >= 0) { + break; // We have tried all levels. + } + if (remptlist->objects == ms) { + nit++; + if (nit >= 3) { + // Do the last round with unbounded flip link level. + b->fliplinklevel = 100000; + } + } else { + ms = remptlist->objects; + if (nit > 0) { + nit--; + } + } + autofliplinklevel+=b->fliplinklevelinc; + } else { + // All points are removed. + break; + } + } // while (1) + + if (remptlist->objects > 0l) { + if (b->verbose) { + printf(" %ld points are not removed !\n", remptlist->objects); } - delete mytetlist; - delete myflipque; } - return true; + b->fliplinklevel = bak_fliplinklevel; + delete remptlist; } +//// //// +//// //// +//// reconstruct_cxx ////////////////////////////////////////////////////////// + +//// refine_cxx /////////////////////////////////////////////////////////////// +//// //// +//// //// + /////////////////////////////////////////////////////////////////////////////// // // -// tallencsegs() Check for encroached segments and save them in list. // -// // -// If 'testpt' (p) != NULL, only check if segments are encroached by p, else,// -// check all the nearby mesh vertices. // +// makefacetverticesmap() Create a map from facet to its vertices. // // // -// If 'ceillists' (B_i(p)) != NULL, there are 'n' B_i(p)s, only check the // -// segments which are on B_i(p)s, else, check the entire list of segments // -// (in the pool 'this->subsegs'). // +// All facets will be indexed (starting from 0). The map is saved in two // +// global arrays: 'idx2facetlist' and 'facetverticeslist'. // // // /////////////////////////////////////////////////////////////////////////////// -bool tetgenmesh::tallencsegs(point testpt, int n, list **ceillists) +void tetgenmesh::makefacetverticesmap() { - list *ceillist; - triface ceiltet; - face checkseg; - int enccount; // long oldencnum; + arraypool *facetvertexlist, *vertlist, **paryvertlist; + face subloop, neighsh, *parysh, *parysh1; + point pa, *ppt, *parypt; + verttype vt; + int facetindex, totalvertices; int i, j, k; - - // Remember the current number of encroached segments. - // oldencnum = badsubsegs->items; - - // Count the number of encroached segments. - enccount = 0; - - if (ceillists != (list **) NULL) { - for (k = 0; k < n; k++) { - ceillist = ceillists[k]; - // Check the segments on B_i(p). - for (i = 0; i < ceillist->len(); i++) { - ceiltet = * (triface *)(* ceillist)[i]; - ceiltet.ver = 0; - for (j = 0; j < 3; j++) { - 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++; - } - // } - } - enextself(ceiltet); + + if (b->verbose) { + printf(" Creating the facet vertices map.\n"); + } + + facetvertexlist = new arraypool(sizeof(arraypool *), 10); + facetindex = totalvertices = 0; + + subfaces->traversalinit(); + subloop.sh = shellfacetraverse(subfaces); + while (subloop.sh != NULL) { + if (!sinfected(subloop)) { + // A new facet. Create its vertices list. + vertlist = new arraypool(sizeof(point *), 8); + ppt = (point *) &(subloop.sh[3]); + for (k = 0; k < 3; k++) { + vt = pointtype(ppt[k]); + if ((vt != FREESEGVERTEX) && (vt != FREEFACETVERTEX)) { + pinfect(ppt[k]); + vertlist->newindex((void **) &parypt); + *parypt = ppt[k]; } } - } - } else { - // Check the entire list of segments. - subsegs->traversalinit(); - 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++; + sinfect(subloop); + caveshlist->newindex((void **) &parysh); + *parysh = subloop; + for (i = 0; i < caveshlist->objects; i++) { + parysh = (face *) fastlookup(caveshlist, i); + setfacetindex(*parysh, facetindex); + for (j = 0; j < 3; j++) { + if (!isshsubseg(*parysh)) { + spivot(*parysh, neighsh); + assert(neighsh.sh != NULL); + if (!sinfected(neighsh)) { + pa = sapex(neighsh); + if (!pinfected(pa)) { + vt = pointtype(pa); + if ((vt != FREESEGVERTEX) && (vt != FREEFACETVERTEX)) { + pinfect(pa); + vertlist->newindex((void **) &parypt); + *parypt = pa; + } + } + sinfect(neighsh); + caveshlist->newindex((void **) &parysh1); + *parysh1 = neighsh; + } + } + senextself(*parysh); } - // } - checkseg.sh = shellfacetraverse(subsegs); - } + } // i + totalvertices += (int) vertlist->objects; + // Uninfect facet vertices. + for (k = 0; k < vertlist->objects; k++) { + parypt = (point *) fastlookup(vertlist, k); + puninfect(*parypt); + } + caveshlist->restart(); + // Save this vertex list. + facetvertexlist->newindex((void **) &paryvertlist); + *paryvertlist = vertlist; + facetindex++; + } + subloop.sh = shellfacetraverse(subfaces); } - // return (badsubsegs->items > oldencnum); - return enccount > 0; -} + // All subfaces are infected. Uninfect them. + subfaces->traversalinit(); + subloop.sh = shellfacetraverse(subfaces); + while (subloop.sh != NULL) { + assert(sinfected(subloop)); + suninfect(subloop); + subloop.sh = shellfacetraverse(subfaces); + } -/////////////////////////////////////////////////////////////////////////////// -// // -// tallencsubs() Find all encroached subfaces and save them in list. // -// // -// If 'testpt' (p) != NULL, only check if subfaces are encroached by p, else,// -// check the adjacent vertices of subfaces. // -// // -// If 'ceillists' (B_i(p)) != NULL, there are 'n' B_i(p)s, only check the // -// subfaces which are on B_i(p)s, else, check the entire list of subfaces // -// (in the pool 'this->subfaces'). // -// // -/////////////////////////////////////////////////////////////////////////////// + if (b->verbose) { + printf(" Found %ld facets.\n", facetvertexlist->objects); + } -bool tetgenmesh::tallencsubs(point testpt, int n, list** ceillists) -{ - list *ceillist; - triface ceiltet; - face checksh; - int enccount; //long oldencnum; - int i, k; - - // Remember the current number of encroached segments. - // oldencnum = badsubfaces->items; - - enccount = 0; // Count the number encroached subfaces. - - if (ceillists != (list **) NULL) { - for (k = 0; k < n; k++) { - ceillist = ceillists[k]; - // Check the subfaces on B_i(p). - for (i = 0; i < ceillist->len(); i++) { - ceiltet = * (triface *)(* ceillist)[i]; - 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++; - } - //} - } - } - } - } else { - // Check the entire list of subfaces. - subfaces->traversalinit(); - 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++; - } - // } - checksh.sh = shellfacetraverse(subfaces); + idx2facetlist = new int[facetindex + 1]; + facetverticeslist = new point[totalvertices]; + + totalworkmemory += ((facetindex + 1) * sizeof(int) + + totalvertices * sizeof(point *)); + + idx2facetlist[0] = 0; + for (i = 0, k = 0; i < facetindex; i++) { + paryvertlist = (arraypool **) fastlookup(facetvertexlist, i); + vertlist = *paryvertlist; + idx2facetlist[i + 1] = (idx2facetlist[i] + (int) vertlist->objects); + for (j = 0; j < vertlist->objects; j++) { + parypt = (point *) fastlookup(vertlist, j); + facetverticeslist[k] = *parypt; + k++; } } + assert(k == totalvertices); - //return (badsubfaces->items > oldencnum); - return enccount > 0; + // Free the lists. + for (i = 0; i < facetvertexlist->objects; i++) { + paryvertlist = (arraypool **) fastlookup(facetvertexlist, i); + vertlist = *paryvertlist; + delete vertlist; + } + delete facetvertexlist; } /////////////////////////////////////////////////////////////////////////////// // // -// tallbadtetrahedrons() Queue all the bad-quality tetrahedra in the mesh.// +// Check whether two segments, or a segment and a facet, or two facets are // +// adjacent to each other. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::tallbadtetrahedrons() +int tetgenmesh::segsegadjacent(face *seg1, face *seg2) { - triface tetloop; + int segidx1 = getfacetindex(*seg1); + int segidx2 = getfacetindex(*seg2); - tetrahedrons->traversalinit(); - tetloop.tet = tetrahedrontraverse(); - while (tetloop.tet != (tetrahedron *) NULL) { - checktet4badqual(&tetloop, true); - tetloop.tet = tetrahedrontraverse(); + if (segidx1 == segidx2) return 0; + + point pa1 = segmentendpointslist[segidx1 * 2]; + point pb1 = segmentendpointslist[segidx1 * 2 + 1]; + point pa2 = segmentendpointslist[segidx2 * 2]; + point pb2 = segmentendpointslist[segidx2 * 2 + 1]; + + if ((pa1 == pa2) || (pa1 == pb2) || (pb1 == pa2) || (pb1 == pb2)) { + return 1; } + return 0; } -/////////////////////////////////////////////////////////////////////////////// -// // -// repairencsegs() Repair (split) all the encroached segments. // -// // -// Each encroached segment is repaired by splitting it - inserting a vertex // -// at or near its midpoint. Newly inserted vertices may encroach upon other // -// subsegments, these are also repaired. // -// // -// 'chkencsub' and 'chkbadtet' are two flags that specify whether one should // -// take note of new encroaced subfaces and bad quality tets that result from // -// inserting vertices to repair encroached subsegments. // -// // -/////////////////////////////////////////////////////////////////////////////// +int tetgenmesh::segfacetadjacent(face *subseg, face *subsh) +{ + int segidx = getfacetindex(*subseg); + point pa = segmentendpointslist[segidx * 2]; + point pb = segmentendpointslist[segidx * 2 + 1]; + + pinfect(pa); + pinfect(pb); + + int fidx = getfacetindex(*subsh); + int count = 0, i; + + for (i = idx2facetlist[fidx]; i < idx2facetlist[fidx+1]; i++) { + if (pinfected(facetverticeslist[i])) count++; + } + + puninfect(pa); + puninfect(pb); + + return count == 1; +} -void tetgenmesh::repairencsegs(bool chkencsub, bool chkbadtet) +int tetgenmesh::facetfacetadjacent(face *subsh1, face *subsh2) { - list **tetlists, **ceillists; - list **sublists, **subceillists; - list *tetlist, *sublist; - queue *flipque; - badface *encloop; - face splitseg; - point newpt, refpt; - point e1, e2; - int nmax, n; + int count = 0, i; - n = 0; - nmax = 128; - if (!b->fliprepair) { - tetlists = new list*[nmax]; - ceillists = new list*[nmax]; - sublists = new list*[nmax]; - subceillists = new list*[nmax]; - } else { - tetlist = new list(sizeof(triface), NULL, 1024); - sublist = new list(sizeof(face), NULL, 256); - flipque = new queue(sizeof(badface)); + int fidx1 = getfacetindex(*subsh1); + int fidx2 = getfacetindex(*subsh2); + + if (fidx1 == fidx2) return 0; + + for (i = idx2facetlist[fidx1]; i < idx2facetlist[fidx1+1]; i++) { + pinfect(facetverticeslist[i]); } - // Loop until the pool 'badsubsegs' is empty. Note that steinerleft == -1 - // if an unlimited number of Steiner points is allowed. - while ((badsubsegs->items > 0) && (steinerleft != 0)) { - badsubsegs->traversalinit(); - encloop = badfacetraverse(badsubsegs); - while ((encloop != (badface *) NULL) && (steinerleft != 0)) { - // Get an encroached subsegment s. - splitseg = encloop->ss; - // Clear the in-queue flag in s. - setshell2badface(splitseg, NULL); - if ((sorg(splitseg) == encloop->forg) && - (sdest(splitseg) == encloop->fdest)) { - if (b->verbose > 1) { - printf(" Get an enc-seg (%d, %d)\n", pointmark(encloop->forg), - pointmark(encloop->fdest)); - } - refpt = (point) NULL; - if (b->conformdel) { - // Look for a reference point. - checkseg4encroach(&splitseg, NULL, &refpt, false); - } - // Create the new point p (at the middle of s). - makepoint(&newpt); - getsplitpoint(encloop->forg, encloop->fdest, refpt, newpt); - setpointtype(newpt, FREESEGVERTEX); - setpoint2seg(newpt, sencode(splitseg)); - // Decide whether p can be inserted or not. - if (acceptsegpt(newpt, refpt, &splitseg)) { - // Save the endpoints of the seg for size interpolation. - e1 = sorg(splitseg); - if (shelltype(splitseg) == SHARP) { - e2 = sdest(splitseg); - } else { - e2 = (point) NULL; // No need to do size interoplation. - } - if (!b->fliprepair) { - // Form BC(p), B(p), CBC(p)s, and C(p)s. - formbowatcavity(newpt, &splitseg, NULL, &n, &nmax, sublists, - subceillists, tetlists, ceillists); - // Validate/update BC(p), B(p), CBC(p)s, and C(p)s. - if (trimbowatcavity(newpt, &splitseg, n, sublists, subceillists, - tetlists, ceillists, -1.0)) { - bowatinsertsite(newpt, &splitseg, n, sublists, subceillists, - tetlists, ceillists, NULL, flipque, true, - chkencsub, chkbadtet); - setnewpointsize(newpt, e1, e2); - if (steinerleft > 0) steinerleft--; - } else { - // p did not insert for invalid B(p). - pointdealloc(newpt); - } - // Free the memory allocated in formbowatcavity(). - 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); - } - } - } 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)) - } // if ((encloop->forg == pa) && (encloop->fdest == pb)) - badfacedealloc(badsubsegs, encloop); // Remove this entry from list. - encloop = badfacetraverse(badsubsegs); // Get the next enc-segment. - } // while ((encloop != (badface *) NULL) && (steinerleft != 0)) - } // while ((badsubsegs->items > 0) && (steinerleft != 0)) - - if (!b->fliprepair) { - delete [] tetlists; - delete [] ceillists; - delete [] sublists; - delete [] subceillists; - } else { - delete tetlist; - delete sublist; - delete flipque; + for (i = idx2facetlist[fidx2]; i < idx2facetlist[fidx2+1]; i++) { + if (pinfected(facetverticeslist[i])) count++; + } + + // Uninfect the vertices. + for (i = idx2facetlist[fidx1]; i < idx2facetlist[fidx1+1]; i++) { + puninfect(facetverticeslist[i]); } + + return count > 0; } /////////////////////////////////////////////////////////////////////////////// // // -// repairencsubs() Repair (split) all the encroached subfaces. // -// // -// Each encroached subface is repaired by splitting it - inserting a vertex // -// at or near its circumcenter. Newly inserted vertices may encroach upon // -// other subfaces, these are also repaired. // -// // -// 'chkbadtet' is a flag that specifies whether one should take note of new // -// bad quality tets that result from inserted vertices. // +// checkseg4encroach() Check if an edge is encroached upon by a point. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::repairencsubs(bool chkbadtet) +int tetgenmesh::checkseg4encroach(point pa, point pb, point checkpt) { - list *tetlists[2], *ceillists[2]; - list *sublist, *subceillist; - list *verlist; - badface *encloop; - face splitsub, symsplitsub; - point newpt, e1; - enum locateresult loc; - REAL normal[3], len; - bool reject; - long oldptnum, oldencsegnum; - int quenumber, n, i; - - n = 0; - sublist = (list *) NULL; - subceillist = (list *) NULL; - verlist = new list(sizeof(point *), NULL, 256); + // Check if the point lies inside the diametrical sphere of this seg. + REAL v1[3], v2[3]; - // Loop until the pool 'badsubfaces' is empty. Note that steinerleft == -1 - // if an unlimited number of Steiner points is allowed. - while ((badsubfaces->items > 0) && (steinerleft != 0)) { - // Get an encroached subface f. - encloop = dequeueencsub(&quenumber); - splitsub = encloop->ss; - // Clear the in-queue flag of f. - setshell2badface(splitsub, NULL); - // f may not be the same one when it was determined to be encroached. - if (!isdead(&splitsub) - && (sorg(splitsub) == encloop->forg) - && (sdest(splitsub) == encloop->fdest) - && (sapex(splitsub) == encloop->fapex)) { - if (b->verbose > 1) { - printf(" Dequeuing ensub (%d, %d, %d) [%d].\n", - pointmark(encloop->forg), pointmark(encloop->fdest), - pointmark(encloop->fapex), quenumber); - } - // Create a new point p at the circumcenter of f. - makepoint(&newpt); - for (i = 0; i < 3; i++) newpt[i] = encloop->cent[i]; - setpointtype(newpt, FREESUBVERTEX); - setpoint2sh(newpt, sencode(splitsub)); - // 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; - } - // Locate p, start from f, stop at segment (1), use a tolerance to - // detect ONVERTEX or OUTSIDE case. Update f on return. - loc = locatesub(newpt, &splitsub, 1, b->epsilon * 1e+2); - if ((loc != ONVERTEX) && (loc != OUTSIDE)) { - // Form BC(p), B(p), CBC(p) and C(p). - 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. - reject = !trimbowatcavity(newpt, NULL, n, &sublist, &subceillist, - tetlists, ceillists, -1.0); - } - if (!reject) { - // CBC(p) should include s, so that s can be removed after CBC(p) - // is remeshed. However, if there are locally non-Delaunay faces - // and encroached subsegments, s may not be collected in CBC(p). - // p should not be inserted in such case. - reject = !sinfected(encloop->ss); - } - if (!reject) { - // Save a point for size interpolation. - e1 = sorg(splitsub); - bowatinsertsite(newpt, NULL, n, &sublist, &subceillist, tetlists, - ceillists, NULL, NULL, true, true, chkbadtet); - setnewpointsize(newpt, e1, NULL); - if (steinerleft > 0) steinerleft--; - } else { - // p is rejected for the one of the following reasons: - // (1) BC(p) is not valid. - // (2) s does not in CBC(p). - // (3) p encroaches upon some segments (queued); or - // (4) p is rejected by point accepting rule, or - // (5) due to the rejection of symp (the PBC). - pointdealloc(newpt); - } // if (!reject) - // Release the cavity and free the memory. - releasebowatcavity(NULL,n,&sublist,&subceillist,tetlists,ceillists); - if (reject) { - // Are there queued encroached subsegments. - if (badsubsegs->items > 0) { - // Repair enc-subsegments. - oldptnum = points->items; - repairencsegs(true, chkbadtet); - /*if (points->items > oldptnum) { - // Some enc-subsegments got split. Try to repair f later. - splitsub = encloop->ss; - if (!isdead(&splitsub)) { - if (!shell2badface(splitsub)) { - checksub4encroach(&splitsub, NULL, true); - } - } - }*/ - } + v1[0] = pa[0] - checkpt[0]; + v1[1] = pa[1] - checkpt[1]; + v1[2] = pa[2] - checkpt[2]; + v2[0] = pb[0] - checkpt[0]; + v2[1] = pb[1] - checkpt[1]; + v2[2] = pb[2] - checkpt[2]; + + if (dot(v1, v2) < 0) { + // Inside. + if (b->metric) { // -m option. + if ((pa[pointmtrindex] > 0) && (pb[pointmtrindex] > 0)) { + // The projection of 'checkpt' lies inside the segment [a,b]. + REAL prjpt[3], u, v, t; + projpt2edge(checkpt, pa, pb, prjpt); + // Interoplate the mesh size at the location 'prjpt'. + u = distance(pa, pb); + v = distance(pa, prjpt); + t = v / u; + // 'u' is the mesh size at 'prjpt' + u = pa[pointmtrindex] + t * (pb[pointmtrindex] - pa[pointmtrindex]); + v = distance(checkpt, prjpt); + if (v < u) { + return 1; // Encroached prot-ball! } } else { - // Don't insert p for one of the following reasons: - // (1) Locate on an existing vertex; or - // (2) locate outside the domain. - // Case (1) should not be possible. If such vertex v exists, it is - // the circumcenter of f, ie., f is non-Delaunay. Either f was got - // split before by v, but survived after v was inserted, or the - // same for a f' which is nearly co-circular with f. Whatsoever, - // there are encroached segs by v, but the routine tallencsegs() - // did not find them out. - if (loc == ONVERTEX) { - printf("Internal error in repairencsubs():\n"); - printf(" During repairing encroached subface (%d, %d, %d)\n", - pointmark(encloop->forg), pointmark(encloop->fdest), - pointmark(encloop->fapex)); - printf(" New point %d is coincident with an existing vertex %d\n", - pointmark(newpt), pointmark(sorg(splitsub))); - terminatetetgen(2); - } - 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 { - 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)) - - delete verlist; + return 1; // NO protecting ball. Encroached. + } + } else { + return 1; // Inside! Encroached. + } + } + + return 0; } /////////////////////////////////////////////////////////////////////////////// // // -// repairbadtets() Repair all bad-quality tetrahedra. // +// checkseg4split() Check if we need to split a segment. // +// // +// A segment needs to be split if it is in the following case: // +// (1) It is encroached by an existing vertex. // +// (2) It has bad quality (too long). // +// (3) Its length is larger than the mesh sizes at its endpoints. // // // -// All bad-quality tets are stored in pool 'badtetrahedrons'. Each bad tet // -// is repaired by inserting a point at or near its circumcenter. However, if // -// this point encroaches any subsegment or subface, it is not inserted. Ins- // -// tead the encroached segment and subface are split. Newly inserted points // -// may create other bad-quality tets, these are also repaired. // +// Return 1 if it needs to be split, otherwise, return 0. 'pencpt' returns // +// an encroaching point if there exists. 'qflag' returns '1' if the segment // +// has a length larger than the desired edge length. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::repairbadtets() +int tetgenmesh::checkseg4split(face *chkseg, point& encpt, int& qflag) { - list *tetlist, *ceillist; - list *verlist; - arraypool *histtetarray; - badface *badtet; - triface starttet; - point newpt, e1; - enum locateresult loc; - bool reject; - long oldptnum; + REAL ccent[3], len, r; int i; - tetlist = new list(sizeof(triface), NULL, 1024); - ceillist = new list(sizeof(triface), NULL, 1024); - verlist = new list(sizeof(point *), NULL, 256); + point forg = sorg(*chkseg); + point fdest = sdest(*chkseg); - histtetarray = new arraypool(sizeof(triface), 8); + // Initialize the return values. + encpt = NULL; + qflag = 0; - // 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)) { - // Get a bad-quality tet t. - badtet = topbadtetra(); - // Make sure that the tet is still the same one when it was tested. - // Subsequent transformations may have made it a different tet. - if ((badtet != (badface *) NULL) && !isdead(&badtet->tt) - && org(badtet->tt) == badtet->forg - && dest(badtet->tt) == badtet->fdest - && apex(badtet->tt) == badtet->fapex - && oppo(badtet->tt) == badtet->foppo) { - if (b->verbose > 1) { - printf(" Dequeuing btet (%d, %d, %d, %d).\n", - pointmark(badtet->forg), pointmark(badtet->fdest), - pointmark(badtet->fapex), pointmark(badtet->foppo)); - } - // Create the new point p (at the circumcenter of t). - makepoint(&newpt); - for (i = 0; i < 3; i++) newpt[i] = badtet->cent[i]; - 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); - } - if ((loc != ONVERTEX) && (loc != OUTSIDE)) { - // For BC(p) and B(p). - infect(starttet); - tetlist->append(&starttet); - formbowatcavityquad(newpt, tetlist, ceillist); - // Check for encroached subsegments. - reject = tallencsegs(newpt, 1, &ceillist); - if (!reject) { - // Check for encroached subfaces. - reject = tallencsubs(newpt, 1, &ceillist); - } - // Execute point accepting rule if p does not encroach upon any - // subsegment and subface. - if (!reject) { - reject = !acceptvolpt(newpt, ceillist, verlist); - } - if (!reject) { - reject = !trimbowatcavity(newpt, NULL, 1, NULL, NULL, &tetlist, - &ceillist, -1.0); - } - if (!reject) { - // BC(p) should include t, so that t can be removed after BC(p) is - // remeshed. However, if there are locally non-Delaunay faces - // and encroached subsegments/subfaces, t may not be collected - // in BC(p). p should not be inserted in such case. - reject = !infected(badtet->tt); - if (reject) outbowatcircumcount++; - } - if (!reject) { - // Save a point for size interpolation. - e1 = org(starttet); - // Insert p. - bowatinsertsite(newpt, NULL, 1, NULL, NULL, &tetlist, &ceillist, - NULL, NULL, false, false, true); - setnewpointsize(newpt, e1, NULL); - if (steinerleft > 0) steinerleft--; - } else { - // p is rejected for one of the following reasons: - // (1) BC(p) is not valid. - // (2) t does not in BC(p). - // (3) p encroaches upon some segments; - // (4) p encroaches upon some subfaces; - // (5) p is rejected by the point accepting rule. - pointdealloc(newpt); - // Uninfect tets of BC(p). - for (i = 0; i < tetlist->len(); i++) { - starttet = * (triface *)(* tetlist)[i]; - uninfect(starttet); - } - } - tetlist->clear(); - ceillist->clear(); - // Split encroached subsegments/subfaces if there are. - if (reject) { - oldptnum = points->items; - if (badsubsegs->items > 0) { - repairencsegs(true, true); - } - if (badsubfaces->items > 0) { - repairencsubs(true); - } - if (points->items > oldptnum) { - // Some encroaching subsegments/subfaces got split. Re-queue the - // tet if it is still alive. - starttet = badtet->tt; - if (!isdead(&starttet)) { - checktet4badqual(&starttet, true); + len = distance(forg, fdest); + r = 0.5 * len; + for (i = 0; i < 3; i++) { + ccent[i] = 0.5 * (forg[i] + fdest[i]); + } + + // First check its quality. + if (checkconstraints && (areabound(*chkseg) > 0.0)) { + if (len > areabound(*chkseg)) { + qflag = 1; + return 1; + } + } + + if (b->fixedvolume) { + if ((len * len * len) > b->maxvolume) { + qflag = 1; + return 1; + } + } + + if (b->metric) { // -m option. Check mesh size. + // Check if the ccent lies outside one of the prot.balls at vertices. + if (((forg[pointmtrindex] > 0) && (r > forg[pointmtrindex])) || + ((fdest[pointmtrindex]) > 0 && (r > fdest[pointmtrindex]))) { + qflag = 1; // Enforce mesh size. + return 1; + } + } + + + // Second check if it is encroached. + // Comment: There may exist more than one encroaching points of this segment. + // The 'encpt' returns the one which is closet to it. + triface searchtet, spintet; + point eapex; + REAL d, diff, smdist = 0; + int t1ver; + + sstpivot1(*chkseg, searchtet); + spintet = searchtet; + while (1) { + eapex = apex(spintet); + if (eapex != dummypoint) { + d = distance(ccent, eapex); + diff = d - r; + if (fabs(diff) / r < b->epsilon) diff = 0.0; // Rounding. + if (diff < 0) { + // This segment is encroached by eapex. + if (useinsertradius) { + if (encpt == NULL) { + encpt = eapex; + smdist = d; + } else { + // Choose the closet encroaching point. + if (d < smdist) { + encpt = eapex; + smdist = d; } } + } else { + encpt = eapex; + break; } - } else { - // Do not insert p. The reason may be one of: - // (1) p is coincident (ONVERTEX) with an existing vertex; or - // (2) p is outside (OUTSIDE) the mesh. - // Case (1) should not be possible. If such vertex v exists, it is - // the circumcenter of t, ie., t is non-Delaunay. Either t was got - // split before by v, but survived after v was inserted, or the - // same for a t' which is nearly co-spherical with t. Whatsoever, - // there are encroached segments or subfaces by v but the routines - // tallencsegs() or tallencsubs() did not find them out. - if (loc == ONVERTEX) { - printf("Internal error in repairbadtets():\n"); - printf(" During repairing bad tet (%d, %d, %d, %d)\n", - pointmark(badtet->forg), pointmark(badtet->fdest), - 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); - } - // 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 - // of t encroaches upon s (or f), but the circumcenter of s (or f) - // is rejected for insertion. - pointdealloc(newpt); - } // if ((loc != ONVERTEX) && (loc != OUTSIDE)) - } // if (!isdead(&badtet->tt) && org(badtet->tt) == badtet->forg && - // Remove the tet from the queue. - dequeuebadtet(); - } // while ((badtetrahedrons->items > 0) && (steinerleft != 0)) - - delete tetlist; - delete ceillist; - delete verlist; - delete histtetarray; + } + } + fnextself(spintet); + if (spintet.tet == searchtet.tet) break; + } // while (1) + + if (encpt != NULL) { + return 1; + } + + return 0; // No need to split it. } /////////////////////////////////////////////////////////////////////////////// // // -// enforcequality() Refine the mesh. // +// splitsegment() Split a segment. // +// // +// The segment 'splitseg' is intended to be split. It will be split if it // +// is in one of the following cases: // +// (1) It is encroached by an existing vertex 'encpt != NULL'; or // +// (2) It is in bad quality 'qflag == 1'; or // +// (3) Its length is larger than the mesh sizes at its endpoints. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::enforcequality() +int tetgenmesh::splitsegment(face *splitseg, point encpt, REAL rrp, + point encpt1, point encpt2, int qflag, + int chkencflag) { - long total, vertcount; - int i; - - if (!b->quiet) { - printf("Adding Steiner points to enforce quality.\n"); - } + point pa = sorg(*splitseg); + point pb = sdest(*splitseg); - total = vertcount = 0l; - if (b->conformdel) { - r2count = r3count = 0l; - } - // If both '-D' and '-r' options are used. - if (b->conformdel && b->refine) { - markacutevertices(65.0); - } - // If '-m' is not used. - if (!b->metric) { - // Find and mark all sharp segments. - marksharpsegments(65.0); - // Decide the sizes for feature points. - decidefeaturepointsizes(); - } - // Initialize the pool of encroached subsegments. - badsubsegs = new memorypool(sizeof(badface), SUBPERBLOCK, POINTER, 0); - // Looking for encroached subsegments. - tallencsegs(NULL, 0, NULL); - if (b->verbose && badsubsegs->items > 0) { - printf(" Splitting encroached subsegments.\n"); - } - vertcount = points->items; - // Fix encroached segments without noting any enc subfaces. - repairencsegs(false, false); - if (b->verbose > 0) { - printf(" %ld split points.\n", points->items - vertcount); - } - total += points->items - vertcount; - - // Initialize the pool of encroached subfaces. - badsubfaces = new memorypool(sizeof(badface), SUBPERBLOCK, POINTER, 0); - // Initialize the priority queues of badfaces. - for (i = 0; i < 3; i++) subquefront[i] = (badface *) NULL; - for (i = 0; i < 3; i++) subquetail[i] = &subquefront[i]; - // Looking for encroached subfaces. - tallencsubs(NULL, 0, NULL); - if (b->verbose && badsubfaces->items > 0) { - printf(" Splitting encroached subfaces.\n"); - } - vertcount = points->items; - // Fix encroached subfaces without noting bad tetrahedra. - repairencsubs(false); - if (b->verbose > 0) { - printf(" %ld split points.\n", points->items - vertcount); - } - total += points->items - vertcount; - // At this point, the mesh should be conforming Delaunay if no input - // angle is smaller than 90 degree. - - // Next, fix bad quality tetrahedra. - if ((b->minratio > 0.0) || b->varvolume || b->fixedvolume) { - // Initialize the pool of bad tets - badtetrahedrons = new memorypool(sizeof(badface), ELEPERBLOCK, POINTER, 0); - // Initialize the priority queues of bad tets. - for (i = 0; i < 64; i++) tetquefront[i] = (badface *) NULL; - firstnonemptyq = -1; - recentq = -1; - // Looking for bad quality tets. - cosmaxdihed = cos(b->maxdihedral * PI / 180.0); - cosmindihed = cos(b->mindihedral * PI / 180.0); - tallbadtetrahedrons(); - if (b->verbose && badtetrahedrons->items > 0) { - printf(" Splitting bad tetrahedra.\n"); - } - vertcount = points->items; - repairbadtets(); - if (b->verbose > 0) { - printf(" %ld refinement points.\n", points->items - vertcount); - } - total += points->items - vertcount; - delete badtetrahedrons; + if ((encpt == NULL) && (qflag == 0)) { + if (useinsertradius) { + // Do not split this segment if the length is smaller than the smaller + // insertion radius at its endpoints. + REAL len = distance(pa, pb); + REAL smrrv = getpointinsradius(pa); + REAL rrv = getpointinsradius(pb); + if (rrv > 0) { + if (smrrv > 0) { + if (rrv < smrrv) { + smrrv = rrv; + } + } else { + smrrv = rrv; + } + } + if (smrrv > 0) { + if ((fabs(smrrv - len) / len) < b->epsilon) smrrv = len; + if (len < smrrv) { + return 0; + } + } + } } - if (b->verbose > 0) { - printf(" Totally added %ld points.\n", total); - } + if (b->nobisect) { // With -Y option. + // Only split this segment if it is allowed to be split. + if (checkconstraints) { + // Check if it has a non-zero length bound. + if (areabound(*splitseg) == 0) { + // It is not allowed. However, if all of facets containing this seg + // is allowed to be split, we still split it. + face parentsh, spinsh; + //splitseg.shver = 0; + spivot(*splitseg, parentsh); + if (parentsh.sh == NULL) { + return 0; // A dangling segment. Do not split it. + } + spinsh = parentsh; + while (1) { + if (areabound(spinsh) == 0) break; + spivotself(spinsh); + if (spinsh.sh == parentsh.sh) break; + } + if (areabound(spinsh) == 0) { + // All facets at this seg are not allowed to be split. + return 0; // Do not split it. + } + } + } else { + return 0; // Do not split this segment. + } + } // if (b->nobisect) - delete badsubfaces; - delete badsubsegs; + triface searchtet; + face searchsh; + point newpt; + insertvertexflags ivf; + + makepoint(&newpt, FREESEGVERTEX); + getsteinerptonsegment(splitseg, encpt, newpt); + + // Split the segment by the Bowyer-Watson algorithm. + sstpivot1(*splitseg, searchtet); + ivf.iloc = (int) ONEDGE; + // Use Bowyer-Watson algorithm. Preserve subsegments and subfaces; + ivf.bowywat = 3; + ivf.validflag = 1; // Validate the B-W cavity. + ivf.lawson = 2; // Do flips to recover Delaunayness. + ivf.rejflag = 0; // Do not check encroachment of new segments/facets. + if (b->metric) { + ivf.rejflag |= 4; // Do check encroachment of protecting balls. + } + ivf.chkencflag = chkencflag; + ivf.sloc = (int) INSTAR; // ivf.iloc; + ivf.sbowywat = 3; // ivf.bowywat; // Surface mesh options. + ivf.splitbdflag = 1; + ivf.respectbdflag = 1; + ivf.assignmeshsize = b->metric; + ivf.smlenflag = useinsertradius; + + + if (insertpoint(newpt, &searchtet, &searchsh, splitseg, &ivf)) { + st_segref_count++; + if (steinerleft > 0) steinerleft--; + if (useinsertradius) { + // Update 'rv' (to be the shortest distance). + REAL rv = ivf.smlen, rp; + if (pointtype(ivf.parentpt) == FREESEGVERTEX) { + face parentseg1, parentseg2; + sdecode(point2sh(newpt), parentseg1); + sdecode(point2sh(ivf.parentpt), parentseg2); + if (segsegadjacent(&parentseg1, &parentseg2)) { + rp = getpointinsradius(ivf.parentpt); + if (rv < rp) { + rv = rp; // The relaxed insertion radius of 'newpt'. + } + } + } else if (pointtype(ivf.parentpt) == FREEFACETVERTEX) { + face parentseg, parentsh; + sdecode(point2sh(newpt), parentseg); + sdecode(point2sh(ivf.parentpt), parentsh); + if (segfacetadjacent(&parentseg, &parentsh)) { + rp = getpointinsradius(ivf.parentpt); + if (rv < rp) { + rv = rp; // The relaxed insertion radius of 'newpt'. + } + } + } + setpointinsradius(newpt, rv); + } + if (flipstack != NULL) { + flipconstraints fc; + fc.chkencflag = chkencflag; + fc.enqflag = 2; + lawsonflip3d(&fc); + unflipqueue->restart(); + } + return 1; + } else { + // Point is not inserted. + pointdealloc(newpt); + return 0; + } } -//// //// -//// //// -//// refine_cxx /////////////////////////////////////////////////////////////// - -//// optimize_cxx ///////////////////////////////////////////////////////////// -//// //// -//// //// - /////////////////////////////////////////////////////////////////////////////// // // -// checktet4ill() Check a tet to see if it is illegal. // -// // -// A tet is "illegal" if it spans on one input facet. Save the tet in queue // -// if it is illegal and the flag 'enqflag' is set. // -// // -// Note: Such case can happen when the input facet has non-coplanar vertices // -// and the Delaunay tetrahedralization of the vertices may creat such tets. // +// repairencsegs() Repair encroached (sub) segments. // // // /////////////////////////////////////////////////////////////////////////////// -bool tetgenmesh::checktet4ill(triface* testtet, bool enqflag) +void tetgenmesh::repairencsegs(int chkencflag) { - badface *newbadtet; - triface checktet; - face checksh1, checksh2; - face checkseg; - bool illflag; - int i; + face *bface; + point encpt = NULL; + int qflag = 0; - illflag = false; - for (testtet->loc = 0; testtet->loc < 4; testtet->loc++) { - tspivot(*testtet, checksh1); - if (checksh1.sh != dummysh) { - testtet->ver = 0; - findedge(&checksh1, org(*testtet), dest(*testtet)); - for (i = 0; i < 3; i++) { - fnext(*testtet, checktet); - tspivot(checktet, checksh2); - if (checksh2.sh != dummysh) { - // Two subfaces share this edge. - sspivot(checksh1, checkseg); - if (checkseg.sh == dummysh) { - // The four corners of the tet are on one facet. Illegal! Try to - // flip the opposite edge of the current one. - enextfnextself(*testtet); - enextself(*testtet); - illflag = true; - break; + // Loop until the pool 'badsubsegs' is empty. Note that steinerleft == -1 + // if an unlimited number of Steiner points is allowed. + while ((badsubsegs->items > 0) && (steinerleft != 0)) { + badsubsegs->traversalinit(); + bface = (face *) badsubsegs->traverse(); + while ((bface != NULL) && (steinerleft != 0)) { + // Skip a deleleted element. + if (bface->shver >= 0) { + // A queued segment may have been deleted (split). + if ((bface->sh != NULL) && (bface->sh[3] != NULL)) { + // A queued segment may have been processed. + if (smarktest2ed(*bface)) { + sunmarktest2(*bface); + if (checkseg4split(bface, encpt, qflag)) { + splitsegment(bface, encpt, 0, NULL, NULL, qflag, chkencflag); + } } } - enextself(*testtet); - senextself(checksh1); + // Remove this entry from list. + bface->shver = -1; // Signal it as a deleted element. + badsubsegs->dealloc((void *) bface); } + bface = (face *) badsubsegs->traverse(); } - if (illflag) break; } - if (illflag && enqflag) { - // Allocate space for the bad tetrahedron. - newbadtet = (badface *) badtetrahedrons->alloc(); - newbadtet->tt = *testtet; - newbadtet->key = -1.0; // = 180 degree. - for (i = 0; i < 3; i++) newbadtet->cent[i] = 0.0; - newbadtet->forg = org(*testtet); - newbadtet->fdest = dest(*testtet); - newbadtet->fapex = apex(*testtet); - newbadtet->foppo = oppo(*testtet); - newbadtet->nextitem = (badface *) NULL; - if (b->verbose > 2) { - printf(" Queueing illtet: (%d, %d, %d, %d).\n", - pointmark(newbadtet->forg), pointmark(newbadtet->fdest), - pointmark(newbadtet->fapex), pointmark(newbadtet->foppo)); + if (badsubsegs->items > 0) { + if (steinerleft == 0) { + if (b->verbose) { + printf("The desired number of Steiner points is reached.\n"); + } + } else { + assert(0); // Unknown case. + } + badsubsegs->traversalinit(); + bface = (face *) badsubsegs->traverse(); + while (bface != NULL) { + // Skip a deleleted element. + if (bface->shver >= 0) { + if ((bface->sh != NULL) && (bface->sh[3] != NULL)) { + if (smarktest2ed(*bface)) { + sunmarktest2(*bface); + } + } + } + bface = (face *) badsubsegs->traverse(); } + badsubsegs->restart(); } - - return illflag; } /////////////////////////////////////////////////////////////////////////////// // // -// checktet4opt() Check a tet to see if it needs to be optimized. // -// // -// 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. // -// // -// 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 // -// big dihedral angle if d is very close to the edge ab; t has three big // -// dihedral angles if d's projection on the face abc is also inside abc, i.e.// -// the shape of t likes a hat; finally, t has two big dihedral angles if d's // -// projection onto abc is outside abc. // +// enqueuesubface() Queue a subface or a subsegment for encroachment chk. // // // /////////////////////////////////////////////////////////////////////////////// -bool tetgenmesh::checktet4opt(triface* testtet, bool enqflag) +void tetgenmesh::enqueuesubface(memorypool *pool, face *chkface) { - badface *newbadtet; - point pa, pb, pc, pd; - REAL N[4][3], len; - REAL cosd; - int count; - int i, j; - - pa = (point) testtet->tet[4]; - pb = (point) testtet->tet[5]; - pc = (point) testtet->tet[6]; - pd = (point) testtet->tet[7]; - // Compute the 4 face normals: N[0] cbd, N[1] acd, N[2] bad, N[3] abc. - 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 (!smarktest2ed(*chkface)) { + smarktest2(*chkface); // Only queue it once. + face *queface = (face *) pool->alloc(); + *queface = *chkface; } +} - count = 0; +/////////////////////////////////////////////////////////////////////////////// +// // +// checkfac4encroach() Check if a subface is encroached by a point. // +// // +/////////////////////////////////////////////////////////////////////////////// - // 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; - testtet->ver = 0; - switch (i) { - case 0: // edge ab - cosd = -dot(N[2], N[3]); - break; - case 1: // edge cd - enextfnextself(*testtet); - enextself(*testtet); - cosd = -dot(N[0], N[1]); - break; - case 2: // edge bd - enextfnextself(*testtet); - enext2self(*testtet); - cosd = -dot(N[0], N[2]); - break; - case 3: // edge bc - enextself(*testtet); - cosd = -dot(N[0], N[3]); - break; - case 4: // edge ad - enext2fnextself(*testtet); - enextself(*testtet); - cosd = -dot(N[1], N[2]); - break; - case 5: // edge ac - enext2self(*testtet); - cosd = -dot(N[1], N[3]); - break; - } - if (cosd < cosmaxdihed) { - // A bigger dihedral angle. - count++; - if (enqflag) { - // Allocate space for the bad tetrahedron. - newbadtet = (badface *) badtetrahedrons->alloc(); - newbadtet->tt = *testtet; - newbadtet->key = cosd; - for (j = 0; j < 3; j++) newbadtet->cent[j] = 0.0; - newbadtet->forg = org(*testtet); - newbadtet->fdest = dest(*testtet); - newbadtet->fapex = apex(*testtet); - newbadtet->foppo = oppo(*testtet); - newbadtet->nextitem = (badface *) NULL; - if (b->verbose > 2) { - printf(" Queueing tet: (%d, %d, %d, %d), dihed %g (degree).\n", - pointmark(newbadtet->forg), pointmark(newbadtet->fdest), - pointmark(newbadtet->fapex), pointmark(newbadtet->foppo), - acos(cosd) * 180.0 / PI); +int tetgenmesh::checkfac4encroach(point pa, point pb, point pc, point checkpt, + REAL* cent, REAL* r) +{ + REAL rd, len; + + circumsphere(pa, pb, pc, NULL, cent, &rd); + assert(rd != 0); + len = distance(cent, checkpt); + if ((fabs(len - rd) / rd) < b->epsilon) len = rd; // Rounding. + + if (len < rd) { + // The point lies inside the circumsphere of this face. + if (b->metric) { // -m option. + if ((pa[pointmtrindex] > 0) && (pb[pointmtrindex] > 0) && + (pc[pointmtrindex] > 0)) { + // Get the projection of 'checkpt' in the plane of pa, pb, and pc. + REAL prjpt[3], n[3]; + REAL a, a1, a2, a3; + projpt2face(checkpt, pa, pb, pc, prjpt); + // Get the face area of [a,b,c]. + facenormal(pa, pb, pc, n, 1, NULL); + a = sqrt(dot(n,n)); + // Get the face areas of [a,b,p], [b,c,p], and [c,a,p]. + facenormal(pa, pb, prjpt, n, 1, NULL); + a1 = sqrt(dot(n,n)); + facenormal(pb, pc, prjpt, n, 1, NULL); + a2 = sqrt(dot(n,n)); + facenormal(pc, pa, prjpt, n, 1, NULL); + a3 = sqrt(dot(n,n)); + if ((fabs(a1 + a2 + a3 - a) / a) < b->epsilon) { + // This face contains the projection. + // Get the mesh size at the location of the projection point. + rd = a1 / a * pc[pointmtrindex] + + a2 / a * pa[pointmtrindex] + + a3 / a * pb[pointmtrindex]; + len = distance(prjpt, checkpt); + if (len < rd) { + return 1; // Encroached. + } } + } else { + return 1; // No protecting ball. Encroached. } + } else { + *r = rd; + return 1; // Encroached. } } - return count > 0; + return 0; } /////////////////////////////////////////////////////////////////////////////// // // -// removeedge() Remove an edge // -// // -// 'remedge' is a tet (abcd) having the edge ab wanted to be removed. Local // -// reconnecting operations are used to remove edge ab. The following opera- // -// tion will be tryed. // +// checkfac4split() Check if a subface needs to be split. // // // -// If ab is on the hull, and abc and abd are both hull faces. Then ab can be // -// removed by stripping abcd from the mesh. However, if ab is a segemnt, do // -// the operation only if 'b->optlevel' > 1 and 'b->nobisect == 0'. // +// A subface needs to be split if it is in the following case: // +// (1) It is encroached by an existing vertex. // +// (2) It has bad quality (has a small angle, -q). // +// (3) It's area is larger than a prescribed value (.var). // // // -// If ab is an internal edge, there are n tets contains it. Then ab can be // -// removed if there exists another m tets which can replace the n tets with- // -// out changing the boundary of the n tets. // -// // -// If 'optflag' is set. The value 'remedge->key' means cos(theta), where // -// 'theta' is the maximal dishedral angle at ab. In this case, even if the // -// n-to-m flip exists, it will not be performed if the maximum dihedral of // -// the new tets is larger than 'theta'. // +// Return 1 if it needs to be split, otherwise, return 0. // +// 'chkfac' represents its longest edge. // // // /////////////////////////////////////////////////////////////////////////////// -bool tetgenmesh::removeedge(badface* remedge, bool optflag) +int tetgenmesh::checkfac4split(face *chkfac, point& encpt, int& qflag, + REAL *cent) { - 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; - //enum fliptype fty; - REAL key; - bool remflag, subflag; - int n, n1, m, i, j, k; - - triface newtet; - point *ppt; + point pa, pb, pc; + REAL area, rd, len; + REAL A[4][4], rhs[4], D; + int indx[4]; + int i; - // 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; - do { - sym(abcd, baccasing); - // Is the tet on the hull? - if (baccasing.tet == dummytet) { - fnext(abcd, badc); - sym(badc, abdcasing); - if (abdcasing.tet == dummytet) { - // Strip the tet from the mesh -> ab is removed as well. - if (removetetbypeeloff(&abcd, newtetlist)) { - 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)); - } - } - return true; - } - } - } - // Check if the oppsite edge cd is on the hull. - enext2fnextself(abcd); - enext2self(abcd); - esymself(abcd); // --> cdab - k++; - } while (k < 2); + encpt = NULL; + qflag = 0; - // Get the tets configuration at ab. Collect maximum 10 tets. - subflag = false; - abcd = remedge->tt; - 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. - subflag = true; break; // return false; - } - // Get the next tet at ab. - fnext(abtetlist[n], abtetlist[n + 1]); - n++; - } while (apex(abtetlist[n]) != apex(abcd)); + pa = sorg(*chkfac); + pb = sdest(*chkfac); + pc = sapex(*chkfac); - remflag = false; - key = remedge->key; + // 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]; // vector V1 (pa->pb) + A[1][0] = pc[0] - pa[0]; + A[1][1] = pc[1] - pa[1]; + A[1][2] = pc[2] - pa[2]; // vector V2 (pa->pc) + cross(A[0], A[1], A[2]); // vector V3 (V1 X V2) - if (subflag && optflag) { - /*abcd = remedge->tt; - adjustedgering(abcd, CCW); - // Try to flip face cda or cdb to improve quality. - for (j = 0; j < 2; j++) { - if (j == 0) { - enext2fnext(abcd, abtetlist[0]); // Goto cda. - } else { - enextfnext(abcd, abtetlist[0]); // Goto cdb. - } - fty = categorizeface(abtetlist[0]); - if (fty == T23) { - // A 2-to-3 flip is possible. - sym(abtetlist[0], abtetlist[1]); - assert(abtetlist[1].tet != dummytet); - n = 2; - m = 3; - remflag = removefacebyflip23(&key, abtetlist, newtetlist, NULL); - } else if (fty == T22) { - // A 2-to-2 or 4-to-4 flip is possible. - n = 2; - newtetlist[0] = abtetlist[0]; - adjustedgering(newtetlist[0], CW); - fnext(newtetlist[0], newtetlist[1]); - assert(newtetlist[1].tet != dummytet); - // May it is 4-to-4 flip. - if (fnext(newtetlist[1], newtetlist[2])) { - fnext(newtetlist[2], newtetlist[3]); - assert(newtetlist[3].tet != dummytet); - n = 4; - } - m = n; - remflag = removeedgebyflip22(&key, n, newtetlist, NULL); - } - // Has quality been improved? - if (remflag) { - if (b->verbose > 1) { - printf(" Done flip %d-to-%d. Qual: %g -> %g.\n", n, m, - acos(remedge->key) / PI * 180.0, acos(key) / PI * 180.0); - } - // Delete the old tets. Note, flip22() does not create new tets. - if (m == 3) { - for (i = 0; i < n; i++) { - tetrahedrondealloc(abtetlist[i].tet); - } - } - 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 - */ - // Faces are not flipable. Return. - return false; - } + area = 0.5 * sqrt(dot(A[2], A[2])); // The area of [a,b,c]. - // 2 < n < 20. - 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)) { - // Four tets case. Try to do edge transformation. - remflag = removeedgebytranNM(&key,n,abtetlist,newtetlist,NULL,NULL,NULL); - } else { - if (b->verbose > 1) { - printf(" !! Unhandled case: n = %d.\n", n); - } + // Compute the right hand side vector b (3x1). + rhs[0] = 0.5 * dot(A[0], A[0]); // edge [a,b] + rhs[1] = 0.5 * dot(A[1], A[1]); // edge [a,c] + rhs[2] = 0.0; + + // Solve the 3 by 3 equations use LU decomposition with partial + // pivoting and backward and forward substitute. + if (!lu_decmp(A, 3, indx, &D, 0)) { + // A degenerate triangle. + assert(0); } - if (remflag) { - optcount[n]++; - // 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, m); - if (optflag) { - printf("Qual: %g -> %g.", acos(remedge->key) / PI * 180.0, - acos(key) / PI * 180.0); - } - printf("\n"); + + lu_solve(A, 3, indx, rhs, 0); + cent[0] = pa[0] + rhs[0]; + cent[1] = pa[1] + rhs[1]; + cent[2] = pa[2] + rhs[2]; + rd = sqrt(rhs[0] * rhs[0] + rhs[1] * rhs[1] + rhs[2] * rhs[2]); + + if (checkconstraints && (areabound(*chkfac) > 0.0)) { + // Check if the subface has too big area. + if (area > areabound(*chkfac)) { + qflag = 1; + return 1; } } - if (!remflag && (key == remedge->key) && (n <= b->maxflipedgelinksize)) { - // Try to do a combination of flips. - n1 = 0; - remflag = removeedgebycombNM(&key, n, abtetlist, &n1, bftetlist, - newtetlist, NULL); - if (remflag) { - optcount[9]++; - // 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); - } - } - 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); - if (optflag) { - printf("Qual: %g -> %g.", acos(remedge->key) / PI * 180.0, - acos(key) / PI * 180.0); - } - printf("\n"); - } + if (b->fixedvolume) { + if ((area * sqrt(area)) > b->maxvolume) { + qflag = 1; + return 1; } } - if (remflag) { - // edge is removed. Test new tets for further optimization. - for (i = 0; i < m; i++) { - if (optflag) { - checktet4opt(&(newtetlist[i]), true); - } else { - checktet4ill(&(newtetlist[i]), true); + if (b->varvolume) { + triface adjtet; + REAL volbnd; + int t1ver; + + stpivot(*chkfac, adjtet); + if (!ishulltet(adjtet)) { + volbnd = volumebound(adjtet.tet); + if ((volbnd > 0) && (area * sqrt(area)) > volbnd) { + qflag = 1; + return 1; } } - // 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)); + fsymself(adjtet); + if (!ishulltet(adjtet)) { + volbnd = volumebound(adjtet.tet); + if ((volbnd > 0) && (area * sqrt(area)) > volbnd) { + qflag = 1; + return 1; } } - 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; + if (b->metric) { // -m option. Check mesh size. + // Check if the ccent lies outside one of the prot.balls at vertices. + if (((pa[pointmtrindex] > 0) && (rd > pa[pointmtrindex])) || + ((pb[pointmtrindex] > 0) && (rd > pb[pointmtrindex])) || + ((pc[pointmtrindex] > 0) && (rd > pc[pointmtrindex]))) { + qflag = 1; // Enforce mesh size. + return 1; } } - 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]; - } + triface searchtet; + REAL smlen = 0; - // 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()); + // Check if this subface is locally encroached. + for (i = 0; i < 2; i++) { + stpivot(*chkfac, searchtet); + if (!ishulltet(searchtet)) { + len = distance(oppo(searchtet), cent); + if ((fabs(len - rd) / rd) < b->epsilon) len = rd;// Rounding. + if (len < rd) { + if (smlen == 0) { + smlen = len; + encpt = oppo(searchtet); } 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; + if (len < smlen) { + smlen = len; + encpt = oppo(searchtet); } - } 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; } + //return 1; } - if (iter > 0) *key = maxcosd; } + sesymself(*chkfac); } - 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; - } + return encpt != NULL; //return 0; } /////////////////////////////////////////////////////////////////////////////// // // -// smoothsliver() Remove a sliver by smoothing a vertex of it. // +// splitsubface() Split a subface. // // // -// The 'slivtet' represents a sliver abcd, and ab is the current edge which // -// has a large dihedral angle (close to 180 degree). // +// The subface may be encroached, or in bad-quality. It is split at its cir- // +// cumcenter ('ccent'). Do not split it if 'ccent' encroaches upon any seg- // +// ment. Instead, one of the encroached segments is split. It is possible // +// that none of the encroached segments can be split. // +// // +// The return value indicates whether a new point is inserted (> 0) or not // +// (= 0). Furthermore, it is inserted on an encroached segment (= 1) or // +// in-side the facet (= 2). // +// // +// 'encpt' is a vertex encroaching upon this subface, i.e., it causes the // +// split of this subface. If 'encpt' is NULL, then the cause of the split // +// this subface is a rejected tet circumcenter 'p', and 'encpt1' is the // +// parent of 'p'. // // // /////////////////////////////////////////////////////////////////////////////// -bool tetgenmesh::smoothsliver(badface* remedge, list *starlist) +int tetgenmesh::splitsubface(face *splitfac, point encpt, point encpt1, + int qflag, REAL *ccent, int chkencflag) { - triface checktet; - point smthpt; - bool smthed; - int idx, i, j; + point pa = sorg(*splitfac); + point pb = sdest(*splitfac); + point pc = sapex(*splitfac); - // Find a Steiner volume point and smooth it. - smthed = false; - for (i = 0; i < 4 && !smthed; i++) { - smthpt = (point) remedge->tt.tet[4 + i]; - // Is it a volume point? - if (pointtype(smthpt) == FREEVOLVERTEX) { - // Is it a Steiner point? - idx = pointmark(smthpt) - in->firstnumber; - if (!(idx < in->numberofpoints)) { - // Smooth a Steiner volume point. - starlist->append(&(remedge->tt.tet)); - formstarpolyhedron(smthpt, starlist, NULL, false); - smthed = smoothpoint(smthpt,NULL,NULL,starlist,false,&remedge->key); - // If it is smoothed. Queue new bad tets. - if (smthed) { - for (j = 0; j < starlist->len(); j++) { - checktet = * (triface *)(* starlist)[j]; - checktet4opt(&checktet, true); - } - } - starlist->clear(); + + + if (b->nobisect) { // With -Y option. + if (checkconstraints) { + // Only split if it is allowed to be split. + // Check if this facet has a non-zero constraint. + if (areabound(*splitfac) == 0) { + return 0; // Do not split it. } + } else { + return 0; } - } + } // if (b->nobisect) - /* Omit to smooth segment points. This may cause infinite loop. - if (smthed) { - return true; + face searchsh; + insertvertexflags ivf; + point newpt; + REAL rv = 0., rp; // Insertion radius of newpt. + int i; + + // Initialize the inserting point. + makepoint(&newpt, FREEFACETVERTEX); + // Split the subface at its circumcenter. + for (i = 0; i < 3; i++) newpt[i] = ccent[i]; + + if (useinsertradius) { + if (encpt != NULL) { + rv = distance(newpt, encpt); + if (pointtype(encpt) == FREESEGVERTEX) { + face parentseg; + sdecode(point2sh(encpt), parentseg); + if (segfacetadjacent(&parentseg, splitfac)) { + rp = getpointinsradius(encpt); + if (rv < (sqrt(2.0) * rp)) { + // This insertion may cause no termination. + pointdealloc(newpt); + return 0; // Reject the insertion of newpt. + } + } + } else if (pointtype(encpt) == FREEFACETVERTEX) { + face parentsh; + sdecode(point2sh(encpt), parentsh); + if (facetfacetadjacent(&parentsh, splitfac)) { + rp = getpointinsradius(encpt); + if (rv < rp) { + pointdealloc(newpt); + return 0; // Reject the insertion of newpt. + } + } + } + } + } // if (useinsertradius) + + // Search a subface which contains 'newpt'. + searchsh = *splitfac; + // Calculate an above point. It lies above the plane containing + // the subface [a,b,c], and save it in dummypoint. Moreover, + // the vector cent->dummypoint is the normal of the plane. + calculateabovepoint4(newpt, pa, pb, pc); + // Parameters: 'aflag' = 1, - above point exists. + // 'cflag' = 0, - non-convex, check co-planarity of the result. + // 'rflag' = 0, - no need to round the locating result. + ivf.iloc = (int) slocate(newpt, &searchsh, 1, 0, 0); + + if (!((ivf.iloc == (int) ONFACE) || (ivf.iloc == (int) ONEDGE))) { + pointdealloc(newpt); + return 0; } - face abseg, nextseg, prevseg; - point pt[2]; - // Check if ab is a segment. - tsspivot(slivtet, &abseg); - if (abseg.sh == dummysh) { - // ab is not a segment. Check if a or b is a Steiner segment point. - for (i = 0; i < 2 && !smthed; i++) { - smthpt = (i == 0 ? org(*slivtet) : dest(*slivtet)); - if (pointtype(smthpt) == FREESEGVERTEX) { - // Is it a Steiner point? - idx = pointmark(smthpt) - in->firstnumber; - if (!(idx < in->numberofpoints)) { - // Smooth a Steiner segment point. Get the segment. - sdecode(point2sh(smthpt), nextseg); - locateseg(smthpt, &nextseg); - assert(sorg(nextseg) == smthpt); - pt[0] = sdest(nextseg); - senext2(nextseg, prevseg); - spivotself(prevseg); - prevseg.shver = 0; - if (sorg(prevseg) == smthpt) sesymself(prevseg); - assert(sdest(prevseg) == smthpt); - pt[1] = sorg(prevseg); - starlist->append(slivtet); - formstarpolyhedron(smthpt, starlist, NULL, true); - smthed = smoothpoint(smthpt, pt[0], pt[1], starlist, false); - // If it is smoothed. Check if the tet is still a sliver. - if (smthed) checktet4opt(slivtet, true); - starlist->clear(); + + + triface searchtet; + face *paryseg; + int splitflag; + + // Insert the point. + stpivot(searchsh, searchtet); + //assert((ivf.iloc == (int) ONFACE) || (ivf.iloc == (int) ONEDGE)); + // Use Bowyer-Watson algorithm. Preserve subsegments and subfaces; + ivf.bowywat = 3; + ivf.lawson = 2; + ivf.rejflag = 1; // Do check the encroachment of segments. + if (b->metric) { + ivf.rejflag |= 4; // Do check encroachment of protecting balls. + } + ivf.chkencflag = chkencflag; + ivf.sloc = (int) INSTAR; // ivf.iloc; + ivf.sbowywat = 3; // ivf.bowywat; + ivf.splitbdflag = 1; + ivf.validflag = 1; + ivf.respectbdflag = 1; + ivf.assignmeshsize = b->metric; + + ivf.refineflag = 2; + ivf.refinesh = searchsh; + ivf.smlenflag = useinsertradius; // Update the insertion radius. + + + if (insertpoint(newpt, &searchtet, &searchsh, NULL, &ivf)) { + st_facref_count++; + if (steinerleft > 0) steinerleft--; + if (useinsertradius) { + // Update 'rv' (to be the shortest distance). + rv = ivf.smlen; + if (pointtype(ivf.parentpt) == FREESEGVERTEX) { + face parentseg, parentsh; + sdecode(point2sh(ivf.parentpt), parentseg); + sdecode(point2sh(newpt), parentsh); + if (segfacetadjacent(&parentseg, &parentsh)) { + rp = getpointinsradius(ivf.parentpt); + if (rv < (sqrt(2.0) * rp)) { + rv = sqrt(2.0) * rp; // The relaxed insertion radius of 'newpt'. + } + } + } else if (pointtype(ivf.parentpt) == FREEFACETVERTEX) { + face parentsh1, parentsh2; + sdecode(point2sh(ivf.parentpt), parentsh1); + sdecode(point2sh(newpt), parentsh2); + if (facetfacetadjacent(&parentsh1, &parentsh2)) { + rp = getpointinsradius(ivf.parentpt); + if (rv < rp) { + rv = rp; // The relaxed insertion radius of 'newpt'. + } + } + } + setpointinsradius(newpt, rv); + } // if (useinsertradius) + if (flipstack != NULL) { + flipconstraints fc; + fc.chkencflag = chkencflag; + fc.enqflag = 2; + lawsonflip3d(&fc); + unflipqueue->restart(); + } + return 1; + } else { + // Point was not inserted. + pointdealloc(newpt); + if (ivf.iloc == (int) ENCSEGMENT) { + // Select an encroached segment and split it. + splitflag = 0; + for (i = 0; i < encseglist->objects; i++) { + paryseg = (face *) fastlookup(encseglist, i); + if (splitsegment(paryseg, NULL, rv, encpt, encpt1, qflag, + chkencflag | 1)) { + splitflag = 1; // A point is inserted on a segment. + break; } } + encseglist->restart(); + if (splitflag) { + // Some segments may need to be repaired. + repairencsegs(chkencflag | 1); + // Queue this subface if it is still alive and not queued. + //if ((splitfac->sh != NULL) && (splitfac->sh[3] != NULL)) { + // // Only queue it if 'qflag' is set. + // if (qflag) { + // enqueuesubface(badsubfacs, splitfac); + // } + //} + } + return splitflag; + } else { + return 0; } } - */ - - return smthed; } /////////////////////////////////////////////////////////////////////////////// // // -// splitsliver() Remove a sliver by inserting a point. // -// // -// The 'remedge->tt' represents a sliver abcd, ab is the current edge which // -// has a large dihedral angle (close to 180 degree). // +// repairencfacs() Repair encroached subfaces. // // // /////////////////////////////////////////////////////////////////////////////// -bool tetgenmesh::splitsliver(badface *remedge, list *tetlist, list *ceillist) +void tetgenmesh::repairencfacs(int chkencflag) { - triface starttet; - face checkseg; - point newpt, pt[4]; - bool remflag; - int i; + face *bface; + point encpt = NULL; + int qflag = 0; + REAL ccent[3]; - // Let 'remedge->tt' be the edge [a, b]. - starttet = remedge->tt; - - // Go to the opposite edge [c, d]. - 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. - 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)); - // Insert p, this should always success. - sstpivot(&checkseg, &starttet); - splittetedge(newpt, &starttet, NULL); - // Collect the new tets connecting at p. - sstpivot(&checkseg, &starttet); - ceillist->append(&starttet); - formstarpolyhedron(newpt, ceillist, NULL, true); - setnewpointsize(newpt, pt[0], NULL); - if (steinerleft > 0) steinerleft--; - // Smooth p. - smoothpoint(newpt, pt[0], pt[1], ceillist, false, NULL); - // Queue new slivers. - for (i = 0; i < ceillist->len(); i++) { - starttet = * (triface *)(* ceillist)[i]; - checktet4opt(&starttet, true); - } - ceillist->clear(); - return true; + // Loop until the pool 'badsubfacs' is empty. Note that steinerleft == -1 + // if an unlimited number of Steiner points is allowed. + while ((badsubfacs->items > 0) && (steinerleft != 0)) { + badsubfacs->traversalinit(); + bface = (face *) badsubfacs->traverse(); + while ((bface != NULL) && (steinerleft != 0)) { + // Skip a deleted element. + if (bface->shver >= 0) { + // A queued subface may have been deleted (split). + if ((bface->sh != NULL) && (bface->sh[3] != NULL)) { + // A queued subface may have been processed. + if (smarktest2ed(*bface)) { + sunmarktest2(*bface); + if (checkfac4split(bface, encpt, qflag, ccent)) { + splitsubface(bface, encpt, NULL, qflag, ccent, chkencflag); + } + } + } + bface->shver = -1; // Signal it as a deleted element. + badsubfacs->dealloc((void *) bface); // Remove this entry from list. + } + bface = (face *) badsubfacs->traverse(); } } - // Create the new point p (at the circumcenter of t). - makepoint(&newpt); - /*// Get the four corners. - for (i = 0; i < 4; i++) { - pt[i] = (point) starttet.tet[4 + i]; - } - 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); - - // Form the Bowyer-Watson cavity of p. - remflag = false; - infect(starttet); - tetlist->append(&starttet); - formbowatcavityquad(newpt, tetlist, ceillist); - if (trimbowatcavity(newpt, NULL, 1, NULL, NULL, &tetlist, &ceillist, -1.0)) { - // Smooth p. - if (smoothpoint( newpt, NULL, NULL, ceillist, false, &remedge->key)) { - // Insert p. - bowatinsertsite(newpt, NULL, 1, NULL, NULL, &tetlist, &ceillist, NULL, - NULL, false, false, false); - setnewpointsize(newpt, pt[0], NULL); - if (steinerleft > 0) steinerleft--; - // Queue new slivers. - for (i = 0; i < ceillist->len(); i++) { - starttet = * (triface *)(* ceillist)[i]; - checktet4opt(&starttet, true); + if (badsubfacs->items > 0) { + if (steinerleft == 0) { + if (b->verbose) { + printf("The desired number of Steiner points is reached.\n"); } - remflag = true; - } // if (smoothpoint) - } // if (trimbowatcavity) - - if (!remflag) { - // p is rejected for BC(p) is not valid. - pointdealloc(newpt); - // Uninfect tets of BC(p). - for (i = 0; i < tetlist->len(); i++) { - starttet = * (triface *)(* tetlist)[i]; - uninfect(starttet); + } else { + assert(0); // Unknown case. + } + badsubfacs->traversalinit(); + bface = (face *) badsubfacs->traverse(); + while (bface != NULL) { + // Skip a deleted element. + if (bface->shver >= 0) { + if ((bface->sh != NULL) && (bface->sh[3] != NULL)) { + if (smarktest2ed(*bface)) { + sunmarktest2(*bface); + } + } + } + bface = (face *) badsubfacs->traverse(); } + badsubfacs->restart(); } - tetlist->clear(); - ceillist->clear(); - - return remflag; } /////////////////////////////////////////////////////////////////////////////// // // -// tallslivers() Queue all the slivers in the mesh. // +// enqueuetetrahedron() Queue a tetrahedron for quality check. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::tallslivers(bool optflag) +void tetgenmesh::enqueuetetrahedron(triface *chktet) { - triface tetloop; - - tetrahedrons->traversalinit(); - tetloop.tet = tetrahedrontraverse(); - while (tetloop.tet != (tetrahedron *) NULL) { - if (optflag) { - checktet4opt(&tetloop, true); - } else { - checktet4ill(&tetloop, true); - } - tetloop.tet = tetrahedrontraverse(); + if (!marktest2ed(*chktet)) { + marktest2(*chktet); // Only queue it once. + triface *quetet = (triface *) badtetrahedrons->alloc(); + *quetet = *chktet; } } /////////////////////////////////////////////////////////////////////////////// // // -// optimizemesh() Improving the mesh quality. // -// // -// 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. // -// (1) is mandatory, while (2) and (3) are optionally. // -// // -// The variable 'b->optlevel' (set after '-s') determines the use of these // -// operations. If it is: 0, do no optimization; 1, only do (1) operation; 2, // -// do (1) and (2) operations; 3, do all operations. Deault, b->optlvel = 2. // +// checktet4split() Check if the tet needs to be split. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::optimizemesh2(bool optflag) +int tetgenmesh::checktet4split(triface *chktet, int &qflag, REAL *ccent) { - list *splittetlist, *tetlist, *ceillist; - badface *remtet, *newbadtet; - REAL maxdihed, objdihed, cosobjdihed; - long oldflipcount, newflipcount; - long oldpointcount; - int slivercount; - int optpasscount; - 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; + point pa, pb, pc, pd, *ppt; + REAL vda[3], vdb[3], vdc[3]; + REAL vab[3], vbc[3], vca[3]; + REAL N[4][3], L[4], cosd[6], elen[6]; + REAL maxcosd, vol, volbnd, smlen = 0, rd; + REAL A[4][4], rhs[4], D; + int indx[4]; + int i, j; - if (!b->quiet) { - if (optflag) { - if (b_steinerflag) { - // This routine is called from removesteiners2(); - } else { - printf("Optimizing mesh.\n"); - } - } else { - printf("Repairing mesh.\n"); + if (b->convex) { // -c + // Skip this tet if it lies in the exterior. + if (elemattribute(chktet->tet, numelemattrib - 1) == -1.0) { + return 0; } } - 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); - } + qflag = 0; + + pd = (point) chktet->tet[7]; + if (pd == dummypoint) { + return 0; // Do not split a hull tet. } - // Initialize the pool of bad tets. - badtetrahedrons = new memorypool(sizeof(badface), ELEPERBLOCK, POINTER, 0); - // Looking for non-optimal tets. - tallslivers(optflag); + pa = (point) chktet->tet[4]; + pb = (point) chktet->tet[5]; + pc = (point) chktet->tet[6]; + + // Get the edge vectors vda: d->a, vdb: d->b, vdc: d->c. + // Set the matrix A = [vda, vdb, vdc]^T. + for (i = 0; i < 3; i++) A[0][i] = vda[i] = pa[i] - pd[i]; + 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]; - oldpointcount = points->items; - opt_tet_peels = opt_face_flips = opt_edge_flips = 0l; - oldflipcount = newflipcount = 0l; - smoothsegverts = 0l; - optpasscount = 0; + // Get the other edge vectors. + for (i = 0; i < 3; i++) vab[i] = pb[i] - pa[i]; + for (i = 0; i < 3; i++) vbc[i] = pc[i] - pb[i]; + for (i = 0; i < 3; i++) vca[i] = pa[i] - pc[i]; - if (optflag && (b->verbose)) { - printf(" level = %d.\n", b->optlevel); + if (!lu_decmp(A, 3, indx, &D, 0)) { + // A degenerated tet (vol = 0). + // This is possible due to the use of exact arithmetic. We temporarily + // leave this tet. It should be fixed by mesh optimization. + return 0; } - // Start the mesh optimization iteration. - do { + // Check volume if '-a#' and '-a' options are used. + if (b->varvolume || b->fixedvolume) { + vol = fabs(A[indx[0]][0] * A[indx[1]][1] * A[indx[2]][2]) / 6.0; + if (b->fixedvolume) { + if (vol > b->maxvolume) { + qflag = 1; + } + } + if (!qflag && b->varvolume) { + volbnd = volumebound(chktet->tet); + if ((volbnd > 0.0) && (vol > volbnd)) { + qflag = 1; + } + } + if (qflag == 1) { + // Calculate the circumcenter of this 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); + for (i = 0; i < 3; i++) ccent[i] = pd[i] + rhs[i]; + return 1; + } + } + + if (b->metric) { // -m option. Check mesh size. + // Calculate the circumradius of this 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); + for (i = 0; i < 3; i++) ccent[i] = pd[i] + rhs[i]; + rd = sqrt(dot(rhs, rhs)); + // Check if the ccent lies outside one of the prot.balls at vertices. + ppt = (point *) &(chktet->tet[4]); + for (i = 0; i < 4; i++) { + if (ppt[i][pointmtrindex] > 0) { + if (rd > ppt[i][pointmtrindex]) { + qflag = 1; // Enforce mesh size. + return 1; + } + } + } + } - if (optflag && (b->verbose > 1)) { - printf(" level = %d.\n", b->optlevel); + if (in->tetunsuitable != NULL) { + // Execute the user-defined meshing sizing evaluation. + if ((*(in->tetunsuitable))(pa, pb, pc, pd, NULL, 0)) { + // Calculate the circumcenter of this 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); + for (i = 0; i < 3; i++) ccent[i] = pd[i] + rhs[i]; + return 1; } + } - // 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); - } - } else { - // Remove the badtet from the list. - badfacedealloc(badtetrahedrons, remtet); + if (useinsertradius) { + // Do not split this tet if the shortest edge is shorter than the + // insertion radius of one of its endpoints. + triface checkedge; + point e1, e2; + REAL rrv, smrrv; + + // Get the shortest edge of this tet. + checkedge.tet = chktet->tet; + for (i = 0; i < 6; i++) { + checkedge.ver = edge2ver[i]; + e1 = org(checkedge); + e2 = dest(checkedge); + elen[i] = distance(e1, e2); + if (i == 0) { + smlen = elen[i]; + j = 0; + } else { + if (elen[i] < smlen) { + smlen = elen[i]; + j = i; } - remtet = badfacetraverse(badtetrahedrons); } - 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)); - - if (b_steinerflag) { - // This routine was called from removesteiner2(). Do not repair - // the bad tets by splitting. - badtetrahedrons->restart(); - } - - 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); - - // Form a list of slivers to be split and clean the pool. - badtetrahedrons->traversalinit(); - remtet = badfacetraverse(badtetrahedrons); - while (remtet != (badface *) NULL) { - splittetlist->append(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]; - 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); - // Is it a large angle? - if (cosdd[0] < cosobjdihed) { - slivercount++; - remtet->key = cosdd[0]; - 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 (", - 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); - } - } + } + // Check if the edge is too short. + checkedge.ver = edge2ver[j]; + // Get the smallest rrv of e1 and e2. + // Note: if rrv of e1 and e2 is zero. Do not use it. + e1 = org(checkedge); + smrrv = getpointinsradius(e1); + e2 = dest(checkedge); + rrv = getpointinsradius(e2); + if (rrv > 0) { + if (smrrv > 0) { + if (rrv < smrrv) { + smrrv = rrv; } - } // i - - delete splittetlist; - delete tetlist; - delete ceillist; + } else { + smrrv = rrv; + } } + if (smrrv > 0) { + // To avoid rounding error, round smrrv before doing comparison. + if ((fabs(smrrv - smlen) / smlen) < b->epsilon) { + smrrv = smlen; + } + if (smrrv > smlen) { + return 0; + } + } + } // if (useinsertradius) - optpasscount++; - } while ((badtetrahedrons->items > 0) && (optpasscount < b->optpasses)); - - if (b->verbose) { - if (opt_tet_peels > 0l) { - printf(" %ld tet removals.\n", opt_tet_peels); + // Check the radius-edge ratio. Set by -q#. + if (b->minratio > 0) { + // Calculate the circumcenter and radius of this 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); + for (i = 0; i < 3; i++) ccent[i] = pd[i] + rhs[i]; + rd = sqrt(dot(rhs, rhs)); + if (!useinsertradius) { + // Calculate the shortest edge length. + elen[0] = dot(vda, vda); + elen[1] = dot(vdb, vdb); + elen[2] = dot(vdc, vdc); + elen[3] = dot(vab, vab); + elen[4] = dot(vbc, vbc); + elen[5] = dot(vca, vca); + smlen = elen[0]; //sidx = 0; + for (i = 1; i < 6; i++) { + if (smlen > elen[i]) { + smlen = elen[i]; //sidx = i; + } + } + smlen = sqrt(smlen); } - if (opt_face_flips > 0l) { - printf(" %ld face flips.\n", opt_face_flips); + D = rd / smlen; + if (D > b->minratio) { + // A bad radius-edge ratio. + return 1; } - if (opt_edge_flips > 0l) { - printf(" %ld edge flips.\n", opt_edge_flips); + } + + // Check the minimum dihedral angle. Set by -qq#. + if (b->mindihedral > 0) { + // Compute the 4 face normals (N[0], ..., N[3]). + for (j = 0; j < 3; j++) { + for (i = 0; i < 3; i++) N[j][i] = 0.0; + N[j][j] = 1.0; // Positive means the inside direction + lu_solve(A, 3, indx, N[j], 0); } - if ((points->items - oldpointcount) > 0l) { - printf(" %ld point insertions", points->items - oldpointcount); - if (smoothsegverts > 0) { - printf(" (%d on segment)", smoothsegverts); - } - printf("\n"); + for (i = 0; i < 3; i++) N[3][i] = - N[0][i] - N[1][i] - N[2][i]; + // Normalize the normals. + for (i = 0; i < 4; i++) { + L[i] = sqrt(dot(N[i], N[i])); + assert(L[i] > 0); + //if (L[i] > 0.0) { + for (j = 0; j < 3; j++) N[i][j] /= L[i]; + //} + } + // Calculate the six dihedral angles. + cosd[0] = -dot(N[0], N[1]); // Edge cd, bd, bc. + cosd[1] = -dot(N[0], N[2]); + cosd[2] = -dot(N[0], N[3]); + cosd[3] = -dot(N[1], N[2]); // Edge ad, ac + cosd[4] = -dot(N[1], N[3]); + cosd[5] = -dot(N[2], N[3]); // Edge ab + // Get the smallest dihedral angle. + //maxcosd = mincosd = cosd[0]; + maxcosd = cosd[0]; + for (i = 1; i < 6; i++) { + //if (cosd[i] > maxcosd) maxcosd = cosd[i]; + maxcosd = (cosd[i] > maxcosd ? cosd[i] : maxcosd); + //mincosd = (cosd[i] < mincosd ? cosd[i] : maxcosd); + } + if (maxcosd > cosmindihed) { + // Calculate the circumcenter of this tet. + // A bad dihedral angle. + //if ((b->quality & 1) == 0) { + 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); + for (i = 0; i < 3; i++) ccent[i] = pd[i] + rhs[i]; + //*rd = sqrt(dot(rhs, rhs)); + //} + return 1; } } - delete badtetrahedrons; - badtetrahedrons = (memorypool *) NULL; + return 0; } -//// //// -//// //// -//// optimize_cxx ///////////////////////////////////////////////////////////// - -//// output_cxx /////////////////////////////////////////////////////////////// -//// //// -//// //// - /////////////////////////////////////////////////////////////////////////////// // // -// jettisonnodes() Jettison unused or duplicated vertices. // -// // -// Unused points are those input points which are outside the mesh domain or // -// have no connection (isolated) to the mesh. Duplicated points exist for // -// example if the input PLC is read from a .stl mesh file (marked during the // -// Delaunay tetrahedralization step. This routine remove these points from // -// points list. All existing points are reindexed. // +// splittetrahedron() Split a tetrahedron. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::jettisonnodes() +int tetgenmesh::splittetrahedron(triface* splittet, int qflag, REAL *ccent, + int chkencflag) { - point pointloop; - bool jetflag; - int oldidx, newidx; - int remcount; + triface searchtet; + face *paryseg; + point newpt; + badface *bface; + insertvertexflags ivf; + int splitflag; + int i; - if (!b->quiet) { - printf("Jettisoning redundants points.\n"); + + + REAL rv = 0.; // Insertion radius of 'newpt'. + + makepoint(&newpt, FREEVOLVERTEX); + for (i = 0; i < 3; i++) newpt[i] = ccent[i]; + + if (useinsertradius) { + rv = distance(newpt, org(*splittet)); + setpointinsradius(newpt, rv); } - points->traversalinit(); - pointloop = pointtraverse(); - oldidx = newidx = 0; // in->firstnumber; - remcount = 0; - while (pointloop != (point) NULL) { - jetflag = (pointtype(pointloop) == DUPLICATEDVERTEX) || - (pointtype(pointloop) == UNUSEDVERTEX); - if (jetflag) { - // It is a duplicated point, delete it. - pointdealloc(pointloop); - remcount++; - } else { - // Re-index it. - setpointmark(pointloop, newidx + in->firstnumber); - if (in->pointmarkerlist != (int *) NULL) { - if (oldidx < in->numberofpoints) { - // Re-index the point marker as well. - in->pointmarkerlist[newidx] = in->pointmarkerlist[oldidx]; + searchtet = *splittet; + ivf.iloc = (int) OUTSIDE; + // Use Bowyer-Watson algorithm. Preserve subsegments and subfaces; + ivf.bowywat = 3; + ivf.lawson = 2; + ivf.rejflag = 3; // Do check for encroached segments and subfaces. + if (b->metric) { + ivf.rejflag |= 4; // Reject it if it lies in some protecting balls. + } + ivf.chkencflag = chkencflag; + ivf.sloc = ivf.sbowywat = 0; // No use. + ivf.splitbdflag = 0; // No use. + ivf.validflag = 1; + ivf.respectbdflag = 1; + ivf.assignmeshsize = b->metric; + + ivf.refineflag = 1; + ivf.refinetet = *splittet; + + + if (insertpoint(newpt, &searchtet, NULL, NULL, &ivf)) { + // Vertex is inserted. + st_volref_count++; + if (steinerleft > 0) steinerleft--; + if (flipstack != NULL) { + flipconstraints fc; + fc.chkencflag = chkencflag; + fc.enqflag = 2; + lawsonflip3d(&fc); + unflipqueue->restart(); + } + return 1; + } else { + // Point is not inserted. + pointdealloc(newpt); + // Check if there are encroached segments/subfaces. + if (ivf.iloc == (int) ENCSEGMENT) { + splitflag = 0; + //if (!b->nobisect) { // not -Y option + if (!b->nobisect || checkconstraints) { + // Select an encroached segment and split it. + for (i = 0; i < encseglist->objects; i++) { + paryseg = (face *) fastlookup(encseglist, i); + if (splitsegment(paryseg, NULL, rv, org(*splittet), NULL, qflag, + chkencflag | 3)) { + splitflag = 1; // A point is inserted on a segment. + break; + } + } + } // if (!b->nobisect) + encseglist->restart(); + if (splitflag) { + // Some segments may need to be repaired. + repairencsegs(chkencflag | 3); + // Some subfaces may need to be repaired. + repairencfacs(chkencflag | 2); + // Queue the tet if it is still alive and not queued. + if ((splittet->tet != NULL) && (splittet->tet[4] != NULL)) { + enqueuetetrahedron(splittet); + } + } + return splitflag; + } else if (ivf.iloc == (int) ENCSUBFACE) { + splitflag = 0; + //if (!b->nobisect) { // not -Y option + if (!b->nobisect || checkconstraints) { + // Select an encroached subface and split it. + for (i = 0; i < encshlist->objects; i++) { + bface = (badface *) fastlookup(encshlist, i); + if (splitsubface(&(bface->ss), NULL, org(*splittet), qflag, + bface->cent, chkencflag | 2)){ + splitflag = 1; // A point is inserted on a subface or a segment. + break; + } + } + } // if (!b->nobisect) + encshlist->restart(); + if (splitflag) { + assert(badsubsegs->items == 0l); + // Some subfaces may need to be repaired. + repairencfacs(chkencflag | 2); + // Queue the tet if it is still alive. + if ((splittet->tet != NULL) && (splittet->tet[4] != NULL)) { + enqueuetetrahedron(splittet); } } - newidx++; - } - oldidx++; - if (oldidx == in->numberofpoints) { - // Update the numbe of input points (Because some were removed). - in->numberofpoints -= remcount; - // Remember this number for output original input nodes. - jettisoninverts = remcount; + return splitflag; } - pointloop = pointtraverse(); - } - if (b->verbose) { - printf(" %d duplicated vertices have been removed.\n", dupverts); - printf(" %d unused vertices have been removed.\n", unuverts); + return 0; } - dupverts = 0; - unuverts = 0; - - // The following line ensures that dead items in the pool of nodes cannot - // be allocated for the new created nodes. This ensures that the input - // nodes will occur earlier in the output files, and have lower indices. - points->deaditemstack = (void *) NULL; } /////////////////////////////////////////////////////////////////////////////// // // -// highorder() Create extra nodes for quadratic subparametric elements. // -// // -// 'highordertable' is an array (size = numberoftetrahedra * 6) for storing // -// high-order nodes of each tetrahedron. This routine is used only when -o2 // -// switch is used. // +// repairbadtets() Repair bad quality tetrahedra. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::highorder() +void tetgenmesh::repairbadtets(int chkencflag) { - triface tetloop, worktet; - triface spintet, adjtet; - point torg, tdest, tapex; - point *extralist, *adjextralist; - point newpoint; - int hitbdry, ptmark; - int i, j; - - if (!b->quiet) { - printf("Adding vertices for second-order tetrahedra.\n"); - } - - // Initialize the 'highordertable'. - highordertable = new point[tetrahedrons->items * 6]; - if (highordertable == (point *) NULL) { - terminatetetgen(1); - } + triface *bface; + REAL ccent[3]; + int qflag = 0; - // The following line ensures that dead items in the pool of nodes cannot - // be allocated for the extra nodes associated with high order elements. - // This ensures that the primary nodes (at the corners of elements) will - // occur earlier in the output files, and have lower indices, than the - // extra nodes. - points->deaditemstack = (void *) NULL; - // Assign an entry for each tetrahedron to find its extra nodes. At the - // mean while, initialize all extra nodes be NULL. - i = 0; - tetrahedrons->traversalinit(); - tetloop.tet = tetrahedrontraverse(); - while (tetloop.tet != (tetrahedron *) NULL) { - tetloop.tet[highorderindex] = (tetrahedron) &highordertable[i]; - for (j = 0; j < 6; j++) { - highordertable[i + j] = (point) NULL; + // Loop until the pool 'badsubfacs' is empty. Note that steinerleft == -1 + // if an unlimited number of Steiner points is allowed. + while ((badtetrahedrons->items > 0) && (steinerleft != 0)) { + badtetrahedrons->traversalinit(); + bface = (triface *) badtetrahedrons->traverse(); + while ((bface != NULL) && (steinerleft != 0)) { + // Skip a deleted element. + if (bface->ver >= 0) { + // A queued tet may have been deleted. + if (!isdeadtet(*bface)) { + // A queued tet may have been processed. + if (marktest2ed(*bface)) { + unmarktest2(*bface); + if (checktet4split(bface, qflag, ccent)) { + splittetrahedron(bface, qflag, ccent, chkencflag); + } + } + } + bface->ver = -1; // Signal it as a deleted element. + badtetrahedrons->dealloc((void *) bface); + } + bface = (triface *) badtetrahedrons->traverse(); } - i += 6; - tetloop.tet = tetrahedrontraverse(); } - // To create a unique node on each edge. Loop over all tetrahedra, and - // look at the six edges of each tetrahedron. If the extra node in - // the tetrahedron corresponding to this edge is NULL, create a node - // for this edge, at the same time, set the new node into the extra - // node lists of all other tetrahedra sharing this edge. - tetrahedrons->traversalinit(); - tetloop.tet = tetrahedrontraverse(); - while (tetloop.tet != (tetrahedron *) NULL) { - // Get the list of extra nodes. - extralist = (point *) tetloop.tet[highorderindex]; - worktet.tet = tetloop.tet; - for (i = 0; i < 6; i++) { - if (extralist[i] == (point) NULL) { - // Operate on this edge. - worktet.loc = edge2locver[i][0]; - worktet.ver = edge2locver[i][1]; - // Create a new node on this edge. - torg = org(worktet); - tdest = dest(worktet); - // Create a new node in the middle of the edge. - newpoint = (point) points->alloc(); - // Interpolate its attributes. - for (j = 0; j < 3 + in->numberofpointattributes; j++) { - newpoint[j] = 0.5 * (torg[j] + tdest[j]); - } - ptmark = (int) points->items - (in->firstnumber == 1 ? 0 : 1); - setpointmark(newpoint, ptmark); - // Add this node to its extra node list. - extralist[i] = newpoint; - // Set 'newpoint' into extra node lists of other tetrahedra - // sharing this edge. - tapex = apex(worktet); - spintet = worktet; - hitbdry = 0; - while (hitbdry < 2) { - if (fnextself(spintet)) { - // Get the extra node list of 'spintet'. - adjextralist = (point *) spintet.tet[highorderindex]; - // Find the index of its extra node list. - j = locver2edge[spintet.loc][spintet.ver]; - // Only set 'newpoint' into 'adjextralist' if it is a NULL. - // Because two faces can belong to the same tetrahedron. - if (adjextralist[j] == (point) NULL) { - adjextralist[j] = newpoint; - } - if (apex(spintet) == tapex) { - break; - } - } else { - hitbdry++; - if (hitbdry < 2) { - esym(worktet, spintet); - } + if (badtetrahedrons->items > 0) { + if (steinerleft == 0) { + if (b->verbose) { + printf("The desired number of Steiner points is reached.\n"); + } + } else { + assert(0); // Unknown case. + } + // Unmark all queued tet. + badtetrahedrons->traversalinit(); + bface = (triface *) badtetrahedrons->traverse(); + while (bface != NULL) { + // Skip a deleted element. + if (bface->ver >= 0) { + if (!isdeadtet(*bface)) { + if (marktest2ed(*bface)) { + unmarktest2(*bface); } } } + bface = (triface *) badtetrahedrons->traverse(); } - tetloop.tet = tetrahedrontraverse(); + // Clear the pool. + badtetrahedrons->restart(); } } /////////////////////////////////////////////////////////////////////////////// // // -// 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. // +// delaunayrefinement() Refine the mesh by Delaunay refinement. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::numberedges() +void tetgenmesh::delaunayrefinement() { - triface tetloop, worktet, spintet; - int hitbdry, i; + triface checktet; + face checksh; + face checkseg; + long steinercount; + int chkencflag; - 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; + long bak_segref_count, bak_facref_count, bak_volref_count; + long bak_flipcount = flip23count + flip32count + flip44count; + + if (!b->quiet) { + printf("Refining mesh...\n"); } - 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++; + if (b->verbose) { + printf(" Min radiu-edge ratio = %g.\n", b->minratio); + printf(" Min dihedral angle = %g.\n", b->mindihedral); + //printf(" Min Edge length = %g.\n", b->minedgelength); + } + + steinerleft = b->steinerleft; // Upperbound of # Steiner points (by -S#). + if (steinerleft > 0) { + // Check if we've already used up the given number of Steiner points. + steinercount = st_segref_count + st_facref_count + st_volref_count; + if (steinercount < steinerleft) { + steinerleft -= steinercount; + } else { + if (!b->quiet) { + printf("\nWarning: "); + printf("The desired number of Steiner points (%d) has reached.\n\n", + b->steinerleft); } + return; // No more Steiner points. } - tetloop.tet = tetrahedrontraverse(); } -} -/////////////////////////////////////////////////////////////////////////////// -// // -// outnodes() Output the points to a .node file or a tetgenio structure. // -// // -// Note: each point has already been numbered on input (the first index is // -// 'in->firstnumber'). // -// // -/////////////////////////////////////////////////////////////////////////////// + if (useinsertradius) { + if ((b->plc && b->nobisect) || b->refine) { // '-pY' or '-r' option. + makesegmentendpointsmap(); + } + makefacetverticesmap(); + } -void tetgenmesh::outnodes(tetgenio* out) -{ - FILE *outfile; - char outnodefilename[FILENAMESIZE]; - shellface subptr; - triface adjtet; - face subloop; - point pointloop; - point *extralist, ep[3]; - int nextras, bmark, shmark, marker; - int coordindex, attribindex; - int pointnumber, firstindex; - int index, i; - if (out == (tetgenio *) NULL) { - strcpy(outnodefilename, b->outfilename); - strcat(outnodefilename, ".node"); - } - - if (!b->quiet) { - if (out == (tetgenio *) NULL) { - printf("Writing %s.\n", outnodefilename); - } else { - printf("Writing nodes.\n"); + encseglist = new arraypool(sizeof(face), 8); + encshlist = new arraypool(sizeof(badface), 8); + + + //if (!b->nobisect) { // if no '-Y' option + if (!b->nobisect || checkconstraints) { + if (b->verbose) { + printf(" Splitting encroached subsegments.\n"); } - } - nextras = in->numberofpointattributes; - bmark = !b->nobound && in->pointmarkerlist; + chkencflag = 1; // Only check encroaching subsegments. + steinercount = points->items; - // Avoid compile warnings. - outfile = (FILE *) NULL; - marker = coordindex = 0; + // Initialize the pool of encroached subsegments. + badsubsegs = new memorypool(sizeof(face), b->shellfaceperblock, + sizeof(void *), 0); - if (out == (tetgenio *) NULL) { - outfile = fopen(outnodefilename, "w"); - if (outfile == (FILE *) NULL) { - printf("File I/O Error: Cannot create file %s.\n", outnodefilename); - terminatetetgen(3); + // Add all segments into the pool. + subsegs->traversalinit(); + checkseg.sh = shellfacetraverse(subsegs); + while (checkseg.sh != (shellface *) NULL) { + enqueuesubface(badsubsegs, &checkseg); + checkseg.sh = shellfacetraverse(subsegs); } - // Number of points, number of dimensions, number of point attributes, - // and number of boundary markers (zero or one). - fprintf(outfile, "%ld %d %d %d\n", points->items, 3, nextras, bmark); - } else { - // Allocate space for 'pointlist'; - out->pointlist = new REAL[points->items * 3]; - if (out->pointlist == (REAL *) NULL) { - terminatetetgen(1); + + // Split all encroached segments. + repairencsegs(chkencflag); + + if (b->verbose) { + printf(" Added %ld Steiner points.\n", points->items - steinercount); } - // Allocate space for 'pointattributelist' if necessary; - if (nextras > 0) { - out->pointattributelist = new REAL[points->items * nextras]; - if (out->pointattributelist == (REAL *) NULL) { - terminatetetgen(1); + + if (b->reflevel > 1) { // '-D2' option + if (b->verbose) { + printf(" Splitting encroached subfaces.\n"); } - } - // Allocate space for 'pointmarkerlist' if necessary; - if (bmark) { - out->pointmarkerlist = new int[points->items]; - if (out->pointmarkerlist == (int *) NULL) { - terminatetetgen(1); + + chkencflag = 2; // Only check encroaching subfaces. + steinercount = points->items; + bak_segref_count = st_segref_count; + bak_facref_count = st_facref_count; + + // Initialize the pool of encroached subfaces. + badsubfacs = new memorypool(sizeof(face), b->shellfaceperblock, + sizeof(void *), 0); + + // Add all subfaces into the pool. + subfaces->traversalinit(); + checksh.sh = shellfacetraverse(subfaces); + while (checksh.sh != (shellface *) NULL) { + enqueuesubface(badsubfacs, &checksh); + checksh.sh = shellfacetraverse(subfaces); + } + + // Split all encroached subfaces. + repairencfacs(chkencflag); + + if (b->verbose) { + printf(" Added %ld (%ld,%ld) Steiner points.\n", + points->items-steinercount, st_segref_count-bak_segref_count, + st_facref_count-bak_facref_count); } + } // if (b->reflevel > 1) + } // if (!b->nobisect) + + if (b->reflevel > 2) { // '-D3' option (The default option) + if (b->verbose) { + printf(" Splitting bad quality tets.\n"); } - out->numberofpoints = points->items; - out->numberofpointattributes = nextras; - coordindex = 0; - attribindex = 0; - } - if (bmark && (b->plc || b->refine)) { - // Initialize the point2tet field of each point. - points->traversalinit(); - pointloop = pointtraverse(); - while (pointloop != (point) NULL) { - setpoint2tet(pointloop, (tetrahedron) NULL); - pointloop = pointtraverse(); + chkencflag = 4; // Only check tetrahedra. + steinercount = points->items; + bak_segref_count = st_segref_count; + bak_facref_count = st_facref_count; + bak_volref_count = st_volref_count; + + // The cosine value of the min dihedral angle (-qq) for tetrahedra. + cosmindihed = cos(b->mindihedral / 180.0 * PI); + + // Initialize the pool of bad quality tetrahedra. + badtetrahedrons = new memorypool(sizeof(triface), b->tetrahedraperblock, + sizeof(void *), 0); + // Add all tetrahedra (no hull tets) into the pool. + tetrahedrons->traversalinit(); + checktet.tet = tetrahedrontraverse(); + while (checktet.tet != NULL) { + enqueuetetrahedron(&checktet); + checktet.tet = tetrahedrontraverse(); } - // Make a map point-to-subface. Hence a boundary point will get the - // facet marker from that facet where it lies on. - subfaces->traversalinit(); - subloop.sh = shellfacetraverse(subfaces); - while (subloop.sh != (shellface *) NULL) { - subloop.shver = 0; - // Check all three points of the subface. - for (i = 0; i < 3; i++) { - pointloop = (point) subloop.sh[3 + i]; - setpoint2tet(pointloop, (tetrahedron) sencode(subloop)); - } - if (b->order == 2) { - // '-o2' switch. Set markers for quadratic nodes of this subface. - stpivot(subloop, adjtet); - if (adjtet.tet == dummytet) { - sesymself(subloop); - stpivot(subloop, adjtet); - } - assert(adjtet.tet != dummytet); - extralist = (point *) adjtet.tet[highorderindex]; - switch (adjtet.loc) { - case 0: - ep[0] = extralist[0]; - ep[1] = extralist[1]; - ep[2] = extralist[2]; - break; - case 1: - ep[0] = extralist[0]; - ep[1] = extralist[4]; - ep[2] = extralist[3]; - break; - case 2: - ep[0] = extralist[1]; - ep[1] = extralist[5]; - ep[2] = extralist[4]; - break; - case 3: - ep[0] = extralist[2]; - ep[1] = extralist[3]; - ep[2] = extralist[5]; - break; - default: break; - } - for (i = 0; i < 3; i++) { - setpoint2tet(ep[i], (tetrahedron) sencode(subloop)); - } - } - subloop.sh = shellfacetraverse(subfaces); - } - } - // Determine the first index (0 or 1). - firstindex = b->zeroindex ? 0 : in->firstnumber; + // Split all bad quality tetrahedra. + repairbadtets(chkencflag); - points->traversalinit(); - pointloop = pointtraverse(); - pointnumber = firstindex; // in->firstnumber; - index = 0; - while (pointloop != (point) NULL) { - if (bmark) { - // Default the vertex has a zero marker. - marker = 0; - // Is it an input vertex? - if (index < in->numberofpoints) { - // Input point's marker is directly copied to output. - marker = in->pointmarkerlist[index]; - } - // Is it a boundary vertex has marker zero? - if ((marker == 0) && (b->plc || b->refine)) { - subptr = (shellface) point2tet(pointloop); - if (subptr != (shellface) NULL) { - // Default a boundary vertex has marker 1. - marker = 1; - if (in->facetmarkerlist != (int *) NULL) { - // The vertex gets the marker from the facet it lies on. - sdecode(subptr, subloop); - shmark = shellmark(subloop); - marker = in->facetmarkerlist[shmark - 1]; - } - } - } + if (b->verbose) { + printf(" Added %ld (%ld,%ld,%ld) Steiner points.\n", + points->items - steinercount, + st_segref_count - bak_segref_count, + st_facref_count - bak_facref_count, + st_volref_count - bak_volref_count); } - if (out == (tetgenio *) NULL) { - // Point number, x, y and z coordinates. - fprintf(outfile, "%4d %.17g %.17g %.17g", pointnumber, - pointloop[0], pointloop[1], pointloop[2]); - for (i = 0; i < nextras; i++) { - // Write an attribute. - fprintf(outfile, " %.17g", pointloop[3 + i]); - } - if (bmark) { - // Write the boundary marker. - fprintf(outfile, " %d", marker); - } - fprintf(outfile, "\n"); - } else { - // X, y, and z coordinates. - out->pointlist[coordindex++] = pointloop[0]; - out->pointlist[coordindex++] = pointloop[1]; - out->pointlist[coordindex++] = pointloop[2]; - // Point attributes. - for (i = 0; i < nextras; i++) { - // Output an attribute. - out->pointattributelist[attribindex++] = pointloop[3 + i]; - } - if (bmark) { - // Output the boundary marker. - out->pointmarkerlist[index] = marker; - } + } // if (b->reflevel > 2) + + if (b->verbose) { + if (flip23count + flip32count + flip44count > bak_flipcount) { + printf(" Performed %ld flips.\n", flip23count + flip32count + + flip44count - bak_flipcount); } - pointloop = pointtraverse(); - pointnumber++; - index++; } - if (out == (tetgenio *) NULL) { - fprintf(outfile, "# Generated by %s\n", b->commandline); - fclose(outfile); + if (steinerleft == 0) { + if (!b->quiet) { + printf("\nWarnning: "); + printf("The desired number of Steiner points (%d) is reached.\n\n", + b->steinerleft); + } + } + + + delete encseglist; + delete encshlist; + + //if (!b->nobisect) { + if (!b->nobisect || checkconstraints) { + totalworkmemory += (badsubsegs->maxitems * badsubsegs->itembytes); + delete badsubsegs; + if (b->reflevel > 1) { + totalworkmemory += (badsubfacs->maxitems * badsubfacs->itembytes); + delete badsubfacs; + } + } + if (b->reflevel > 2) { + totalworkmemory += (badtetrahedrons->maxitems*badtetrahedrons->itembytes); + delete badtetrahedrons; } } +//// //// +//// //// +//// refine_cxx /////////////////////////////////////////////////////////////// + +//// optimize_cxx ///////////////////////////////////////////////////////////// +//// //// +//// //// + /////////////////////////////////////////////////////////////////////////////// // // -// outmetrics() Output the metric to a file (*.mtr) or a tetgenio obj. // +// lawsonflip3d() A three-dimensional Lawson's algorithm. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::outmetrics(tetgenio* out) +long tetgenmesh::lawsonflip3d(flipconstraints *fc) { - FILE *outfile; - char outmtrfilename[FILENAMESIZE]; - list *tetlist, *ptlist; - triface tetloop; - point ptloop, neipt; - REAL lave, len; // lmin, lmax, - int mtrindex; - int i; + triface fliptets[5], neightet, hulltet; + face checksh, casingout; + badface *popface, *bface; + point pd, pe, *pts; + REAL sign, ori; + long flipcount, totalcount = 0l; + long sliver_peels = 0l; + int t1ver; + int i; - if (out == (tetgenio *) NULL) { - strcpy(outmtrfilename, b->outfilename); - strcat(outmtrfilename, ".mtr"); - } - if (!b->quiet) { - if (out == (tetgenio *) NULL) { - printf("Writing %s.\n", outmtrfilename); - } else { - printf("Writing metrics.\n"); + while (1) { + + if (b->verbose > 2) { + printf(" Lawson flip %ld faces.\n", flippool->items); } - } + flipcount = 0l; - // Avoid compile warnings. - outfile = (FILE *) NULL; - mtrindex = 0; + while (flipstack != (badface *) NULL) { + // Pop a face from the stack. + popface = flipstack; + fliptets[0] = popface->tt; + flipstack = flipstack->nextitem; // The next top item in stack. + flippool->dealloc((void *) popface); - if (out == (tetgenio *) NULL) { - outfile = fopen(outmtrfilename, "w"); - if (outfile == (FILE *) NULL) { - printf("File I/O Error: Cannot create file %s.\n", outmtrfilename); - terminatetetgen(3); - } - // Number of points, number of point metrices, - // fprintf(outfile, "%ld %d\n", points->items, sizeoftensor + 3); - fprintf(outfile, "%ld %d\n", points->items, 1); - } else { - // Allocate space for 'pointmtrlist' if necessary; - // out->pointmtrlist = new REAL[points->items * (sizeoftensor + 3)]; - out->pointmtrlist = new REAL[points->items]; - if (out->pointmtrlist == (REAL *) NULL) { - terminatetetgen(1); - } - out->numberofpointmtrs = 1; // (sizeoftensor + 3); - mtrindex = 0; - } - - // Initialize the point2tet field of each point. - points->traversalinit(); - ptloop = pointtraverse(); - while (ptloop != (point) NULL) { - setpoint2tet(ptloop, (tetrahedron) NULL); - ptloop = pointtraverse(); - } - // Create the point-to-tet map. - tetrahedrons->traversalinit(); - tetloop.tet = tetrahedrontraverse(); - while (tetloop.tet != (tetrahedron *) NULL) { - for (i = 0; i < 4; i++) { - ptloop = (point) tetloop.tet[4 + i]; - setpoint2tet(ptloop, encode(tetloop)); - } - tetloop.tet = tetrahedrontraverse(); - } + // Skip it if it is a dead tet (destroyed by previous flips). + if (isdeadtet(fliptets[0])) continue; + // Skip it if it is not the same tet as we saved. + if (!facemarked(fliptets[0])) continue; - tetlist = new list(sizeof(triface), NULL, 256); - ptlist = new list(sizeof(point *), NULL, 256); + unmarkface(fliptets[0]); - points->traversalinit(); - ptloop = pointtraverse(); - while (ptloop != (point) NULL) { - decode(point2tet(ptloop), tetloop); - if (!isdead(&tetloop)) { - // Form the star of p. - tetlist->append(&tetloop); - formstarpolyhedron(ptloop, tetlist, ptlist, true); - // lmin = longest; - // lmax = 0.0; - lave = 0.0; - for (i = 0; i < ptlist->len(); i++) { - neipt = * (point *)(* ptlist)[i]; - len = distance(ptloop, neipt); - // lmin = lmin < len ? lmin : len; - // lmax = lmax > len ? lmax : len; - lave += len; - } - lave /= ptlist->len(); - } - if (out == (tetgenio *) NULL) { - // for (i = 0; i < sizeoftensor; i++) { - // fprintf(outfile, "%-16.8e ", ptloop[pointmtrindex + i]); - // } - if (ptlist->len() > 0) { - // fprintf(outfile, "%-16.8e %-16.8e %-16.8e", lmin, lmax, lave); - fprintf(outfile, "%-16.8e ", lave); - } else { - fprintf(outfile, "0.0 "); // fprintf(outfile, "0.0 0.0 0.0"); + if (ishulltet(fliptets[0])) continue; + + fsym(fliptets[0], fliptets[1]); + if (ishulltet(fliptets[1])) { + if (nonconvex) { + // Check if 'fliptets[0]' it is a hull sliver. + tspivot(fliptets[0], checksh); + for (i = 0; i < 3; i++) { + if (!isshsubseg(checksh)) { + spivot(checksh, casingout); + //assert(casingout.sh != NULL); + if (sorg(checksh) != sdest(casingout)) sesymself(casingout); + stpivot(casingout, neightet); + if (neightet.tet == fliptets[0].tet) { + // Found a hull sliver 'neightet'. Let it be [e,d,a,b], where + // [e,d,a] and [d,e,b] are hull faces. + edestoppo(neightet, hulltet); // [a,b,e,d] + fsymself(hulltet); // [b,a,e,#] + if (oppo(hulltet) == dummypoint) { + pe = org(neightet); + if ((pointtype(pe) == FREEFACETVERTEX) || + (pointtype(pe) == FREESEGVERTEX)) { + removevertexbyflips(pe); + } + } else { + eorgoppo(neightet, hulltet); // [b,a,d,e] + fsymself(hulltet); // [a,b,d,#] + if (oppo(hulltet) == dummypoint) { + pd = dest(neightet); + if ((pointtype(pd) == FREEFACETVERTEX) || + (pointtype(pd) == FREESEGVERTEX)) { + removevertexbyflips(pd); + } + } else { + // Perform a 3-to-2 flip to remove the sliver. + fliptets[0] = neightet; // [e,d,a,b] + fnext(fliptets[0], fliptets[1]); // [e,d,b,c] + fnext(fliptets[1], fliptets[2]); // [e,d,c,a] + flip32(fliptets, 1, fc); + // Update counters. + flip32count--; + flip22count--; + sliver_peels++; + if (fc->remove_ndelaunay_edge) { + // Update the volume (must be decreased). + //assert(fc->tetprism_vol_sum <= 0); + tetprism_vol_sum += fc->tetprism_vol_sum; + fc->tetprism_vol_sum = 0.0; // Clear it. + } + } + } + break; + } // if (neightet.tet == fliptets[0].tet) + } // if (!isshsubseg(checksh)) + senextself(checksh); + } // i + } // if (nonconvex) + continue; } - fprintf(outfile, "\n"); - } else { - // for (i = 0; i < sizeoftensor; i++) { - // out->pointmtrlist[mtrindex++] = ptloop[pointmtrindex + i]; - // } - if (ptlist->len() > 0) { - // out->pointmtrlist[mtrindex++] = lmin; - // out->pointmtrlist[mtrindex++] = lmax; - out->pointmtrlist[mtrindex++] = lave; - } else { - // out->pointmtrlist[mtrindex++] = 0.0; - // out->pointmtrlist[mtrindex++] = 0.0; - out->pointmtrlist[mtrindex++] = 0.0; + + if (checksubfaceflag) { + // Do not flip if it is a subface. + if (issubface(fliptets[0])) continue; + } + + // Test whether the face is locally Delaunay or not. + pts = (point *) fliptets[1].tet; + sign = insphere_s(pts[4], pts[5], pts[6], pts[7], oppo(fliptets[0])); + + if (sign < 0) { + // A non-Delaunay face. Try to flip it. + pd = oppo(fliptets[0]); + pe = oppo(fliptets[1]); + + // Check the convexity of its three edges. Stop checking either a + // locally non-convex edge (ori < 0) or a flat edge (ori = 0) is + // encountered, and 'fliptet' represents that edge. + for (i = 0; i < 3; i++) { + ori = orient3d(org(fliptets[0]), dest(fliptets[0]), pd, pe); + if (ori <= 0) break; + enextself(fliptets[0]); + } + + if (ori > 0) { + // A 2-to-3 flip is found. + // [0] [a,b,c,d], + // [1] [b,a,c,e]. no dummypoint. + flip23(fliptets, 0, fc); + flipcount++; + if (fc->remove_ndelaunay_edge) { + // Update the volume (must be decreased). + //assert(fc->tetprism_vol_sum <= 0); + tetprism_vol_sum += fc->tetprism_vol_sum; + fc->tetprism_vol_sum = 0.0; // Clear it. + } + continue; + } else { // ori <= 0 + // The edge ('fliptets[0]' = [a',b',c',d]) is non-convex or flat, + // where the edge [a',b'] is one of [a,b], [b,c], and [c,a]. + if (checksubsegflag) { + // Do not flip if it is a segment. + if (issubseg(fliptets[0])) continue; + } + // Check if there are three or four tets sharing at this edge. + esymself(fliptets[0]); // [b,a,d,c] + for (i = 0; i < 3; i++) { + fnext(fliptets[i], fliptets[i+1]); + } + if (fliptets[3].tet == fliptets[0].tet) { + // A 3-to-2 flip is found. (No hull tet.) + flip32(fliptets, 0, fc); + flipcount++; + if (fc->remove_ndelaunay_edge) { + // Update the volume (must be decreased). + //assert(fc->tetprism_vol_sum <= 0); + tetprism_vol_sum += fc->tetprism_vol_sum; + fc->tetprism_vol_sum = 0.0; // Clear it. + } + continue; + } else { + // There are more than 3 tets at this edge. + fnext(fliptets[3], fliptets[4]); + if (fliptets[4].tet == fliptets[0].tet) { + // There are exactly 4 tets at this edge. + if (nonconvex) { + if (apex(fliptets[3]) == dummypoint) { + // This edge is locally non-convex on the hull. + // It can be removed by a 4-to-4 flip. + ori = 0; + } + } // if (nonconvex) + if (ori == 0) { + // A 4-to-4 flip is found. (Two hull tets may be involved.) + // Current tets in 'fliptets': + // [0] [b,a,d,c] (d may be newpt) + // [1] [b,a,c,e] + // [2] [b,a,e,f] (f may be dummypoint) + // [3] [b,a,f,d] + esymself(fliptets[0]); // [a,b,c,d] + // A 2-to-3 flip replaces face [a,b,c] by edge [e,d]. + // This creates a degenerate tet [e,d,a,b] (tmpfliptets[0]). + // It will be removed by the followed 3-to-2 flip. + flip23(fliptets, 0, fc); // No hull tet. + fnext(fliptets[3], fliptets[1]); + fnext(fliptets[1], fliptets[2]); + // Current tets in 'fliptets': + // [0] [...] + // [1] [b,a,d,e] (degenerated, d may be new point). + // [2] [b,a,e,f] (f may be dummypoint) + // [3] [b,a,f,d] + // A 3-to-2 flip replaces edge [b,a] by face [d,e,f]. + // Hull tets may be involved (f may be dummypoint). + flip32(&(fliptets[1]), (apex(fliptets[3]) == dummypoint), fc); + flipcount++; + flip23count--; + flip32count--; + flip44count++; + if (fc->remove_ndelaunay_edge) { + // Update the volume (must be decreased). + //assert(fc->tetprism_vol_sum <= 0); + tetprism_vol_sum += fc->tetprism_vol_sum; + fc->tetprism_vol_sum = 0.0; // Clear it. + } + continue; + } // if (ori == 0) + } + } + } // if (ori <= 0) + + // This non-Delaunay face is unflippable. Save it. + unflipqueue->newindex((void **) &bface); + bface->tt = fliptets[0]; + bface->forg = org(fliptets[0]); + bface->fdest = dest(fliptets[0]); + bface->fapex = apex(fliptets[0]); + } // if (sign < 0) + } // while (flipstack) + + if (b->verbose > 2) { + if (flipcount > 0) { + printf(" Performed %ld flips.\n", flipcount); } } - tetlist->clear(); - ptlist->clear(); - ptloop = pointtraverse(); - } + // Accumulate the counter of flips. + totalcount += flipcount; - delete tetlist; - delete ptlist; + assert(flippool->items == 0l); + // Return if no unflippable faces left. + if (unflipqueue->objects == 0l) break; + // Return if no flip has been performed. + if (flipcount == 0l) break; - if (out == (tetgenio *) NULL) { - fprintf(outfile, "# Generated by %s\n", b->commandline); - fclose(outfile); + // Try to flip the unflippable faces. + for (i = 0; i < unflipqueue->objects; i++) { + bface = (badface *) fastlookup(unflipqueue, i); + if (!isdeadtet(bface->tt) && + (org(bface->tt) == bface->forg) && + (dest(bface->tt) == bface->fdest) && + (apex(bface->tt) == bface->fapex)) { + flippush(flipstack, &(bface->tt)); + } + } + unflipqueue->restart(); + + } // while (1) + + if (b->verbose > 2) { + if (totalcount > 0) { + printf(" Performed %ld flips.\n", totalcount); + } + if (sliver_peels > 0) { + printf(" Removed %ld hull slivers.\n", sliver_peels); + } + if (unflipqueue->objects > 0l) { + printf(" %ld unflippable edges remained.\n", unflipqueue->objects); + } } + + return totalcount + sliver_peels; } /////////////////////////////////////////////////////////////////////////////// // // -// outelements() Output the tetrahedra to an .ele file or a tetgenio // -// structure. // +// recoverdelaunay() Recovery the locally Delaunay property. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::outelements(tetgenio* out) +void tetgenmesh::recoverdelaunay() { - FILE *outfile; - char outelefilename[FILENAMESIZE]; - tetrahedron* tptr; - triface worktet, spintet; - int *tlist; - REAL *talist; - int firstindex, shift; - int pointindex; - int attribindex; - point p1, p2, p3, p4; - point *extralist; - int elementnumber; - int eextras; - int hitbdry, i; - - if (out == (tetgenio *) NULL) { - strcpy(outelefilename, b->outfilename); - strcat(outelefilename, ".ele"); - } + arraypool *flipqueue, *nextflipqueue, *swapqueue; + triface tetloop, neightet, *parytet; + badface *bface, *parybface; + point *ppt; + flipconstraints fc; + int i, j; if (!b->quiet) { - if (out == (tetgenio *) NULL) { - printf("Writing %s.\n", outelefilename); - } else { - printf("Writing elements.\n"); - } + printf("Recovering Delaunayness...\n"); } - // Avoid compile warnings. - outfile = (FILE *) NULL; - tlist = (int *) NULL; - talist = (double *) NULL; - pointindex = attribindex = 0; + tetprism_vol_sum = 0.0; // Initialize it. - eextras = in->numberoftetrahedronattributes; - if (out == (tetgenio *) NULL) { - outfile = fopen(outelefilename, "w"); - if (outfile == (FILE *) NULL) { - printf("File I/O Error: Cannot create file %s.\n", outelefilename); - terminatetetgen(3); - } - // Number of tetras, points per tetra, attributes per tetra. - fprintf(outfile, "%ld %d %d\n", tetrahedrons->items, - b->order == 1 ? 4 : 10, eextras); - } else { - // Allocate memory for output tetrahedra. - out->tetrahedronlist = new int[tetrahedrons->items * - (b->order == 1 ? 4 : 10)]; - if (out->tetrahedronlist == (int *) NULL) { - 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) { - terminatetetgen(1); + // Put all interior faces of the mesh into 'flipstack'. + tetrahedrons->traversalinit(); + tetloop.tet = tetrahedrontraverse(); + while (tetloop.tet != NULL) { + for (tetloop.ver = 0; tetloop.ver < 4; tetloop.ver++) { + decode(tetloop.tet[tetloop.ver], neightet); + if (!facemarked(neightet)) { + flippush(flipstack, &tetloop); } } - out->numberoftetrahedra = tetrahedrons->items; - out->numberofcorners = b->order == 1 ? 4 : 10; - out->numberoftetrahedronattributes = eextras; - tlist = out->tetrahedronlist; - talist = out->tetrahedronattributelist; - pointindex = 0; - attribindex = 0; + ppt = (point *) &(tetloop.tet[4]); + tetprism_vol_sum += tetprismvol(ppt[0], ppt[1], ppt[2], ppt[3]); + tetloop.tet = tetrahedrontraverse(); } - // 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. + // Calulate a relatively lower bound for small improvement. + // Used to avoid rounding error in volume calculation. + fc.bak_tetprism_vol = tetprism_vol_sum * b->epsilon * 1e-3; + + if (b->verbose) { + printf(" Initial obj = %.17g\n", tetprism_vol_sum); } - // Count the total edge numbers. - meshedges = 0l; + if (b->verbose > 1) { + printf(" Recover Delaunay [Lawson] : %ld\n", flippool->items); + } - 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]; - } - p3 = (point) tptr[6]; - p4 = (point) tptr[7]; - if (out == (tetgenio *) NULL) { - // Tetrahedron number, indices for four points. - fprintf(outfile, "%5d %5d %5d %5d %5d", elementnumber, - pointmark(p1) - shift, pointmark(p2) - shift, - pointmark(p3) - shift, pointmark(p4) - shift); - if (b->order == 2) { - extralist = (point *) tptr[highorderindex]; - // Tetrahedron number, indices for four points plus six extra points. - fprintf(outfile, " %5d %5d %5d %5d %5d %5d", - pointmark(extralist[0]) - shift, pointmark(extralist[1]) - shift, - pointmark(extralist[2]) - shift, pointmark(extralist[3]) - shift, - pointmark(extralist[4]) - shift, pointmark(extralist[5]) - shift); - } - for (i = 0; i < eextras; i++) { - fprintf(outfile, " %.17g", elemattribute(tptr, i)); - } - fprintf(outfile, "\n"); - } else { - tlist[pointindex++] = pointmark(p1) - shift; - tlist[pointindex++] = pointmark(p2) - shift; - tlist[pointindex++] = pointmark(p3) - shift; - tlist[pointindex++] = pointmark(p4) - shift; - if (b->order == 2) { - extralist = (point *) tptr[highorderindex]; - tlist[pointindex++] = pointmark(extralist[0]) - shift; - tlist[pointindex++] = pointmark(extralist[1]) - shift; - tlist[pointindex++] = pointmark(extralist[2]) - shift; - tlist[pointindex++] = pointmark(extralist[3]) - shift; - tlist[pointindex++] = pointmark(extralist[4]) - shift; - tlist[pointindex++] = pointmark(extralist[5]) - shift; - } - for (i = 0; i < eextras; i++) { - talist[attribindex++] = elemattribute(tptr, i); - } - } - if (b->neighout) { - // 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; + // First only use the basic Lawson's flip. + fc.remove_ndelaunay_edge = 1; + fc.enqflag = 2; + + lawsonflip3d(&fc); + + if (b->verbose > 1) { + printf(" obj (after Lawson) = %.17g\n", tetprism_vol_sum); + } + + if (unflipqueue->objects == 0l) { + return; // The mesh is Delaunay. + } + + fc.unflip = 1; // Unflip if the edge is not flipped. + fc.collectnewtets = 1; // new tets are returned in 'cavetetlist'. + fc.enqflag = 0; + + autofliplinklevel = 1; // Init level. + b->fliplinklevel = -1; // No fixed level. + + // For efficiency reason, we limit the maximium size of the edge star. + int bakmaxflipstarsize = b->flipstarsize; + b->flipstarsize = 10; // default + + flipqueue = new arraypool(sizeof(badface), 10); + nextflipqueue = new arraypool(sizeof(badface), 10); + + // Swap the two flip queues. + swapqueue = flipqueue; + flipqueue = unflipqueue; + unflipqueue = swapqueue; + + while (flipqueue->objects > 0l) { + + if (b->verbose > 1) { + printf(" Recover Delaunay [level = %2d] #: %ld.\n", + autofliplinklevel, flipqueue->objects); + } + + for (i = 0; i < flipqueue->objects; i++) { + bface = (badface *) fastlookup(flipqueue, i); + if (getedge(bface->forg, bface->fdest, &bface->tt)) { + if (removeedgebyflips(&(bface->tt), &fc) == 2) { + tetprism_vol_sum += fc.tetprism_vol_sum; + fc.tetprism_vol_sum = 0.0; // Clear it. + // Queue new faces for flips. + for (j = 0; j < cavetetlist->objects; j++) { + parytet = (triface *) fastlookup(cavetetlist, j); + // A queued new tet may be dead. + if (!isdeadtet(*parytet)) { + for (parytet->ver = 0; parytet->ver < 4; parytet->ver++) { + // Avoid queue a face twice. + decode(parytet->tet[parytet->ver], neightet); + if (!facemarked(neightet)) { + flippush(flipstack, parytet); + } + } // parytet->ver + } + } // j + cavetetlist->restart(); + // Remove locally non-Delaunay faces. New non-Delaunay edges + // may be found. They are saved in 'unflipqueue'. + fc.enqflag = 2; + lawsonflip3d(&fc); + fc.enqflag = 0; + // There may be unflipable faces. Add them in flipqueue. + for (j = 0; j < unflipqueue->objects; j++) { + bface = (badface *) fastlookup(unflipqueue, j); + flipqueue->newindex((void **) &parybface); + *parybface = *bface; + } + unflipqueue->restart(); } else { - hitbdry++; - if (hitbdry < 2) { - esym(worktet, spintet); - fnextself(spintet); // In the same tet. - } + // Unable to remove this edge. Save it. + nextflipqueue->newindex((void **) &parybface); + *parybface = *bface; + // Normally, it should be zero. + //assert(fc.tetprism_vol_sum == 0.0); + // However, due to rounding errors, a tiny value may appear. + fc.tetprism_vol_sum = 0.0; } } - // Count this edge if no adjacent tets are smaller than this tet. - if (spintet.tet >= worktet.tet) { - meshedges++; + } // i + + if (b->verbose > 1) { + printf(" obj (after level %d) = %.17g.\n", autofliplinklevel, + tetprism_vol_sum); + } + flipqueue->restart(); + + // Swap the two flip queues. + swapqueue = flipqueue; + flipqueue = nextflipqueue; + nextflipqueue = swapqueue; + + if (flipqueue->objects > 0l) { + // default 'b->delmaxfliplevel' is 1. + if (autofliplinklevel >= b->delmaxfliplevel) { + // For efficiency reason, we do not search too far. + break; } + autofliplinklevel+=b->fliplinklevelinc; + } + } // while (flipqueue->objects > 0l) + + if (flipqueue->objects > 0l) { + if (b->verbose > 1) { + printf(" %ld non-Delaunay edges remained.\n", flipqueue->objects); } - tptr = tetrahedrontraverse(); - elementnumber++; - } - if (b->neighout) { - // Set the outside element marker. - * (int *) (dummytet + elemmarkerindex) = -1; } - if (out == (tetgenio *) NULL) { - fprintf(outfile, "# Generated by %s\n", b->commandline); - fclose(outfile); + if (b->verbose) { + printf(" Final obj = %.17g\n", tetprism_vol_sum); } + + b->flipstarsize = bakmaxflipstarsize; + delete flipqueue; + delete nextflipqueue; } /////////////////////////////////////////////////////////////////////////////// // // -// outfaces() Output all faces to a .face file or a tetgenio structure. // -// // -// This routines outputs all triangular faces (including outer boundary // -// faces and inner faces) of this mesh. // +// gettetrahedron() Get a tetrahedron which have the given vertices. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::outfaces(tetgenio* out) +int tetgenmesh::gettetrahedron(point pa, point pb, point pc, point pd, + triface *searchtet) { - FILE *outfile; - char facefilename[FILENAMESIZE]; - int *elist; - int *emlist; - int neigh1, neigh2; - int index; - triface tface, tsymface; - face checkmark; - point torg, tdest, tapex; - long faces; - int bmark, faceid, marker; - int firstindex, shift; - int facenumber; - - if (out == (tetgenio *) NULL) { - strcpy(facefilename, b->outfilename); - strcat(facefilename, ".face"); - } - - if (!b->quiet) { - if (out == (tetgenio *) NULL) { - printf("Writing %s.\n", facefilename); - } else { - printf("Writing faces.\n"); - } - } - - // Avoid compile warnings. - outfile = (FILE *) NULL; - elist = (int *) NULL; - emlist = (int *) NULL; - index = marker = 0; - - faces = (4l * tetrahedrons->items + hullsize) / 2l; - bmark = !b->nobound && in->facetmarkerlist; + triface spintet; + int t1ver; - if (out == (tetgenio *) NULL) { - outfile = fopen(facefilename, "w"); - if (outfile == (FILE *) NULL) { - printf("File I/O Error: Cannot create file %s.\n", facefilename); - terminatetetgen(3); - } - fprintf(outfile, "%ld %d\n", faces, bmark); - } else { - // Allocate memory for 'trifacelist'. - out->trifacelist = new int[faces * 3]; - if (out->trifacelist == (int *) NULL) { - terminatetetgen(1); - } - // Allocate memory for 'trifacemarkerlist' if necessary. - if (bmark) { - out->trifacemarkerlist = new int[faces]; - if (out->trifacemarkerlist == (int *) NULL) { - terminatetetgen(1); + if (getedge(pa, pb, searchtet)) { + spintet = *searchtet; + while (1) { + if (apex(spintet) == pc) { + *searchtet = spintet; + break; } + fnextself(spintet); + if (spintet.tet == searchtet->tet) break; } - if (b->neighout > 1) { - // '-nn' switch. - out->adjtetlist = new int[subfaces->items * 2]; - if (out->adjtetlist == (int *) NULL) { - terminatetetgen(1); + if (apex(*searchtet) == pc) { + if (oppo(*searchtet) == pd) { + return 1; + } else { + fsymself(*searchtet); + if (oppo(*searchtet) == pd) { + return 1; + } } } - out->numberoftrifaces = faces; - elist = out->trifacelist; - emlist = out->trifacemarkerlist; - index = 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. - } + return 0; +} - tetrahedrons->traversalinit(); - tface.tet = tetrahedrontraverse(); - facenumber = firstindex; // in->firstnumber; - // To loop over the set of faces, loop over all tetrahedra, and look at - // the four faces of each one. If there isn't another tetrahedron - // adjacent to this face, operate on the face. If there is another - // adjacent tetrahedron, operate on the face only if the current - // tetrahedron has a smaller pointer than its neighbor. This way, each - // face is considered only once. - while (tface.tet != (tetrahedron *) NULL) { - for (tface.loc = 0; tface.loc < 4; tface.loc ++) { - sym(tface, tsymface); - if ((tsymface.tet == dummytet) || (tface.tet < tsymface.tet)) { - torg = org(tface); - tdest = dest(tface); - tapex = apex(tface); - if (bmark) { - // Get the boundary marker of this face. If it is an inner face, - // it has no boundary marker, set it be zero. - if (b->useshelles) { - // Shell face is used. - tspivot(tface, checkmark); - if (checkmark.sh == dummysh) { - marker = 0; // It is an inner face. - } else { - faceid = shellmark(checkmark) - 1; - marker = in->facetmarkerlist[faceid]; +/////////////////////////////////////////////////////////////////////////////// +// // +// improvequalitybyflips() Improve the mesh quality by flips. // +// // +/////////////////////////////////////////////////////////////////////////////// + +long tetgenmesh::improvequalitybyflips() +{ + arraypool *flipqueue, *nextflipqueue, *swapqueue; + badface *bface, *parybface; + triface *parytet; + point *ppt; + flipconstraints fc; + REAL *cosdd, ncosdd[6], maxdd; + long totalremcount, remcount; + int remflag; + int n, i, j, k; + + //assert(unflipqueue->objects > 0l); + flipqueue = new arraypool(sizeof(badface), 10); + nextflipqueue = new arraypool(sizeof(badface), 10); + + // Backup flip edge options. + int bakautofliplinklevel = autofliplinklevel; + int bakfliplinklevel = b->fliplinklevel; + int bakmaxflipstarsize = b->flipstarsize; + + // Set flip edge options. + autofliplinklevel = 1; + b->fliplinklevel = -1; + b->flipstarsize = 10; // b->optmaxflipstarsize; + + fc.remove_large_angle = 1; + fc.unflip = 1; + fc.collectnewtets = 1; + fc.checkflipeligibility = 1; + + totalremcount = 0l; + + // Swap the two flip queues. + swapqueue = flipqueue; + flipqueue = unflipqueue; + unflipqueue = swapqueue; + + while (flipqueue->objects > 0l) { + + remcount = 0l; + + while (flipqueue->objects > 0l) { + if (b->verbose > 1) { + printf(" Improving mesh qualiy by flips [%d]#: %ld.\n", + autofliplinklevel, flipqueue->objects); + } + + for (k = 0; k < flipqueue->objects; k++) { + bface = (badface *) fastlookup(flipqueue, k); + if (gettetrahedron(bface->forg, bface->fdest, bface->fapex, + bface->foppo, &bface->tt)) { + //assert(!ishulltet(bface->tt)); + // There are bad dihedral angles in this tet. + if (bface->tt.ver != 11) { + // The dihedral angles are permuted. + // Here we simply re-compute them. Slow!!. + ppt = (point *) & (bface->tt.tet[4]); + tetalldihedral(ppt[0], ppt[1], ppt[2], ppt[3], bface->cent, + &bface->key, NULL); + bface->forg = ppt[0]; + bface->fdest = ppt[1]; + bface->fapex = ppt[2]; + bface->foppo = ppt[3]; + bface->tt.ver = 11; + } + if (bface->key == 0) { + // Re-comput the quality values. Due to smoothing operations. + ppt = (point *) & (bface->tt.tet[4]); + tetalldihedral(ppt[0], ppt[1], ppt[2], ppt[3], bface->cent, + &bface->key, NULL); + } + cosdd = bface->cent; + remflag = 0; + for (i = 0; (i < 6) && !remflag; i++) { + if (cosdd[i] < cosmaxdihed) { + // Found a large dihedral angle. + bface->tt.ver = edge2ver[i]; // Go to the edge. + fc.cosdihed_in = cosdd[i]; + fc.cosdihed_out = 0.0; // 90 degree. + n = removeedgebyflips(&(bface->tt), &fc); + if (n == 2) { + // Edge is flipped. + remflag = 1; + if (fc.cosdihed_out < cosmaxdihed) { + // Queue new bad tets for further improvements. + for (j = 0; j < cavetetlist->objects; j++) { + parytet = (triface *) fastlookup(cavetetlist, j); + if (!isdeadtet(*parytet)) { + ppt = (point *) & (parytet->tet[4]); + // Do not test a hull tet. + if (ppt[3] != dummypoint) { + tetalldihedral(ppt[0], ppt[1], ppt[2], ppt[3], ncosdd, + &maxdd, NULL); + if (maxdd < cosmaxdihed) { + // There are bad dihedral angles in this tet. + nextflipqueue->newindex((void **) &parybface); + parybface->tt.tet = parytet->tet; + parybface->tt.ver = 11; + parybface->forg = ppt[0]; + parybface->fdest = ppt[1]; + parybface->fapex = ppt[2]; + parybface->foppo = ppt[3]; + parybface->key = maxdd; + for (n = 0; n < 6; n++) { + parybface->cent[n] = ncosdd[n]; + } + } + } // if (ppt[3] != dummypoint) + } + } // j + } // if (fc.cosdihed_out < cosmaxdihed) + cavetetlist->restart(); + remcount++; + } } - } else { - // Shell face is not used, only distinguish outer and inner face. - marker = tsymface.tet != dummytet ? 1 : 0; - } - } - if (b->neighout > 1) { - // '-nn' switch. Output adjacent tets indices. - neigh1 = * (int *)(tface.tet + elemmarkerindex); - if (tsymface.tet != dummytet) { - neigh2 = * (int *)(tsymface.tet + elemmarkerindex); - } else { - neigh2 = -1; - } - } - if (out == (tetgenio *) NULL) { - // Face number, indices of three vertices. - fprintf(outfile, "%5d %4d %4d %4d", facenumber, - pointmark(torg) - shift, pointmark(tdest) - shift, - pointmark(tapex) - shift); - if (bmark) { - // Output a boundary marker. - fprintf(outfile, " %d", marker); - } - if (b->neighout > 1) { - fprintf(outfile, " %5d %5d", neigh1, neigh2); - } - fprintf(outfile, "\n"); - } else { - // Output indices of three vertices. - elist[index++] = pointmark(torg) - shift; - elist[index++] = pointmark(tdest) - shift; - elist[index++] = pointmark(tapex) - shift; - if (bmark) { - emlist[facenumber - in->firstnumber] = marker; - } - if (b->neighout > 1) { - out->adjtetlist[(facenumber - in->firstnumber) * 2] = neigh1; - out->adjtetlist[(facenumber - in->firstnumber) * 2 + 1] = neigh2; + } // i + if (!remflag) { + // An unremoved bad tet. Queue it again. + unflipqueue->newindex((void **) &parybface); + *parybface = *bface; } - } - facenumber++; + } // if (gettetrahedron(...)) + } // k + + flipqueue->restart(); + + // Swap the two flip queues. + swapqueue = flipqueue; + flipqueue = nextflipqueue; + nextflipqueue = swapqueue; + } // while (flipqueues->objects > 0) + + if (b->verbose > 1) { + printf(" Removed %ld bad tets.\n", remcount); + } + totalremcount += remcount; + + if (unflipqueue->objects > 0l) { + //if (autofliplinklevel >= b->optmaxfliplevel) { + if (autofliplinklevel >= b->optlevel) { + break; } + autofliplinklevel+=b->fliplinklevelinc; + //b->flipstarsize = 10 + (1 << (b->optlevel - 1)); } - tface.tet = tetrahedrontraverse(); - } - if (out == (tetgenio *) NULL) { - fprintf(outfile, "# Generated by %s\n", b->commandline); - fclose(outfile); - } + // Swap the two flip queues. + swapqueue = flipqueue; + flipqueue = unflipqueue; + unflipqueue = swapqueue; + } // while (flipqueues->objects > 0) + + // Restore original flip edge options. + autofliplinklevel = bakautofliplinklevel; + b->fliplinklevel = bakfliplinklevel; + b->flipstarsize = bakmaxflipstarsize; + + delete flipqueue; + delete nextflipqueue; + + return totalremcount; } /////////////////////////////////////////////////////////////////////////////// // // -// outhullfaces() Output outer boundary faces to a .face file or a // -// tetgenio structure. // +// smoothpoint() Moving a vertex to improve the mesh quality. // +// // +// 'smtpt' (p) is a point to be smoothed. Generally, it is a Steiner point. // +// It may be not a vertex of the mesh. // +// // +// This routine tries to move 'p' inside its star until a selected objective // +// function over all tetrahedra in the star is improved. The function may be // +// the some quality measures, i.e., aspect ratio, maximum dihedral angel, or // +// simply the volume of the tetrahedra. // // // -// The normal of each face is arranged to point inside of the domain (use // -// right-hand rule). This routines will outputs convex hull faces if the // -// mesh is a Delaunay tetrahedralization. // +// 'linkfacelist' contains the list of link faces of 'p'. Since a link face // +// has two orientations, ccw or cw, with respect to 'p'. 'ccw' indicates // +// the orientation is ccw (1) or not (0). // +// // +// 'opm' is a structure contains the parameters of the objective function. // +// It is needed by the evaluation of the function value. // +// // +// The return value indicates weather the point is smoothed or not. // +// // +// ASSUMPTION: This routine assumes that all link faces are true faces, i.e, // +// no face has 'dummypoint' as its vertex. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::outhullfaces(tetgenio* out) +int tetgenmesh::smoothpoint(point smtpt, arraypool *linkfacelist, int ccw, + optparameters *opm) { - FILE *outfile; - char facefilename[FILENAMESIZE]; - int *elist; - int index; - triface tface, tsymface; - face checkmark; - point torg, tdest, tapex; - int firstindex, shift; - int facenumber; + triface *parytet, *parytet1, swaptet; + point pa, pb, pc; + REAL fcent[3], startpt[3], nextpt[3], bestpt[3]; + REAL oldval, minval = 0.0, val; + REAL maxcosd; // oldang, newang; + REAL ori, diff; + int numdirs, iter; + int i, j, k; - if (out == (tetgenio *) NULL) { - strcpy(facefilename, b->outfilename); - strcat(facefilename, ".face"); + // Decide the number of moving directions. + numdirs = (int) linkfacelist->objects; + if (numdirs > opm->numofsearchdirs) { + numdirs = opm->numofsearchdirs; // Maximum search directions. } - if (!b->quiet) { - if (out == (tetgenio *) NULL) { - printf("Writing %s.\n", facefilename); - } else { - printf("Writing faces.\n"); - } + // Set the initial value. + if (!opm->max_min_volume) { + assert(opm->initval >= 0.0); } + opm->imprval = opm->initval; + iter = 0; - // Avoid compile warnings. - outfile = (FILE *) NULL; - elist = (int *) NULL; - index = 0; + for (i = 0; i < 3; i++) { + bestpt[i] = startpt[i] = smtpt[i]; + } - if (out == (tetgenio *) NULL) { - outfile = fopen(facefilename, "w"); - if (outfile == (FILE *) NULL) { - printf("File I/O Error: Cannot create file %s.\n", facefilename); - terminatetetgen(3); - } - fprintf(outfile, "%ld 0\n", hullsize); - } else { - // Allocate memory for 'trifacelist'. - out->trifacelist = new int[hullsize * 3]; - if (out->trifacelist == (int *) NULL) { - terminatetetgen(1); - } - out->numberoftrifaces = hullsize; - elist = out->trifacelist; - index = 0; - } + // Iterate until the obj function is not improved. + while (1) { - // 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. - } + // Find the best next location. + oldval = opm->imprval; - tetrahedrons->traversalinit(); - tface.tet = tetrahedrontraverse(); - facenumber = firstindex; // in->firstnumber; - // To loop over the set of hull faces, loop over all tetrahedra, and look - // at the four faces of each one. If there isn't another tetrahedron - // adjacent to this face, operate on the face. - while (tface.tet != (tetrahedron *) NULL) { - for (tface.loc = 0; tface.loc < 4; tface.loc ++) { - sym(tface, tsymface); - if (tsymface.tet == dummytet) { - torg = org(tface); - tdest = dest(tface); - tapex = apex(tface); - if (out == (tetgenio *) NULL) { - // Face number, indices of three vertices. - fprintf(outfile, "%5d %4d %4d %4d", facenumber, - pointmark(torg) - shift, pointmark(tdest) - shift, - pointmark(tapex) - shift); - fprintf(outfile, "\n"); + for (i = 0; i < numdirs; i++) { + // Randomly pick a link face (0 <= k <= objects - i - 1). + k = (int) randomnation(linkfacelist->objects - i); + parytet = (triface *) fastlookup(linkfacelist, k); + // Calculate a new position from 'p' to the center of this face. + pa = org(*parytet); + pb = dest(*parytet); + pc = apex(*parytet); + for (j = 0; j < 3; j++) { + fcent[j] = (pa[j] + pb[j] + pc[j]) / 3.0; + } + for (j = 0; j < 3; j++) { + nextpt[j] = startpt[j] + opm->searchstep * (fcent[j] - startpt[j]); + } + // Calculate the largest minimum function value for the new location. + for (j = 0; j < linkfacelist->objects; j++) { + parytet = (triface *) fastlookup(linkfacelist, j); + if (ccw) { + pa = org(*parytet); + pb = dest(*parytet); } else { - // Output indices of three vertices. - elist[index++] = pointmark(torg) - shift; - elist[index++] = pointmark(tdest) - shift; - elist[index++] = pointmark(tapex) - shift; + pb = org(*parytet); + pa = dest(*parytet); } - facenumber++; + pc = apex(*parytet); + ori = orient3d(pa, pb, pc, nextpt); + if (ori < 0.0) { + // Calcuate the objective function value. + if (opm->max_min_volume) { + //val = -ori; + val = - orient3dfast(pa, pb, pc, nextpt); + } else if (opm->max_min_aspectratio) { + val = tetaspectratio(pa, pb, pc, nextpt); + } else if (opm->min_max_dihedangle) { + tetalldihedral(pa, pb, pc, nextpt, NULL, &maxcosd, NULL); + if (maxcosd < -1) maxcosd = -1.0; // Rounding. + val = maxcosd + 1.0; // Make it be positive. + } else { + // Unknown objective function. + val = 0.0; + } + } else { // ori >= 0.0; + // An invalid new tet. + // This may happen if the mesh contains inverted elements. + if (opm->max_min_volume) { + //val = -ori; + val = - orient3dfast(pa, pb, pc, nextpt); + } else { + // Discard this point. + break; // j + } + } // if (ori >= 0.0) + // Stop looping when the object value is not improved. + if (val <= opm->imprval) { + break; // j + } else { + // Remember the smallest improved value. + if (j == 0) { + minval = val; + } else { + minval = (val < minval) ? val : minval; + } + } + } // j + if (j == linkfacelist->objects) { + // The function value has been improved. + opm->imprval = minval; + // Save the new location of the point. + for (j = 0; j < 3; j++) bestpt[j] = nextpt[j]; + } + // Swap k-th and (object-i-1)-th entries. + j = linkfacelist->objects - i - 1; + parytet = (triface *) fastlookup(linkfacelist, k); + parytet1 = (triface *) fastlookup(linkfacelist, j); + swaptet = *parytet1; + *parytet1 = *parytet; + *parytet = swaptet; + } // i + + diff = opm->imprval - oldval; + if (diff > 0.0) { + // Is the function value improved effectively? + if (opm->max_min_volume) { + //if ((diff / oldval) < b->epsilon) diff = 0.0; + } else if (opm->max_min_aspectratio) { + if ((diff / oldval) < 1e-3) diff = 0.0; + } else if (opm->min_max_dihedangle) { + //oldang = acos(oldval - 1.0); + //newang = acos(opm->imprval - 1.0); + //if ((oldang - newang) < 0.00174) diff = 0.0; // about 0.1 degree. + } else { + // Unknown objective function. + assert(0); // Not possible. } } - tface.tet = tetrahedrontraverse(); - } - if (out == (tetgenio *) NULL) { - fprintf(outfile, "# Generated by %s\n", b->commandline); - fclose(outfile); + if (diff > 0.0) { + // Yes, move p to the new location and continue. + for (j = 0; j < 3; j++) startpt[j] = bestpt[j]; + iter++; + if ((opm->maxiter > 0) && (iter >= opm->maxiter)) { + // Maximum smoothing iterations reached. + break; + } + } else { + break; + } + + } // while (1) + + if (iter > 0) { + // The point has been smoothed. + opm->smthiter = iter; // Remember the number of iterations. + // The point has been smoothed. Update it to its new position. + for (i = 0; i < 3; i++) smtpt[i] = startpt[i]; } + + return iter; } + /////////////////////////////////////////////////////////////////////////////// // // -// outsubfaces() Output subfaces (i.e. boundary faces) to a .face file or // -// a tetgenio structure. // -// // -// The boundary faces are exist in 'subfaces'. For listing triangle vertices // -// in the same sense for all triangles in the mesh, the direction determined // -// by right-hand rule is pointer to the inside of the volume. // +// improvequalitysmoothing() Improve mesh quality by smoothing. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::outsubfaces(tetgenio* out) +long tetgenmesh::improvequalitybysmoothing(optparameters *opm) { - FILE *outfile; - char facefilename[FILENAMESIZE]; - int *elist; - int *emlist; - int index, index1, index2; - triface abuttingtet; - face faceloop; - point torg, tdest, tapex; - int bmark, faceid, marker; - int firstindex, shift; - int neigh1, neigh2; - int facenumber; + arraypool *flipqueue, *swapqueue; + triface *parytet; + badface *bface, *parybface; + point *ppt; + long totalsmtcount, smtcount; + int smtflag; + int iter, i, j, k; - if (out == (tetgenio *) NULL) { - strcpy(facefilename, b->outfilename); - strcat(facefilename, ".face"); - } + //assert(unflipqueue->objects > 0l); + flipqueue = new arraypool(sizeof(badface), 10); - if (!b->quiet) { - if (out == (tetgenio *) NULL) { - printf("Writing %s.\n", facefilename); - } else { - printf("Writing faces.\n"); - } - } + // Swap the two flip queues. + swapqueue = flipqueue; + flipqueue = unflipqueue; + unflipqueue = swapqueue; - // Avoid compile warnings. - outfile = (FILE *) NULL; - elist = (int *) NULL; - emlist = (int *) NULL; - index = index1 = index2 = 0; - faceid = marker = 0; - neigh1 = neigh2 = 0; + totalsmtcount = 0l; + iter = 0; - bmark = !b->nobound && in->facetmarkerlist; + while (flipqueue->objects > 0l) { - if (out == (tetgenio *) NULL) { - outfile = fopen(facefilename, "w"); - if (outfile == (FILE *) NULL) { - printf("File I/O Error: Cannot create file %s.\n", facefilename); - terminatetetgen(3); - } - // Number of subfaces. - fprintf(outfile, "%ld %d\n", subfaces->items, bmark); - } else { - // Allocate memory for 'trifacelist'. - out->trifacelist = new int[subfaces->items * 3]; - if (out->trifacelist == (int *) NULL) { - terminatetetgen(1); - } - if (bmark) { - // Allocate memory for 'trifacemarkerlist'. - out->trifacemarkerlist = new int[subfaces->items]; - if (out->trifacemarkerlist == (int *) NULL) { - terminatetetgen(1); - } - } - if (b->neighout > 1) { - // '-nn' switch. - out->adjtetlist = new int[subfaces->items * 2]; - if (out->adjtetlist == (int *) NULL) { - terminatetetgen(1); - } - } - out->numberoftrifaces = subfaces->items; - elist = out->trifacelist; - emlist = out->trifacemarkerlist; - } + smtcount = 0l; - // 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. - } + if (b->verbose > 1) { + printf(" Improving mesh quality by smoothing [%d]#: %ld.\n", + iter, flipqueue->objects); + } + + for (k = 0; k < flipqueue->objects; k++) { + bface = (badface *) fastlookup(flipqueue, k); + if (gettetrahedron(bface->forg, bface->fdest, bface->fapex, + bface->foppo, &bface->tt)) { + // Operate on it if it is not in 'unflipqueue'. + if (!marktested(bface->tt)) { + // Here we simply re-compute the quality. Since other smoothing + // operation may have moved the vertices of this tet. + ppt = (point *) & (bface->tt.tet[4]); + tetalldihedral(ppt[0], ppt[1], ppt[2], ppt[3], bface->cent, + &bface->key, NULL); + if (bface->key < cossmtdihed) { // if (maxdd < cosslidihed) { + // It is a sliver. Try to smooth its vertices. + smtflag = 0; + opm->initval = bface->key + 1.0; + for (i = 0; (i < 4) && !smtflag; i++) { + if (pointtype(ppt[i]) == FREEVOLVERTEX) { + getvertexstar(1, ppt[i], cavetetlist, NULL, NULL); + opm->searchstep = 0.001; // Search step size + smtflag = smoothpoint(ppt[i], cavetetlist, 1, opm); + if (smtflag) { + while (opm->smthiter == opm->maxiter) { + opm->searchstep *= 10.0; // Increase the step size. + opm->initval = opm->imprval; + opm->smthiter = 0; // reset + smoothpoint(ppt[i], cavetetlist, 1, opm); + } + // This tet is modifed. + smtcount++; + if ((opm->imprval - 1.0) < cossmtdihed) { + // There are slivers in new tets. Queue them. + for (j = 0; j < cavetetlist->objects; j++) { + parytet = (triface *) fastlookup(cavetetlist, j); + assert(!isdeadtet(*parytet)); + // Operate it if it is not in 'unflipqueue'. + if (!marktested(*parytet)) { + // Evaluate its quality. + // Re-use ppt, bface->key, bface->cent. + ppt = (point *) & (parytet->tet[4]); + tetalldihedral(ppt[0], ppt[1], ppt[2], ppt[3], + bface->cent, &bface->key, NULL); + if (bface->key < cossmtdihed) { + // A new sliver. Queue it. + marktest(*parytet); // It is in unflipqueue. + unflipqueue->newindex((void **) &parybface); + parybface->tt = *parytet; + parybface->forg = ppt[0]; + parybface->fdest = ppt[1]; + parybface->fapex = ppt[2]; + parybface->foppo = ppt[3]; + parybface->tt.ver = 11; + parybface->key = 0.0; + } + } + } // j + } // if ((opm->imprval - 1.0) < cossmtdihed) + } // if (smtflag) + cavetetlist->restart(); + } // if (pointtype(ppt[i]) == FREEVOLVERTEX) + } // i + if (!smtflag) { + // Didn't smooth. Queue it again. + marktest(bface->tt); // It is in unflipqueue. + unflipqueue->newindex((void **) &parybface); + parybface->tt = bface->tt; + parybface->forg = ppt[0]; + parybface->fdest = ppt[1]; + parybface->fapex = ppt[2]; + parybface->foppo = ppt[3]; + parybface->tt.ver = 11; + parybface->key = 0.0; + } + } // if (maxdd < cosslidihed) + } // if (!marktested(...)) + } // if (gettetrahedron(...)) + } // k - subfaces->traversalinit(); - faceloop.sh = shellfacetraverse(subfaces); - facenumber = firstindex; // in->firstnumber; - while (faceloop.sh != (shellface *) NULL) { - stpivot(faceloop, abuttingtet); - if (abuttingtet.tet == dummytet) { - sesymself(faceloop); - stpivot(faceloop, abuttingtet); - } - if (abuttingtet.tet != dummytet) { - // If there is a tetrahedron containing this subface, orient it so - // that the normal of this face points to inside of the volume by - // right-hand rule. - adjustedgering(abuttingtet, CCW); - torg = org(abuttingtet); - tdest = dest(abuttingtet); - tapex = apex(abuttingtet); - } else { - // This may happen when only a surface mesh be generated. - torg = sorg(faceloop); - tdest = sdest(faceloop); - tapex = sapex(faceloop); - } - if (bmark) { - faceid = shellmark(faceloop) - 1; - marker = in->facetmarkerlist[faceid]; + flipqueue->restart(); + + // Unmark the tets in unflipqueue. + for (i = 0; i < unflipqueue->objects; i++) { + bface = (badface *) fastlookup(unflipqueue, i); + unmarktest(bface->tt); } - if (b->neighout > 1) { - // '-nn' switch. Output adjacent tets indices. - neigh1 = -1; - stpivot(faceloop, abuttingtet); - if (abuttingtet.tet != dummytet) { - neigh1 = * (int *)(abuttingtet.tet + elemmarkerindex); - } - neigh2 = -1; - sesymself(faceloop); - stpivot(faceloop, abuttingtet); - if (abuttingtet.tet != dummytet) { - neigh2 = * (int *)(abuttingtet.tet + elemmarkerindex); - } + + if (b->verbose > 1) { + printf(" Smooth %ld points.\n", smtcount); } - if (out == (tetgenio *) NULL) { - fprintf(outfile, "%5d %4d %4d %4d", facenumber, - pointmark(torg) - shift, pointmark(tdest) - shift, - pointmark(tapex) - shift); - if (bmark) { - fprintf(outfile, " %d", marker); - } - if (b->neighout > 1) { - fprintf(outfile, " %5d %5d", neigh1, neigh2); - } - fprintf(outfile, "\n"); + totalsmtcount += smtcount; + + if (smtcount == 0l) { + // No point has been smoothed. + break; } else { - // Output three vertices of this face; - elist[index++] = pointmark(torg) - shift; - elist[index++] = pointmark(tdest) - shift; - elist[index++] = pointmark(tapex) - shift; - if (bmark) { - emlist[index1++] = marker; - } - if (b->neighout > 1) { - out->adjtetlist[index2++] = neigh1; - out->adjtetlist[index2++] = neigh2; + iter++; + if (iter == 2) { //if (iter >= b->optpasses) { + break; } } - facenumber++; - faceloop.sh = shellfacetraverse(subfaces); - } - if (out == (tetgenio *) NULL) { - fprintf(outfile, "# Generated by %s\n", b->commandline); - fclose(outfile); - } + // Swap the two flip queues. + swapqueue = flipqueue; + flipqueue = unflipqueue; + unflipqueue = swapqueue; + } // while + + delete flipqueue; + + return totalsmtcount; } /////////////////////////////////////////////////////////////////////////////// // // -// outedges() Output all edges to a .edge file or a structure. // +// splitsliver() Split a sliver. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::outedges(tetgenio* out) +int tetgenmesh::splitsliver(triface *slitet, REAL cosd, int chkencflag) { - FILE *outfile; - char edgefilename[FILENAMESIZE]; - int *elist, *emlist; - int index, index1; - triface tetloop, worktet, spintet; - face checkseg; - point torg, tdest; - int firstindex, shift; - int edgenumber, faceid, marker; - int hitbdry, i; + triface *abtets; + triface searchtet, spintet, *parytet; + point pa, pb, steinerpt; + optparameters opm; + insertvertexflags ivf; + REAL smtpt[3], midpt[3]; + int success; + int t1ver; + int n, i; - if (out == (tetgenio *) NULL) { - strcpy(edgefilename, b->outfilename); - strcat(edgefilename, ".edge"); + // 'slitet' is [c,d,a,b], where [c,d] has a big dihedral angle. + // Go to the opposite edge [a,b]. + edestoppo(*slitet, searchtet); // [a,b,c,d]. + + // Do not split a segment. + if (issubseg(searchtet)) { + return 0; } - if (!b->quiet) { - if (out == (tetgenio *) NULL) { - printf("Writing %s.\n", edgefilename); - } else { - printf("Writing edges.\n"); - } + // Count the number of tets shared at [a,b]. + // Do not split it if it is a hull edge. + spintet = searchtet; + n = 0; + while (1) { + if (ishulltet(spintet)) break; + n++; + fnextself(spintet); + if (spintet.tet == searchtet.tet) break; } + if (ishulltet(spintet)) { + return 0; // It is a hull edge. + } + assert(n >= 3); - // Avoid compile warnings. - outfile = (FILE *) NULL; - elist = (int *) NULL; - emlist = (int *) NULL; - index = index1 = 0; - faceid = marker = 0; + // Get all tets at edge [a,b]. + abtets = new triface[n]; + spintet = searchtet; + for (i = 0; i < n; i++) { + abtets[i] = spintet; + fnextself(spintet); + } - 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); - } - // Write the number of edges, boundary markers (0 or 1). - fprintf(outfile, "%ld %d\n", meshedges, !b->nobound); - } else { - // Allocate memory for 'edgelist'. - out->edgelist = new int[meshedges * 2]; - if (out->edgelist == (int *) NULL) { - terminatetetgen(1); - } - if (!b->nobound) { - out->edgemarkerlist = new int[meshedges]; - } - out->numberofedges = meshedges; - elist = out->edgelist; - emlist = out->edgemarkerlist; + // Initialize the list of 2n boundary faces. + for (i = 0; i < n; i++) { + eprev(abtets[i], searchtet); + esymself(searchtet); // [a,p_i,p_i+1]. + cavetetlist->newindex((void **) &parytet); + *parytet = searchtet; + enext(abtets[i], searchtet); + esymself(searchtet); // [p_i,b,p_i+1]. + cavetetlist->newindex((void **) &parytet); + *parytet = searchtet; } - // 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 (reduce) the output indices by 1. + // Init the Steiner point at the midpoint of edge [a,b]. + pa = org(abtets[0]); + pb = dest(abtets[0]); + for (i = 0; i < 3; i++) { + smtpt[i] = midpt[i] = 0.5 * (pa[i] + pb[i]); } - tetrahedrons->traversalinit(); - tetloop.tet = tetrahedrontraverse(); - edgenumber = firstindex; // in->firstnumber; - 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) { - torg = org(worktet); - tdest = dest(worktet); - if (out == (tetgenio *) NULL) { - fprintf(outfile, "%5d %4d %4d", edgenumber, - pointmark(torg) - shift, pointmark(tdest) - shift); - } else { - // Output three vertices of this face; - elist[index++] = pointmark(torg) - shift; - 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. - } - } else { - marker = 0; // It's not a segment. - } - if (out == (tetgenio *) NULL) { - fprintf(outfile, " %d", marker); - } else { - emlist[index1++] = marker; - } - } - if (out == (tetgenio *) NULL) { - fprintf(outfile, "\n"); - } - edgenumber++; - } + // Point smooth options. + opm.min_max_dihedangle = 1; + opm.initval = cosd + 1.0; // Initial volume is zero. + opm.numofsearchdirs = 20; + opm.searchstep = 0.001; + opm.maxiter = 100; // Limit the maximum iterations. + + success = smoothpoint(smtpt, cavetetlist, 1, &opm); + + if (success) { + while (opm.smthiter == opm.maxiter) { + // It was relocated and the prescribed maximum iteration reached. + // Try to increase the search stepsize. + opm.searchstep *= 10.0; + //opm.maxiter = 100; // Limit the maximum iterations. + opm.initval = opm.imprval; + opm.smthiter = 0; // Init. + smoothpoint(smtpt, cavetetlist, 1, &opm); } - tetloop.tet = tetrahedrontraverse(); + } // if (success) + + cavetetlist->restart(); + + if (!success) { + delete [] abtets; + return 0; } - if (out == (tetgenio *) NULL) { - fprintf(outfile, "# Generated by %s\n", b->commandline); - fclose(outfile); + + // Insert the Steiner point. + makepoint(&steinerpt, FREEVOLVERTEX); + for (i = 0; i < 3; i++) steinerpt[i] = smtpt[i]; + + // Insert the created Steiner point. + for (i = 0; i < n; i++) { + infect(abtets[i]); + caveoldtetlist->newindex((void **) &parytet); + *parytet = abtets[i]; + } + + searchtet = abtets[0]; // No need point location. + if (b->metric) { + locate(steinerpt, &searchtet); // For size interpolation. + } + + delete [] abtets; + + ivf.iloc = (int) INSTAR; + ivf.chkencflag = chkencflag; + ivf.assignmeshsize = b->metric; + + + if (insertpoint(steinerpt, &searchtet, NULL, NULL, &ivf)) { + // The vertex has been inserted. + st_volref_count++; + if (steinerleft > 0) steinerleft--; + return 1; + } else { + // The Steiner point is too close to an existing vertex. Reject it. + pointdealloc(steinerpt); + return 0; } } /////////////////////////////////////////////////////////////////////////////// // // -// outsubsegments() Output segments to a .edge file or a structure. // +// removeslivers() Remove slivers by adding Steiner points. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::outsubsegments(tetgenio* out) +long tetgenmesh::removeslivers(int chkencflag) { - FILE *outfile; - char edgefilename[FILENAMESIZE]; - int *elist; - int index; - face edgeloop; - point torg, tdest; - int firstindex, shift; - int edgenumber; + arraypool *flipqueue, *swapqueue; + badface *bface, *parybface; + triface slitet, *parytet; + point *ppt; + REAL cosdd[6], maxcosd; + long totalsptcount, sptcount; + int iter, i, j, k; - if (out == (tetgenio *) NULL) { - strcpy(edgefilename, b->outfilename); - strcat(edgefilename, ".edge"); - } + //assert(unflipqueue->objects > 0l); + flipqueue = new arraypool(sizeof(badface), 10); - if (!b->quiet) { - if (out == (tetgenio *) NULL) { - printf("Writing %s.\n", edgefilename); - } else { - printf("Writing edges.\n"); - } - } + // Swap the two flip queues. + swapqueue = flipqueue; + flipqueue = unflipqueue; + unflipqueue = swapqueue; - // Avoid compile warnings. - outfile = (FILE *) NULL; - elist = (int *) NULL; - index = 0; + totalsptcount = 0l; + iter = 0; - 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); - } - // Number of subsegments. - fprintf(outfile, "%ld\n", subsegs->items); - } else { - // Allocate memory for 'edgelist'. - out->edgelist = new int[subsegs->items * 2]; - if (out->edgelist == (int *) NULL) { - terminatetetgen(1); - } - out->numberofedges = subsegs->items; - elist = out->edgelist; - } + while ((flipqueue->objects > 0l) && (steinerleft != 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. - } + sptcount = 0l; - subsegs->traversalinit(); - edgeloop.sh = shellfacetraverse(subsegs); - edgenumber = firstindex; // in->firstnumber; - while (edgeloop.sh != (shellface *) NULL) { - torg = sorg(edgeloop); - tdest = sdest(edgeloop); - if (out == (tetgenio *) NULL) { - fprintf(outfile, "%5d %4d %4d\n", edgenumber, - pointmark(torg) - shift, pointmark(tdest) - shift); + if (b->verbose > 1) { + printf(" Splitting bad quality tets [%d]#: %ld.\n", + iter, flipqueue->objects); + } + + for (k = 0; (k < flipqueue->objects) && (steinerleft != 0); k++) { + bface = (badface *) fastlookup(flipqueue, k); + if (gettetrahedron(bface->forg, bface->fdest, bface->fapex, + bface->foppo, &bface->tt)) { + if ((bface->key == 0) || (bface->tt.ver != 11)) { + // Here we need to re-compute the quality. Since other smoothing + // operation may have moved the vertices of this tet. + ppt = (point *) & (bface->tt.tet[4]); + tetalldihedral(ppt[0], ppt[1], ppt[2], ppt[3], bface->cent, + &bface->key, NULL); + } + if (bface->key < cosslidihed) { + // It is a sliver. Try to split it. + slitet.tet = bface->tt.tet; + //cosdd = bface->cent; + for (j = 0; j < 6; j++) { + if (bface->cent[j] < cosslidihed) { + // Found a large dihedral angle. + slitet.ver = edge2ver[j]; // Go to the edge. + if (splitsliver(&slitet, bface->cent[j], chkencflag)) { + sptcount++; + break; + } + } + } // j + if (j < 6) { + // A sliver is split. Queue new slivers. + badtetrahedrons->traversalinit(); + parytet = (triface *) badtetrahedrons->traverse(); + while (parytet != NULL) { + unmarktest2(*parytet); + ppt = (point *) & (parytet->tet[4]); + tetalldihedral(ppt[0], ppt[1], ppt[2], ppt[3], cosdd, + &maxcosd, NULL); + if (maxcosd < cosslidihed) { + // A new sliver. Queue it. + unflipqueue->newindex((void **) &parybface); + parybface->forg = ppt[0]; + parybface->fdest = ppt[1]; + parybface->fapex = ppt[2]; + parybface->foppo = ppt[3]; + parybface->tt.tet = parytet->tet; + parybface->tt.ver = 11; + parybface->key = maxcosd; + for (i = 0; i < 6; i++) { + parybface->cent[i] = cosdd[i]; + } + } + parytet = (triface *) badtetrahedrons->traverse(); + } + badtetrahedrons->restart(); + } else { + // Didn't split. Queue it again. + unflipqueue->newindex((void **) &parybface); + *parybface = *bface; + } // if (j == 6) + } // if (bface->key < cosslidihed) + } // if (gettetrahedron(...)) + } // k + + flipqueue->restart(); + + if (b->verbose > 1) { + printf(" Split %ld tets.\n", sptcount); + } + totalsptcount += sptcount; + + if (sptcount == 0l) { + // No point has been smoothed. + break; } else { - // Output three vertices of this face; - elist[index++] = pointmark(torg) - shift; - elist[index++] = pointmark(tdest) - shift; + iter++; + if (iter == 2) { //if (iter >= b->optpasses) { + break; + } } - edgenumber++; - edgeloop.sh = shellfacetraverse(subsegs); - } - if (out == (tetgenio *) NULL) { - fprintf(outfile, "# Generated by %s\n", b->commandline); - fclose(outfile); - } + // Swap the two flip queues. + swapqueue = flipqueue; + flipqueue = unflipqueue; + unflipqueue = swapqueue; + } // while + + delete flipqueue; + + return totalsptcount; } /////////////////////////////////////////////////////////////////////////////// // // -// outneighbors() Output tet neighbors to a .neigh file or a structure. // +// optimizemesh() Optimize mesh for specified objective functions. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::outneighbors(tetgenio* out) +void tetgenmesh::optimizemesh() { - FILE *outfile; - char neighborfilename[FILENAMESIZE]; - int *nlist; - int index; - triface tetloop, tetsym; - int neighbor1, neighbor2, neighbor3, neighbor4; - int firstindex; - int elementnumber; - - if (out == (tetgenio *) NULL) { - strcpy(neighborfilename, b->outfilename); - strcat(neighborfilename, ".neigh"); - } + badface *parybface; + triface checktet; + point *ppt; + int optpasses; + optparameters opm; + REAL ncosdd[6], maxdd; + long totalremcount, remcount; + long totalsmtcount, smtcount; + long totalsptcount, sptcount; + int chkencflag; + int iter; + int n; if (!b->quiet) { - if (out == (tetgenio *) NULL) { - printf("Writing %s.\n", neighborfilename); - } else { - printf("Writing neighbors.\n"); - } + printf("Optimizing mesh...\n"); } - // Avoid compile warnings. - outfile = (FILE *) NULL; - nlist = (int *) NULL; - index = 0; + optpasses = ((1 << b->optlevel) - 1); - if (out == (tetgenio *) NULL) { - outfile = fopen(neighborfilename, "w"); - if (outfile == (FILE *) NULL) { - printf("File I/O Error: Cannot create file %s.\n", neighborfilename); - terminatetetgen(3); - } - // Number of tetrahedra, four faces per tetrahedron. - fprintf(outfile, "%ld %d\n", tetrahedrons->items, 4); - } else { - // Allocate memory for 'neighborlist'. - out->neighborlist = new int[tetrahedrons->items * 4]; - if (out->neighborlist == (int *) NULL) { - terminatetetgen(1); - } - nlist = out->neighborlist; + if (b->verbose) { + printf(" Optimization level = %d.\n", b->optlevel); + printf(" Optimization scheme = %d.\n", b->optscheme); + printf(" Number of iteration = %d.\n", optpasses); + printf(" Min_Max dihed angle = %g.\n", b->optmaxdihedral); } - // Determine the first index (0 or 1). - firstindex = b->zeroindex ? 0 : in->firstnumber; + totalsmtcount = totalsptcount = totalremcount = 0l; + cosmaxdihed = cos(b->optmaxdihedral / 180.0 * PI); + cossmtdihed = cos(b->optminsmtdihed / 180.0 * PI); + cosslidihed = cos(b->optminslidihed / 180.0 * PI); + + int attrnum = numelemattrib - 1; + + // Put all bad tetrahedra into array. tetrahedrons->traversalinit(); - tetloop.tet = tetrahedrontraverse(); - elementnumber = firstindex; // in->firstnumber; - while (tetloop.tet != (tetrahedron *) NULL) { - tetloop.loc = 2; - sym(tetloop, tetsym); - neighbor1 = * (int *) (tetsym.tet + elemmarkerindex); - tetloop.loc = 3; - sym(tetloop, tetsym); - neighbor2 = * (int *) (tetsym.tet + elemmarkerindex); - tetloop.loc = 1; - sym(tetloop, tetsym); - neighbor3 = * (int *) (tetsym.tet + elemmarkerindex); - tetloop.loc = 0; - sym(tetloop, tetsym); - neighbor4 = * (int *) (tetsym.tet + elemmarkerindex); - if (out == (tetgenio *) NULL) { - // Tetrahedra number, neighboring tetrahedron numbers. - fprintf(outfile, "%4d %4d %4d %4d %4d\n", elementnumber, - neighbor1, neighbor2, neighbor3, neighbor4); - } else { - nlist[index++] = neighbor1; - nlist[index++] = neighbor2; - nlist[index++] = neighbor3; - nlist[index++] = neighbor4; + checktet.tet = tetrahedrontraverse(); + while (checktet.tet != NULL) { + if (b->convex) { // -c + // Skip this tet if it lies in the exterior. + if (elemattribute(checktet.tet, attrnum) == -1.0) { + checktet.tet = tetrahedrontraverse(); + continue; + } } - tetloop.tet = tetrahedrontraverse(); - elementnumber++; + ppt = (point *) & (checktet.tet[4]); + tetalldihedral(ppt[0], ppt[1], ppt[2], ppt[3], ncosdd, &maxdd, NULL); + if (maxdd < cosmaxdihed) { + // There are bad dihedral angles in this tet. + unflipqueue->newindex((void **) &parybface); + parybface->tt.tet = checktet.tet; + parybface->tt.ver = 11; + parybface->forg = ppt[0]; + parybface->fdest = ppt[1]; + parybface->fapex = ppt[2]; + parybface->foppo = ppt[3]; + parybface->key = maxdd; + for (n = 0; n < 6; n++) { + parybface->cent[n] = ncosdd[n]; + } + } + checktet.tet = tetrahedrontraverse(); + } + + totalremcount = improvequalitybyflips(); + + if ((unflipqueue->objects > 0l) && + ((b->optscheme & 2) || (b->optscheme & 4))) { + // The pool is only used by removeslivers(). + badtetrahedrons = new memorypool(sizeof(triface), b->tetrahedraperblock, + sizeof(void *), 0); + + // Smoothing options. + opm.min_max_dihedangle = 1; + opm.numofsearchdirs = 10; + // opm.searchstep = 0.001; + opm.maxiter = 30; // Limit the maximum iterations. + //opm.checkencflag = 4; // Queue affected tets after smoothing. + chkencflag = 4; // Queue affected tets after splitting a sliver. + iter = 0; + + while (iter < optpasses) { + smtcount = sptcount = remcount = 0l; + if (b->optscheme & 2) { + smtcount += improvequalitybysmoothing(&opm); + totalsmtcount += smtcount; + if (smtcount > 0l) { + remcount = improvequalitybyflips(); + totalremcount += remcount; + } + } + if (unflipqueue->objects > 0l) { + if (b->optscheme & 4) { + sptcount += removeslivers(chkencflag); + totalsptcount += sptcount; + if (sptcount > 0l) { + remcount = improvequalitybyflips(); + totalremcount += remcount; + } + } + } + if (unflipqueue->objects > 0l) { + if (remcount > 0l) { + iter++; + } else { + break; + } + } else { + break; + } + } // while (iter) + + delete badtetrahedrons; + } - if (out == (tetgenio *) NULL) { - fprintf(outfile, "# Generated by %s\n", b->commandline); - fclose(outfile); + if (unflipqueue->objects > 0l) { + if (b->verbose > 1) { + printf(" %ld bad tets remained.\n", unflipqueue->objects); + } + unflipqueue->restart(); + } + + if (b->verbose) { + if (totalremcount > 0l) { + printf(" Removed %ld edges.\n", totalremcount); + } + if (totalsmtcount > 0l) { + printf(" Smoothed %ld points.\n", totalsmtcount); + } + if (totalsptcount > 0l) { + printf(" Split %ld slivers.\n", totalsptcount); + } } } +//// //// +//// //// +//// optimize_cxx ///////////////////////////////////////////////////////////// + +//// meshstat_cxx ///////////////////////////////////////////////////////////// +//// //// +//// //// + /////////////////////////////////////////////////////////////////////////////// // // -// outvoronoi() Output the Voronoi diagram to .v.node, .v.edge, v.face, // -// and .v.cell. // -// // -// The Voronoi diagram is the geometric dual of the Delaunay triangulation. // -// The Voronoi vertices are the circumcenters of Delaunay tetrahedra. Each // -// Voronoi edge connects two Voronoi vertices at two sides of a common Dela- // -// unay face. At a face of convex hull, it becomes a ray (goto the infinity).// -// A Voronoi face is the convex hull of all Voronoi vertices around a common // -// Delaunay edge. It is a closed polygon for any interal Delaunay edge. At a // -// ridge, it is unbounded. Each Voronoi cell is the convex hull of all Vor- // -// onoi vertices around a common Delaunay vertex. It is a polytope for any // -// internal Delaunay vertex. It is an unbounded polyhedron for a Delaunay // -// vertex belonging to the convex hull. // +// printfcomma() Print a (large) number with the 'thousands separator'. // // // -// Comment: Special thanks to Victor Liu for finding and fixing few bugs. // +// The following code was simply copied from "stackoverflow". // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::outvoronoi(tetgenio* out) +void tetgenmesh::printfcomma(unsigned long n) { - FILE *outfile; - char outfilename[FILENAMESIZE]; - tetgenio::voroedge *vedge; - tetgenio::vorofacet *vfacet; - list *tetlist, *ptlist; - triface tetloop, worktet, spintet; - point pt[4], ptloop, neipt; - REAL ccent[3], infvec[3], vec1[3], vec2[3], L; - long faces, edges; - int *tetfaceindexarray, *tetedgeindexarray; - int arraysize, *vertarray; - int vpointcount, vedgecount, vfacecount, tcount; - int index, shift; - int end1, end2; - int hitbdry, i, j, k; - - // Output Voronoi vertices to .v.node file. - if (out == (tetgenio *) NULL) { - strcpy(outfilename, b->outfilename); - strcat(outfilename, ".v.node"); + unsigned long n2 = 0; + int scale = 1; + while (n >= 1000) { + n2 = n2 + scale * (n % 1000); + n /= 1000; + scale *= 1000; } + printf ("%ld", n); + while (scale != 1) { + scale /= 1000; + n = n2 / scale; + n2 = n2 % scale; + printf (",%03ld", n); + } +} - if (!b->quiet) { - if (out == (tetgenio *) NULL) { - printf("Writing %s.\n", outfilename); - } else { - printf("Writing Voronoi vertices.\n"); - } - } +/////////////////////////////////////////////////////////////////////////////// +// // +// checkmesh() Test the mesh for topological consistency. // +// // +// If 'topoflag' is set, only check the topological connection of the mesh, // +// i.e., do not report degenerated or inverted elements. // +// // +/////////////////////////////////////////////////////////////////////////////// - // Determine the first index (0 or 1). - shift = (b->zeroindex ? 0 : in->firstnumber); - // 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; - outfile = (FILE *) NULL; // Avoid compile warnings. +int tetgenmesh::checkmesh(int topoflag) +{ + triface tetloop, neightet, symtet; + point pa, pb, pc, pd; + REAL ori; + int horrors, i; - 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); - } - // Number of voronoi points, 3 dim, no attributes, no marker. - fprintf(outfile, "%ld 3 0 0\n", tetrahedrons->items); - } else { - // Allocate space for 'vpointlist'. - out->numberofvpoints = (int) tetrahedrons->items; - out->vpointlist = new REAL[out->numberofvpoints * 3]; - if (out->vpointlist == (REAL *) NULL) { - terminatetetgen(1); - } + if (!b->quiet) { + printf(" Checking consistency of mesh...\n"); } - // Loop the tetrahedronlist once, do the following: - // (1) Output Voronoi vertices (the circumcenter of the tetrahedron). - // (2) Make a map from points-to-tetrahedra (for Voronoi cells). + horrors = 0; + tetloop.ver = 0; + // Run through the list of tetrahedra, checking each one. tetrahedrons->traversalinit(); - tetloop.tet = tetrahedrontraverse(); - vpointcount = 0; - index = 0; + tetloop.tet = alltetrahedrontraverse(); while (tetloop.tet != (tetrahedron *) NULL) { - // Calculate the circumcenter. - for (i = 0; i < 4; i++) { - pt[i] = (point) tetloop.tet[4 + i]; - setpoint2tet(pt[i], encode(tetloop)); + // Check all four faces of the tetrahedron. + for (tetloop.ver = 0; tetloop.ver < 4; tetloop.ver++) { + pa = org(tetloop); + pb = dest(tetloop); + pc = apex(tetloop); + pd = oppo(tetloop); + if (tetloop.ver == 0) { // Only test for inversion once. + if (!ishulltet(tetloop)) { // Only do test if it is not a hull tet. + if (!topoflag) { + ori = orient3d(pa, pb, pc, pd); + if (ori >= 0.0) { + printf(" !! !! %s ", ori > 0.0 ? "Inverted" : "Degenerated"); + printf(" (%d, %d, %d, %d) (ori = %.17g)\n", pointmark(pa), + pointmark(pb), pointmark(pc), pointmark(pd), ori); + horrors++; + } + } + } + if (infected(tetloop)) { + // This may be a bug. Report it. + printf(" !! (%d, %d, %d, %d) is infected.\n", pointmark(pa), + pointmark(pb), pointmark(pc), pointmark(pd)); + horrors++; + } + if (marktested(tetloop)) { + // This may be a bug. Report it. + printf(" !! (%d, %d, %d, %d) is marked.\n", pointmark(pa), + pointmark(pb), pointmark(pc), pointmark(pd)); + horrors++; + } + } + if (tetloop.tet[tetloop.ver] == NULL) { + printf(" !! !! No neighbor at face (%d, %d, %d).\n", pointmark(pa), + pointmark(pb), pointmark(pc)); + horrors++; + } else { + // Find the neighboring tetrahedron on this face. + fsym(tetloop, neightet); + // Check that the tetrahedron's neighbor knows it's a neighbor. + fsym(neightet, symtet); + if ((tetloop.tet != symtet.tet) || (tetloop.ver != symtet.ver)) { + printf(" !! !! Asymmetric tetra-tetra bond:\n"); + if (tetloop.tet == symtet.tet) { + printf(" (Right tetrahedron, wrong orientation)\n"); + } + printf(" First: (%d, %d, %d, %d)\n", pointmark(pa), + pointmark(pb), pointmark(pc), pointmark(pd)); + printf(" Second: (%d, %d, %d, %d)\n", pointmark(org(neightet)), + pointmark(dest(neightet)), pointmark(apex(neightet)), + pointmark(oppo(neightet))); + horrors++; + } + // Check if they have the same edge (the bond() operation). + if ((org(neightet) != pb) || (dest(neightet) != pa)) { + printf(" !! !! Wrong edge-edge bond:\n"); + printf(" First: (%d, %d, %d, %d)\n", pointmark(pa), + pointmark(pb), pointmark(pc), pointmark(pd)); + printf(" Second: (%d, %d, %d, %d)\n", pointmark(org(neightet)), + pointmark(dest(neightet)), pointmark(apex(neightet)), + pointmark(oppo(neightet))); + horrors++; + } + // Check if they have the same apex. + if (apex(neightet) != pc) { + printf(" !! !! Wrong face-face bond:\n"); + printf(" First: (%d, %d, %d, %d)\n", pointmark(pa), + pointmark(pb), pointmark(pc), pointmark(pd)); + printf(" Second: (%d, %d, %d, %d)\n", pointmark(org(neightet)), + pointmark(dest(neightet)), pointmark(apex(neightet)), + pointmark(oppo(neightet))); + horrors++; + } + // Check if they have the same opposite. + if (oppo(neightet) == pd) { + printf(" !! !! Two identical tetra:\n"); + printf(" First: (%d, %d, %d, %d)\n", pointmark(pa), + pointmark(pb), pointmark(pc), pointmark(pd)); + printf(" Second: (%d, %d, %d, %d)\n", pointmark(org(neightet)), + pointmark(dest(neightet)), pointmark(apex(neightet)), + pointmark(oppo(neightet))); + horrors++; + } + } + if (facemarked(tetloop)) { + // This may be a bug. Report it. + printf(" !! tetface (%d, %d, %d) %d is marked.\n", pointmark(pa), + pointmark(pb), pointmark(pc), pointmark(pd)); + } } - circumsphere(pt[0], pt[1], pt[2], pt[3], ccent, NULL); - if (out == (tetgenio *) NULL) { - fprintf(outfile, "%4d %16.8e %16.8e %16.8e\n", vpointcount + shift, - ccent[0], ccent[1], ccent[2]); - } else { - out->vpointlist[index++] = ccent[0]; - out->vpointlist[index++] = ccent[1]; - out->vpointlist[index++] = ccent[2]; + // Check the six edges of this tet. + for (i = 0; i < 6; i++) { + tetloop.ver = edge2ver[i]; + if (edgemarked(tetloop)) { + // This may be a bug. Report it. + printf(" !! tetedge (%d, %d) %d, %d is marked.\n", + pointmark(org(tetloop)), pointmark(dest(tetloop)), + pointmark(apex(tetloop)), pointmark(oppo(tetloop))); + } } - // Remember the index of this element. - * (int *) (tetloop.tet + elemmarkerindex) = vpointcount; - vpointcount++; - tetloop.tet = tetrahedrontraverse(); + tetloop.tet = alltetrahedrontraverse(); } - // Set the outside element marker. - * (int *) (dummytet + elemmarkerindex) = -1; - - if (out == (tetgenio *) NULL) { - fprintf(outfile, "# Generated by %s\n", b->commandline); - fclose(outfile); + if (horrors == 0) { + if (!b->quiet) { + printf(" In my studied opinion, the mesh appears to be consistent.\n"); + } + } else { + printf(" !! !! !! !! %d %s witnessed.\n", horrors, + horrors > 1 ? "abnormity" : "abnormities"); } - // Output Voronoi edges to .v.edge file. - if (out == (tetgenio *) NULL) { - strcpy(outfilename, b->outfilename); - strcat(outfilename, ".v.edge"); - } - + return horrors; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// checkshells() Test the boundary mesh for topological consistency. // +// // +/////////////////////////////////////////////////////////////////////////////// + +int tetgenmesh::checkshells() +{ + triface neightet, symtet; + face shloop, spinsh, nextsh; + face checkseg; + point pa, pb; + int bakcount; + int horrors, i; + if (!b->quiet) { - if (out == (tetgenio *) NULL) { - printf("Writing %s.\n", outfilename); - } else { - printf("Writing Voronoi edges.\n"); + printf(" Checking consistency of the mesh boundary...\n"); + } + horrors = 0; + + void **bakpathblock = subfaces->pathblock; + void *bakpathitem = subfaces->pathitem; + int bakpathitemsleft = subfaces->pathitemsleft; + int bakalignbytes = subfaces->alignbytes; + + subfaces->traversalinit(); + shloop.sh = shellfacetraverse(subfaces); + while (shloop.sh != NULL) { + shloop.shver = 0; + for (i = 0; i < 3; i++) { + // Check the face ring at this edge. + pa = sorg(shloop); + pb = sdest(shloop); + spinsh = shloop; + spivot(spinsh, nextsh); + bakcount = horrors; + while ((nextsh.sh != NULL) && (nextsh.sh != shloop.sh)) { + if (nextsh.sh[3] == NULL) { + printf(" !! !! Wrong subface-subface connection (Dead subface).\n"); + printf(" First: x%lx (%d, %d, %d).\n", (uintptr_t) spinsh.sh, + pointmark(sorg(spinsh)), pointmark(sdest(spinsh)), + pointmark(sapex(spinsh))); + printf(" Second: x%lx (DEAD)\n", (uintptr_t) nextsh.sh); + horrors++; + break; + } + // check if they have the same edge. + if (!(((sorg(nextsh) == pa) && (sdest(nextsh) == pb)) || + ((sorg(nextsh) == pb) && (sdest(nextsh) == pa)))) { + printf(" !! !! Wrong subface-subface connection.\n"); + printf(" First: x%lx (%d, %d, %d).\n", (uintptr_t) spinsh.sh, + pointmark(sorg(spinsh)), pointmark(sdest(spinsh)), + pointmark(sapex(spinsh))); + printf(" Scond: x%lx (%d, %d, %d).\n", (uintptr_t) nextsh.sh, + pointmark(sorg(nextsh)), pointmark(sdest(nextsh)), + pointmark(sapex(nextsh))); + horrors++; + break; + } + // Check they should not have the same apex. + if (sapex(nextsh) == sapex(spinsh)) { + printf(" !! !! Existing two duplicated subfaces.\n"); + printf(" First: x%lx (%d, %d, %d).\n", (uintptr_t) spinsh.sh, + pointmark(sorg(spinsh)), pointmark(sdest(spinsh)), + pointmark(sapex(spinsh))); + printf(" Scond: x%lx (%d, %d, %d).\n", (uintptr_t) nextsh.sh, + pointmark(sorg(nextsh)), pointmark(sdest(nextsh)), + pointmark(sapex(nextsh))); + horrors++; + break; + } + spinsh = nextsh; + spivot(spinsh, nextsh); + } + // Check subface-subseg bond. + sspivot(shloop, checkseg); + if (checkseg.sh != NULL) { + if (checkseg.sh[3] == NULL) { + printf(" !! !! Wrong subface-subseg connection (Dead subseg).\n"); + printf(" Sub: x%lx (%d, %d, %d).\n", (uintptr_t) shloop.sh, + pointmark(sorg(shloop)), pointmark(sdest(shloop)), + pointmark(sapex(shloop))); + printf(" Sub: x%lx (Dead)\n", (uintptr_t) checkseg.sh); + horrors++; + } else { + if (!(((sorg(checkseg) == pa) && (sdest(checkseg) == pb)) || + ((sorg(checkseg) == pb) && (sdest(checkseg) == pa)))) { + printf(" !! !! Wrong subface-subseg connection.\n"); + printf(" Sub: x%lx (%d, %d, %d).\n", (uintptr_t) shloop.sh, + pointmark(sorg(shloop)), pointmark(sdest(shloop)), + pointmark(sapex(shloop))); + printf(" Seg: x%lx (%d, %d).\n", (uintptr_t) checkseg.sh, + pointmark(sorg(checkseg)), pointmark(sdest(checkseg))); + horrors++; + } + } + } + if (horrors > bakcount) break; // An error detected. + senextself(shloop); + } + // Check tet-subface connection. + stpivot(shloop, neightet); + if (neightet.tet != NULL) { + if (neightet.tet[4] == NULL) { + printf(" !! !! Wrong sub-to-tet connection (Dead tet)\n"); + printf(" Sub: x%lx (%d, %d, %d).\n", (uintptr_t) shloop.sh, + pointmark(sorg(shloop)), pointmark(sdest(shloop)), + pointmark(sapex(shloop))); + printf(" Tet: x%lx (DEAD)\n", (uintptr_t) neightet.tet); + horrors++; + } else { + if (!((sorg(shloop) == org(neightet)) && + (sdest(shloop) == dest(neightet)))) { + printf(" !! !! Wrong sub-to-tet connection\n"); + printf(" Sub: x%lx (%d, %d, %d).\n", (uintptr_t) shloop.sh, + pointmark(sorg(shloop)), pointmark(sdest(shloop)), + pointmark(sapex(shloop))); + printf(" Tet: x%lx (%d, %d, %d, %d).\n", + (uintptr_t) neightet.tet, pointmark(org(neightet)), + pointmark(dest(neightet)), pointmark(apex(neightet)), + pointmark(oppo(neightet))); + horrors++; + } + tspivot(neightet, spinsh); + if (!((sorg(spinsh) == org(neightet)) && + (sdest(spinsh) == dest(neightet)))) { + printf(" !! !! Wrong tet-sub connection.\n"); + printf(" Sub: x%lx (%d, %d, %d).\n", (uintptr_t) spinsh.sh, + pointmark(sorg(spinsh)), pointmark(sdest(spinsh)), + pointmark(sapex(spinsh))); + printf(" Tet: x%lx (%d, %d, %d, %d).\n", + (uintptr_t) neightet.tet, pointmark(org(neightet)), + pointmark(dest(neightet)), pointmark(apex(neightet)), + pointmark(oppo(neightet))); + horrors++; + } + fsym(neightet, symtet); + tspivot(symtet, spinsh); + if (spinsh.sh != NULL) { + if (!((sorg(spinsh) == org(symtet)) && + (sdest(spinsh) == dest(symtet)))) { + printf(" !! !! Wrong tet-sub connection.\n"); + printf(" Sub: x%lx (%d, %d, %d).\n", (uintptr_t) spinsh.sh, + pointmark(sorg(spinsh)), pointmark(sdest(spinsh)), + pointmark(sapex(spinsh))); + printf(" Tet: x%lx (%d, %d, %d, %d).\n", + (uintptr_t) symtet.tet, pointmark(org(symtet)), + pointmark(dest(symtet)), pointmark(apex(symtet)), + pointmark(oppo(symtet))); + horrors++; + } + } else { + printf(" Warning: Broken tet-sub-tet connection.\n"); + } + } + } + if (sinfected(shloop)) { + // This may be a bug. report it. + printf(" !! A infected subface: (%d, %d, %d).\n", + pointmark(sorg(shloop)), pointmark(sdest(shloop)), + pointmark(sapex(shloop))); } + if (smarktested(shloop)) { + // This may be a bug. report it. + printf(" !! A marked subface: (%d, %d, %d).\n", pointmark(sorg(shloop)), + pointmark(sdest(shloop)), pointmark(sapex(shloop))); + } + shloop.sh = shellfacetraverse(subfaces); } - 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); + if (horrors == 0) { + if (!b->quiet) { + printf(" Mesh boundaries connected correctly.\n"); } - // Number of Voronoi edges, no marker. - fprintf(outfile, "%ld 0\n", faces); } else { - // Allocate space for 'vpointlist'. - out->numberofvedges = (int) faces; - out->vedgelist = new tetgenio::voroedge[out->numberofvedges]; + printf(" !! !! !! !! %d boundary connection viewed with horror.\n", + horrors); + } + + subfaces->pathblock = bakpathblock; + subfaces->pathitem = bakpathitem; + subfaces->pathitemsleft = bakpathitemsleft; + subfaces->alignbytes = bakalignbytes; + + return horrors; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// checksegments() Check the connections between tetrahedra and segments. // +// // +/////////////////////////////////////////////////////////////////////////////// + +int tetgenmesh::checksegments() +{ + triface tetloop, neightet, spintet; + shellface *segs; + face neighsh, spinsh, checksh; + face sseg, checkseg; + point pa, pb; + int miscount; + int t1ver; + int horrors, i; + + + if (!b->quiet) { + printf(" Checking tet->seg connections...\n"); } - // Loop the tetrahedronlist once, output the Voronoi edges. The index of - // each Voronoi edge corresponding to the index of the Delaunay face. - // The four faces' indices of each tetrahedron are saved in the list - // 'tetfaceindexarray', in the entry of i, where i (0-based) is the - // index of this tetrahedron (= vpointcount). - tetfaceindexarray = new int[tetrahedrons->items * 4]; + horrors = 0; tetrahedrons->traversalinit(); tetloop.tet = tetrahedrontraverse(); - vedgecount = 0; - index = 0; - while (tetloop.tet != (tetrahedron *) NULL) { - // Count the number of Voronoi edges. Look at the four faces of each - // tetrahedron. Count the face if the tetrahedron's pointer is - // smaller than its neighbor's or the neighbor is outside. - end1 = * (int *) (tetloop.tet + elemmarkerindex); - for (i = 0; i < 4; i++) { - decode(tetloop.tet[i], worktet); - if ((worktet.tet == dummytet) || (tetloop.tet < worktet.tet)) { - if (out == (tetgenio *) NULL) { - fprintf(outfile, "%4d %4d", vedgecount + shift, end1 + shift); - } else { - vedge = &(out->vedgelist[index++]); - vedge->v1 = end1 + shift; - } - end2 = * (int *) (worktet.tet + elemmarkerindex); - // Note that end2 may be -1 (worktet.tet is outside). - if (end2 == -1) { - // Calculate the out normal of this hull face. - worktet.tet = tetloop.tet; - worktet.loc = i; - worktet.ver = 1; // The CW edge ring. - pt[0] = org(worktet); - pt[1] = dest(worktet); - pt[2] = apex(worktet); - for (j = 0; j < 3; j++) vec1[j] = pt[1][j] - pt[0][j]; - for (j = 0; j < 3; j++) vec2[j] = pt[2][j] - pt[0][j]; - cross(vec1, vec2, infvec); - // Normalize it. - L = sqrt(infvec[0] * infvec[0] + infvec[1] * infvec[1] - + infvec[2] * infvec[2]); - if (L > 0) for (j = 0; j < 3; j++) infvec[j] /= L; - if (out == (tetgenio *) NULL) { - fprintf(outfile, " -1"); - fprintf(outfile, " %g %g %g\n", infvec[0], infvec[1], infvec[2]); + while (tetloop.tet != NULL) { + // Loop the six edges of the tet. + if (tetloop.tet[8] != NULL) { + segs = (shellface *) tetloop.tet[8]; + for (i = 0; i < 6; i++) { + sdecode(segs[i], sseg); + if (sseg.sh != NULL) { + // Get the edge of the tet. + tetloop.ver = edge2ver[i]; + // Check if they are the same edge. + pa = (point) sseg.sh[3]; + pb = (point) sseg.sh[4]; + 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", + (uintptr_t) tetloop.tet, pointmark(org(tetloop)), + pointmark(dest(tetloop)), pointmark(apex(tetloop)), + pointmark(oppo(tetloop)), (uintptr_t) sseg.sh, + pointmark(pa), pointmark(pb)); + horrors++; } else { - vedge->v2 = -1; - vedge->vnormal[0] = infvec[0]; - vedge->vnormal[1] = infvec[1]; - vedge->vnormal[2] = infvec[2]; + // Loop all tets sharing at this edge. + neightet = tetloop; + do { + tsspivot1(neightet, checkseg); + if (checkseg.sh != sseg.sh) { + printf(" !! Wrong tet->seg connection.\n"); + printf(" Tet: x%lx (%d, %d, %d, %d) - ", + (uintptr_t) neightet.tet, pointmark(org(neightet)), + pointmark(dest(neightet)), pointmark(apex(neightet)), + pointmark(oppo(neightet))); + if (checkseg.sh != NULL) { + printf("Seg x%lx (%d, %d).\n", (uintptr_t) checkseg.sh, + pointmark(sorg(checkseg)),pointmark(sdest(checkseg))); + } else { + printf("Seg: NULL.\n"); + } + horrors++; + } + fnextself(neightet); + } while (neightet.tet != tetloop.tet); } - } else { - if (out == (tetgenio *) NULL) { - fprintf(outfile, " %4d\n", end2 + shift); + // Check the seg->tet pointer. + sstpivot1(sseg, neightet); + if (neightet.tet == NULL) { + printf(" !! Wrong seg->tet connection (A NULL tet).\n"); + horrors++; } else { - vedge->v2 = end2 + shift; - vedge->vnormal[0] = 0.0; - vedge->vnormal[1] = 0.0; - vedge->vnormal[2] = 0.0; + if (!(((org(neightet) == pa) && (dest(neightet) == pb)) || + ((org(neightet) == pb) && (dest(neightet) == pa)))) { + printf(" !! Wrong seg->tet connection (Wrong edge).\n"); + printf(" Tet: x%lx (%d, %d, %d, %d) - Seg: x%lx (%d, %d).\n", + (uintptr_t) neightet.tet, pointmark(org(neightet)), + pointmark(dest(neightet)), pointmark(apex(neightet)), + pointmark(oppo(neightet)), (uintptr_t) sseg.sh, + pointmark(pa), pointmark(pb)); + horrors++; + } } } - // Save the face index in this tet and its neighbor if exists. - tetfaceindexarray[end1 * 4 + i] = vedgecount; - if (end2 != -1) { - tetfaceindexarray[end2 * 4 + worktet.loc] = vedgecount; + } + } + // Loop the six edge of this tet. + neightet.tet = tetloop.tet; + for (i = 0; i < 6; i++) { + neightet.ver = edge2ver[i]; + if (edgemarked(neightet)) { + // A possible bug. Report it. + printf(" !! A marked edge: (%d, %d, %d, %d) -- x%lx %d.\n", + pointmark(org(neightet)), pointmark(dest(neightet)), + pointmark(apex(neightet)), pointmark(oppo(neightet)), + (uintptr_t) neightet.tet, neightet.ver); + // Check if all tets at the edge are marked. + spintet = neightet; + while (1) { + fnextself(spintet); + if (!edgemarked(spintet)) { + printf(" !! !! An unmarked edge (%d, %d, %d, %d) -- x%lx %d.\n", + pointmark(org(spintet)), pointmark(dest(spintet)), + pointmark(apex(spintet)), pointmark(oppo(spintet)), + (uintptr_t) spintet.tet, spintet.ver); + horrors++; + } + if (spintet.tet == neightet.tet) break; } - vedgecount++; } } tetloop.tet = tetrahedrontraverse(); } - if (out == (tetgenio *) NULL) { - fprintf(outfile, "# Generated by %s\n", b->commandline); - fclose(outfile); + if (!b->quiet) { + printf(" Checking seg->tet connections...\n"); } - // Output Voronoi faces to .v.face file. - if (out == (tetgenio *) NULL) { - strcpy(outfilename, b->outfilename); - strcat(outfilename, ".v.face"); - } - - if (!b->quiet) { - if (out == (tetgenio *) NULL) { - printf("Writing %s.\n", outfilename); - } else { - printf("Writing Voronoi faces.\n"); + miscount = 0; // Count the number of unrecovered segments. + subsegs->traversalinit(); + sseg.shver = 0; + sseg.sh = shellfacetraverse(subsegs); + while (sseg.sh != NULL) { + pa = sorg(sseg); + pb = sdest(sseg); + spivot(sseg, neighsh); + if (neighsh.sh != NULL) { + spinsh = neighsh; + while (1) { + // Check seg-subface bond. + if (((sorg(spinsh) == pa) && (sdest(spinsh) == pb)) || + ((sorg(spinsh) == pb) && (sdest(spinsh) == pa))) { + // Keep the same rotate direction. + //if (sorg(spinsh) != pa) { + // sesymself(spinsh); + // printf(" !! Wrong ori at subface (%d, %d, %d) -- x%lx %d\n", + // pointmark(sorg(spinsh)), pointmark(sdest(spinsh)), + // pointmark(sapex(spinsh)), (uintptr_t) spinsh.sh, + // spinsh.shver); + // horrors++; + //} + stpivot(spinsh, spintet); + if (spintet.tet != NULL) { + // Check if all tets at this segment. + while (1) { + tsspivot1(spintet, checkseg); + if (checkseg.sh == NULL) { + printf(" !! !! No seg at tet (%d, %d, %d, %d) -- x%lx %d\n", + pointmark(org(spintet)), pointmark(dest(spintet)), + pointmark(apex(spintet)), pointmark(oppo(spintet)), + (uintptr_t) spintet.tet, spintet.ver); + horrors++; + } + if (checkseg.sh != sseg.sh) { + printf(" !! !! Wrong seg (%d, %d) at tet (%d, %d, %d, %d)\n", + pointmark(sorg(checkseg)), pointmark(sdest(checkseg)), + pointmark(org(spintet)), pointmark(dest(spintet)), + pointmark(apex(spintet)), pointmark(oppo(spintet))); + horrors++; + } + fnextself(spintet); + // Stop at the next subface. + tspivot(spintet, checksh); + if (checksh.sh != NULL) break; + } // while (1) + } + } else { + printf(" !! Wrong seg-subface (%d, %d, %d) -- x%lx %d connect\n", + pointmark(sorg(spinsh)), pointmark(sdest(spinsh)), + pointmark(sapex(spinsh)), (uintptr_t) spinsh.sh, + spinsh.shver); + horrors++; + break; + } // if pa, pb + spivotself(spinsh); + if (spinsh.sh == NULL) break; // A dangling segment. + if (spinsh.sh == neighsh.sh) break; + } // while (1) + } // if (neighsh.sh != NULL) + // Count the number of "un-recovered" segments. + sstpivot1(sseg, neightet); + if (neightet.tet == NULL) { + miscount++; } + sseg.sh = shellfacetraverse(subsegs); } - 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); - } - // Number of Voronoi faces. - fprintf(outfile, "%ld 0\n", edges); - } else { - out->numberofvfacets = edges; - out->vfacetlist = new tetgenio::vorofacet[out->numberofvfacets]; - if (out->vfacetlist == (tetgenio::vorofacet *) NULL) { - terminatetetgen(1); - } + if (!b->quiet) { + printf(" Checking seg->seg connections...\n"); } - // Loop the tetrahedronlist once, Output Voronoi facets. The index of each - // Voronoi facet corresponding to the index of the Delaunay edge. The - // six edges' indices of each tetrahedron are saved in the list 'tetedge- - // indexarray', in the entry of i, where i (0-based) is the index of - // this tetrahedron (= vpointcount). - tetedgeindexarray = new int[tetrahedrons->items * 6]; - tetrahedrons->traversalinit(); - tetloop.tet = tetrahedrontraverse(); - vfacecount = 0; - 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 = tetloop; - for (i = 0; i < 6; i++) { - worktet.loc = edge2locver[i][0]; - worktet.ver = edge2locver[i][1]; - // Now count the number of tets surrounding this edge. - tcount = 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; - tcount++; - } 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) { - // Get the two endpoints of this edge. - pt[0] = org(worktet); - pt[1] = dest(worktet); - end1 = pointmark(pt[0]) - in->firstnumber; - end2 = pointmark(pt[1]) - in->firstnumber; - if (out == (tetgenio *) NULL) { - fprintf(outfile, "%4d %4d %4d %-2d ", vfacecount + shift, - end1 + shift, end2 + shift, tcount + (hitbdry > 0)); - } else { - vfacet = &(out->vfacetlist[vfacecount]); - vfacet->c1 = end1 + shift; - vfacet->c2 = end2 + shift; - vfacet->elist = new int[tcount + (hitbdry > 0) + 1]; - vfacet->elist[0] = tcount + (hitbdry > 0); - index = 1; - } - // If hitbdry > 0, then spintet is a hull face. - if (hitbdry > 0) { - // The edge list starts with a ray. - vpointcount = * (int *) (spintet.tet + elemmarkerindex); - vedgecount = tetfaceindexarray[vpointcount * 4 + spintet.loc]; - if (out == (tetgenio *) NULL) { - fprintf(outfile, " %d", vedgecount + shift); + points->traversalinit(); + pa = pointtraverse(); + while (pa != NULL) { + if (pointtype(pa) == FREESEGVERTEX) { + // There should be two subsegments connected at 'pa'. + // Get a subsegment containing 'pa'. + sdecode(point2sh(pa), sseg); + if ((sseg.sh == NULL) || sseg.sh[3] == NULL) { + printf(" !! Dead point-to-seg pointer at point %d.\n", + pointmark(pa)); + horrors++; + } else { + sseg.shver = 0; + if (sorg(sseg) != pa) { + if (sdest(sseg) != pa) { + printf(" !! Wrong point-to-seg pointer at point %d.\n", + pointmark(pa)); + horrors++; } else { - vfacet->elist[index++] = vedgecount + shift; - } - // Save this facet number in tet. - tetedgeindexarray[vpointcount * 6 + - locver2edge[spintet.loc][spintet.ver]] = vfacecount; - esymself(spintet); - fnextself(spintet); // In the same tet. - } - // Output internal Voronoi edges. - for (j = 0; j < tcount; j++) { - vpointcount = * (int *) (spintet.tet + elemmarkerindex); - vedgecount = tetfaceindexarray[vpointcount * 4 + spintet.loc]; - if (out == (tetgenio *) NULL) { - fprintf(outfile, " %d", vedgecount + shift); + // Find the next subsegment at 'pa'. + senext(sseg, checkseg); + if ((checkseg.sh == NULL) || (checkseg.sh[3] == NULL)) { + printf(" !! Dead seg-seg connection at point %d.\n", + pointmark(pa)); + horrors++; + } else { + spivotself(checkseg); + checkseg.shver = 0; + if (sorg(checkseg) != pa) { + printf(" !! Wrong seg-seg connection at point %d.\n", + pointmark(pa)); + horrors++; + } + } + } + } else { + // Find the previous subsegment at 'pa'. + senext2(sseg, checkseg); + if ((checkseg.sh == NULL) || (checkseg.sh[3] == NULL)) { + printf(" !! Dead seg-seg connection at point %d.\n", + pointmark(pa)); + horrors++; } else { - vfacet->elist[index++] = vedgecount + shift; + spivotself(checkseg); + checkseg.shver = 0; + if (sdest(checkseg) != pa) { + printf(" !! Wrong seg-seg connection at point %d.\n", + pointmark(pa)); + horrors++; + } } - // Save this facet number in tet. - tetedgeindexarray[vpointcount * 6 + - locver2edge[spintet.loc][spintet.ver]] = vfacecount; - fnextself(spintet); - } - if (out == (tetgenio *) NULL) { - fprintf(outfile, "\n"); } - vfacecount++; } - } // if (i = 0; i < 6; i++) - tetloop.tet = tetrahedrontraverse(); - } - - if (out == (tetgenio *) NULL) { - fprintf(outfile, "# Generated by %s\n", b->commandline); - fclose(outfile); + } + pa = pointtraverse(); } - // Output Voronoi cells to .v.cell file. - if (out == (tetgenio *) NULL) { - strcpy(outfilename, b->outfilename); - strcat(outfilename, ".v.cell"); + if (horrors == 0) { + printf(" Segments are connected properly.\n"); + } else { + printf(" !! !! !! !! Found %d missing connections.\n", horrors); } - - if (!b->quiet) { - if (out == (tetgenio *) NULL) { - printf("Writing %s.\n", outfilename); - } else { - printf("Writing Voronoi cells.\n"); - } + if (miscount > 0) { + printf(" !! !! Found %d missing segments.\n", miscount); } - 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); - } - // Number of Voronoi cells. - fprintf(outfile, "%ld\n", points->items); - } else { - out->numberofvcells = points->items; - out->vcelllist = new int*[out->numberofvcells]; - if (out->vcelllist == (int **) NULL) { - terminatetetgen(1); - } + return horrors; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// checkdelaunay() Ensure that the mesh is (constrained) Delaunay. // +// // +/////////////////////////////////////////////////////////////////////////////// + +int tetgenmesh::checkdelaunay() +{ + triface tetloop; + triface symtet; + face checksh; + point pa, pb, pc, pd, pe; + REAL sign; + int ndcount; // Count the non-locally Delaunay faces. + int horrors; + + if (!b->quiet) { + printf(" Checking Delaunay property of the mesh...\n"); } - // Loop through point list, for each point, output a Voronoi cell. - tetlist = new list(sizeof(triface), NULL, 256); - ptlist = new list(sizeof(point *), NULL, 256); - points->traversalinit(); - ptloop = pointtraverse(); - vpointcount = 0; - while (ptloop != (point) NULL) { - decode(point2tet(ptloop), tetloop); - // assert(!isdead(&tetloop)); - if (!isdead(&tetloop)) { - // Form the star of p. - tetlist->append(&tetloop); - formstarpolyhedron(ptloop, tetlist, ptlist, true); - tcount = ptlist->len(); - if (out == (tetgenio *) NULL) { - fprintf(outfile, "%4d %-2d ", vpointcount + shift, tcount); - } else { - arraysize = tcount; - vertarray = new int[arraysize + 1]; - out->vcelllist[vpointcount] = vertarray; - vertarray[0] = arraysize; - index = 1; - } - // List Voronoi facets bounding this cell. - for (i = 0; i < ptlist->len(); i++) { - neipt = * (point *)(* ptlist)[i]; - // Find a tet in tetlist having edge (ptloop, neipt) -- Very Slow. - for (j = 0; j < tetlist->len(); j++) { - tetloop = * (triface *)(* tetlist)[j]; - for (k = 0; k < 6; k++) { - tetloop.loc = edge2locver[k][0]; - tetloop.ver = edge2locver[k][1]; - if (org(tetloop) == ptloop) { - if (dest(tetloop) == neipt) break; - } else if (org(tetloop) == neipt) { - if (dest(tetloop) == ptloop) break; - } + ndcount = 0; + horrors = 0; + tetloop.ver = 0; + // Run through the list of triangles, checking each one. + tetrahedrons->traversalinit(); + tetloop.tet = tetrahedrontraverse(); + while (tetloop.tet != (tetrahedron *) NULL) { + // Check all four faces of the tetrahedron. + for (tetloop.ver = 0; tetloop.ver < 4; tetloop.ver++) { + fsym(tetloop, symtet); + // Only do test if its adjoining tet is not a hull tet or its pointer + // is larger (to ensure that each pair isn't tested twice). + if (((point) symtet.tet[7] != dummypoint)&&(tetloop.tet < symtet.tet)) { + pa = org(tetloop); + pb = dest(tetloop); + pc = apex(tetloop); + pd = oppo(tetloop); + pe = oppo(symtet); + sign = insphere_s(pa, pb, pc, pd, pe); + if (sign < 0.0) { + ndcount++; + if (checksubfaceflag) { + tspivot(tetloop, checksh); + } + if (checksh.sh == NULL) { + printf(" !! Non-locally Delaunay (%d, %d, %d) - %d, %d\n", + pointmark(pa), pointmark(pb), pointmark(pc), pointmark(pd), + pointmark(pe)); + horrors++; } - 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(); + tetloop.tet = tetrahedrontraverse(); } - delete tetlist; - delete ptlist; - delete [] tetfaceindexarray; - delete [] tetedgeindexarray; - if (out == (tetgenio *) NULL) { - fprintf(outfile, "# Generated by %s\n", b->commandline); - fclose(outfile); + if (horrors == 0) { + if (!b->quiet) { + if (ndcount > 0) { + printf(" The mesh is constrained Delaunay.\n"); + } else { + printf(" The mesh is Delaunay.\n"); + } + } + } else { + printf(" !! !! !! !! Found %d non-Delaunay faces.\n", horrors); } + + return horrors; } /////////////////////////////////////////////////////////////////////////////// // // -// outsmesh() Write surface mesh to a .smesh file, which can be read and // -// tetrahedralized by TetGen. // +// Check if the current tetrahedralization is (constrained) regular. // // // -// You can specify a filename (without suffix) in 'smfilename'. If you don't // -// supply a filename (let smfilename be NULL), the default name stored in // -// 'tetgenbehavior' will be used. // +// The parameter 'type' determines which regularity should be checked: // +// - 0: check the Delaunay property. // +// - 1: check the Delaunay property with symbolic perturbation. // +// - 2: check the regular property, the weights are stored in p[3]. // +// - 3: check the regular property with symbolic perturbation. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::outsmesh(char* smfilename) +int tetgenmesh::checkregular(int type) { - FILE *outfile; - char nodfilename[FILENAMESIZE]; - char smefilename[FILENAMESIZE]; - face faceloop; - point p1, p2, p3; - int firstindex, shift; - int bmark; - int faceid, marker; - int i; - - if (smfilename != (char *) NULL && smfilename[0] != '\0') { - strcpy(smefilename, smfilename); - } else if (b->outfilename[0] != '\0') { - strcpy(smefilename, b->outfilename); - } else { - strcpy(smefilename, "unnamed"); - } - strcpy(nodfilename, smefilename); - strcat(smefilename, ".smesh"); - strcat(nodfilename, ".node"); + triface tetloop; + triface symtet; + face checksh; + point p[5]; + REAL sign; + int ndcount; // Count the non-locally Delaunay faces. + int horrors; if (!b->quiet) { - printf("Writing %s.\n", smefilename); - } - outfile = fopen(smefilename, "w"); - if (outfile == (FILE *) NULL) { - printf("File I/O Error: Cannot create file %s.\n", smefilename); - return; - } - - // 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. + printf(" Checking %s %s property of the mesh...\n", + (type & 2) == 0 ? "Delaunay" : "regular", + (type & 1) == 0 ? " " : "(s)"); } - fprintf(outfile, "# %s. TetGen's input file.\n", smefilename); - fprintf(outfile, "\n# part 1: node list.\n"); - fprintf(outfile, "0 3 0 0 # nodes are found in %s.\n", nodfilename); + // Make sure orient3d(p[1], p[0], p[2], p[3]) > 0; + // Hence if (insphere(p[1], p[0], p[2], p[3], p[4]) > 0) means that + // p[4] lies inside the circumsphere of p[1], p[0], p[2], p[3]. + // The same if orient4d(p[1], p[0], p[2], p[3], p[4]) > 0 means that + // p[4] lies below the oriented hyperplane passing through + // p[1], p[0], p[2], p[3]. - marker = 0; // avoid compile warning. - bmark = !b->nobound && in->facetmarkerlist; + ndcount = 0; + horrors = 0; + tetloop.ver = 0; + // Run through the list of triangles, checking each one. + tetrahedrons->traversalinit(); + tetloop.tet = tetrahedrontraverse(); + while (tetloop.tet != (tetrahedron *) NULL) { + // Check all four faces of the tetrahedron. + for (tetloop.ver = 0; tetloop.ver < 4; tetloop.ver++) { + fsym(tetloop, symtet); + // Only do test if its adjoining tet is not a hull tet or its pointer + // is larger (to ensure that each pair isn't tested twice). + if (((point) symtet.tet[7] != dummypoint)&&(tetloop.tet < symtet.tet)) { + p[0] = org(tetloop); // pa + p[1] = dest(tetloop); // pb + p[2] = apex(tetloop); // pc + p[3] = oppo(tetloop); // pd + p[4] = oppo(symtet); // pe + + if (type == 0) { + sign = insphere(p[1], p[0], p[2], p[3], p[4]); + } else if (type == 1) { + sign = insphere_s(p[1], p[0], p[2], p[3], p[4]); + } else if (type == 2) { + sign = orient4d(p[1], p[0], p[2], p[3], p[4], + p[1][3], p[0][3], p[2][3], p[3][3], p[4][3]); + } else { // type == 3 + sign = orient4d_s(p[1], p[0], p[2], p[3], p[4], + p[1][3], p[0][3], p[2][3], p[3][3], p[4][3]); + } - fprintf(outfile, "\n# part 2: facet list.\n"); - // Number of facets, boundary marker. - fprintf(outfile, "%ld %d\n", subfaces->items, bmark); - - subfaces->traversalinit(); - faceloop.sh = shellfacetraverse(subfaces); - while (faceloop.sh != (shellface *) NULL) { - p1 = sorg(faceloop); - p2 = sdest(faceloop); - p3 = sapex(faceloop); - if (bmark) { - faceid = shellmark(faceloop) - 1; - if (faceid >= 0) { - marker = in->facetmarkerlist[faceid]; - } else { - marker = 0; // This subface must be added manually later. + if (sign > 0.0) { + ndcount++; + if (checksubfaceflag) { + tspivot(tetloop, checksh); + } + if (checksh.sh == NULL) { + printf(" !! Non-locally %s (%d, %d, %d) - %d, %d\n", + (type & 2) == 0 ? "Delaunay" : "regular", + pointmark(p[0]), pointmark(p[1]), pointmark(p[2]), + pointmark(p[3]), pointmark(p[4])); + horrors++; + } + } } } - fprintf(outfile, "3 %4d %4d %4d", pointmark(p1) - shift, - pointmark(p2) - shift, pointmark(p3) - shift); - if (bmark) { - fprintf(outfile, " %d", marker); - } - fprintf(outfile, "\n"); - faceloop.sh = shellfacetraverse(subfaces); + tetloop.tet = tetrahedrontraverse(); } - // Copy input holelist. - fprintf(outfile, "\n# part 3: hole list.\n"); - fprintf(outfile, "%d\n", in->numberofholes); - for (i = 0; i < in->numberofholes; i++) { - fprintf(outfile, "%d %g %g %g\n", i + in->firstnumber, - in->holelist[i * 3], in->holelist[i * 3 + 1], - in->holelist[i * 3 + 2]); - } - - // Copy input regionlist. - fprintf(outfile, "\n# part 4: region list.\n"); - fprintf(outfile, "%d\n", in->numberofregions); - for (i = 0; i < in->numberofregions; i++) { - fprintf(outfile, "%d %g %g %g %d %g\n", i + in->firstnumber, - in->regionlist[i * 5], in->regionlist[i * 5 + 1], - in->regionlist[i * 5 + 2], (int) in->regionlist[i * 5 + 3], - in->regionlist[i * 5 + 4]); + if (horrors == 0) { + if (!b->quiet) { + if (ndcount > 0) { + printf(" The mesh is constrained %s.\n", + (type & 2) == 0 ? "Delaunay" : "regular"); + } else { + printf(" The mesh is %s.\n", (type & 2) == 0 ? "Delaunay" : "regular"); + } + } + } else { + printf(" !! !! !! !! Found %d non-%s faces.\n", horrors, + (type & 2) == 0 ? "Delaunay" : "regular"); } - fprintf(outfile, "# Generated by %s\n", b->commandline); - fclose(outfile); + return horrors; } /////////////////////////////////////////////////////////////////////////////// // // -// outmesh2medit() Write mesh to a .mesh file, which can be read and // -// rendered by Medit (a free mesh viewer from INRIA). // +// checkconforming() Ensure that the mesh is conforming Delaunay. // // // -// You can specify a filename (without suffix) in 'mfilename'. If you don't // -// supply a filename (let mfilename be NULL), the default name stored in // -// 'tetgenbehavior' will be used. The output file will have the suffix .mesh.// +// If 'flag' is 1, only check subsegments. If 'flag' is 2, check subfaces. // +// If 'flag' is 3, check both subsegments and subfaces. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::outmesh2medit(char* mfilename) +int tetgenmesh::checkconforming(int flag) { - FILE *outfile; - char mefilename[FILENAMESIZE]; - tetrahedron* tetptr; - triface tface, tsymface; - face segloop, checkmark; - point ptloop, p1, p2, p3, p4; - long faces; - int pointnumber; + triface searchtet, neightet, spintet; + face shloop; + face segloop; + point eorg, edest, eapex, pa, pb, pc; + REAL cent[3], radius, dist, diff, rd, len; + bool enq; + int encsubsegs, encsubfaces; + int t1ver; int i; - if (mfilename != (char *) NULL && mfilename[0] != '\0') { - strcpy(mefilename, mfilename); - } else if (b->outfilename[0] != '\0') { - strcpy(mefilename, b->outfilename); - } else { - strcpy(mefilename, "unnamed"); - } - strcat(mefilename, ".mesh"); - - if (!b->quiet) { - printf("Writing %s.\n", mefilename); - } - outfile = fopen(mefilename, "w"); - if (outfile == (FILE *) NULL) { - printf("File I/O Error: Cannot create file %s.\n", mefilename); - return; - } - - fprintf(outfile, "MeshVersionFormatted 1\n"); - fprintf(outfile, "\n"); - fprintf(outfile, "Dimension\n"); - fprintf(outfile, "3\n"); - fprintf(outfile, "\n"); + REAL A[4][4], rhs[4], D; + int indx[4]; + REAL elen[3]; - fprintf(outfile, "\n# Set of mesh vertices\n"); - fprintf(outfile, "Vertices\n"); - fprintf(outfile, "%ld\n", points->items); + encsubsegs = 0; - points->traversalinit(); - ptloop = pointtraverse(); - pointnumber = 1; // Medit need start number form 1. - while (ptloop != (point) NULL) { - // Point coordinates. - fprintf(outfile, "%.17g %.17g %.17g", ptloop[0], ptloop[1], ptloop[2]); - if (in->numberofpointattributes > 0) { - // Write an attribute, ignore others if more than one. - fprintf(outfile, " %.17g\n", ptloop[3]); - } else { - fprintf(outfile, " 0\n"); + if (flag & 1) { + if (!b->quiet) { + printf(" Checking conforming property of segments...\n"); } - setpointmark(ptloop, pointnumber); - ptloop = pointtraverse(); - pointnumber++; - } - - // Compute the number of edges. - faces = (4l * tetrahedrons->items + hullsize) / 2l; - - fprintf(outfile, "\n# Set of Triangles\n"); - fprintf(outfile, "Triangles\n"); - fprintf(outfile, "%ld\n", faces); + encsubsegs = 0; - tetrahedrons->traversalinit(); - tface.tet = tetrahedrontraverse(); - // To loop over the set of faces, loop over all tetrahedra, and look at - // the four faces of each tetrahedron. If there isn't another tetrahedron - // adjacent to the face, operate on the face. If there is another adj- - // acent tetrahedron, operate on the face only if the current tetrahedron - // has a smaller pointer than its neighbor. This way, each face is - // considered only once. - while (tface.tet != (tetrahedron *) NULL) { - for (tface.loc = 0; tface.loc < 4; tface.loc ++) { - sym(tface, tsymface); - if (tface.tet < tsymface.tet || tsymface.tet == dummytet) { - p1 = org (tface); - p2 = dest(tface); - p3 = apex(tface); - fprintf(outfile, "%5d %5d %5d", - pointmark(p1), pointmark(p2), pointmark(p3)); - fprintf(outfile, " 0\n"); + // Run through the list of subsegments, check each one. + subsegs->traversalinit(); + segloop.sh = shellfacetraverse(subsegs); + while (segloop.sh != (shellface *) NULL) { + eorg = (point) segloop.sh[3]; + edest = (point) segloop.sh[4]; + radius = 0.5 * distance(eorg, edest); + for (i = 0; i < 3; i++) cent[i] = 0.5 * (eorg[i] + edest[i]); + + enq = false; + sstpivot1(segloop, neightet); + if (neightet.tet != NULL) { + spintet = neightet; + while (1) { + eapex= apex(spintet); + if (eapex != dummypoint) { + dist = distance(eapex, cent); + diff = dist - radius; + if (fabs(diff) / radius <= b->epsilon) diff = 0.0; // Rounding. + if (diff < 0) { + enq = true; break; + } + } + fnextself(spintet); + if (spintet.tet == neightet.tet) break; + } + } + if (enq) { + printf(" !! !! Non-conforming segment: (%d, %d)\n", + pointmark(eorg), pointmark(edest)); + encsubsegs++; } + segloop.sh = shellfacetraverse(subsegs); } - tface.tet = tetrahedrontraverse(); - } - - fprintf(outfile, "\n# Set of Tetrahedra\n"); - fprintf(outfile, "Tetrahedra\n"); - fprintf(outfile, "%ld\n", tetrahedrons->items); - tetrahedrons->traversalinit(); - tetptr = tetrahedrontraverse(); - while (tetptr != (tetrahedron *) NULL) { - p1 = (point) tetptr[4]; - p2 = (point) tetptr[5]; - p3 = (point) tetptr[6]; - p4 = (point) tetptr[7]; - fprintf(outfile, "%5d %5d %5d %5d", - pointmark(p1), pointmark(p2), pointmark(p3), pointmark(p4)); - if (in->numberoftetrahedronattributes > 0) { - fprintf(outfile, " %.17g", elemattribute(tetptr, 0)); + if (encsubsegs == 0) { + if (!b->quiet) { + printf(" The segments are conforming Delaunay.\n"); + } } else { - fprintf(outfile, " 0"); + printf(" !! !! %d subsegments are non-conforming.\n", encsubsegs); } - fprintf(outfile, "\n"); - tetptr = tetrahedrontraverse(); - } + } // if (flag & 1) - fprintf(outfile, "\nCorners\n"); - fprintf(outfile, "%d\n", in->numberofpoints); + encsubfaces = 0; - for (i = 0; i < in->numberofpoints; i++) { - fprintf(outfile, "%4d\n", i + 1); - } + if (flag & 2) { + if (!b->quiet) { + printf(" Checking conforming property of subfaces...\n"); + } - if (b->useshelles) { - fprintf(outfile, "\nEdges\n"); - fprintf(outfile, "%ld\n", subsegs->items); + // Run through the list of subfaces, check each one. + subfaces->traversalinit(); + shloop.sh = shellfacetraverse(subfaces); + while (shloop.sh != (shellface *) NULL) { + pa = (point) shloop.sh[3]; + pb = (point) shloop.sh[4]; + pc = (point) shloop.sh[5]; + + // 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]; // vector V1 (pa->pb) + A[1][0] = pc[0] - pa[0]; + A[1][1] = pc[1] - pa[1]; + A[1][2] = pc[2] - pa[2]; // vector V2 (pa->pc) + cross(A[0], A[1], A[2]); // vector V3 (V1 X V2) + + // Compute the right hand side vector b (3x1). + elen[0] = dot(A[0], A[0]); + elen[1] = dot(A[1], A[1]); + rhs[0] = 0.5 * elen[0]; + rhs[1] = 0.5 * elen[1]; + rhs[2] = 0.0; + + if (lu_decmp(A, 3, indx, &D, 0)) { + lu_solve(A, 3, indx, rhs, 0); + cent[0] = pa[0] + rhs[0]; + cent[1] = pa[1] + rhs[1]; + cent[2] = pa[2] + rhs[2]; + rd = sqrt(rhs[0] * rhs[0] + rhs[1] * rhs[1] + rhs[2] * rhs[2]); + + // Check if this subface is encroached. + for (i = 0; i < 2; i++) { + stpivot(shloop, searchtet); + if (!ishulltet(searchtet)) { + len = distance(oppo(searchtet), cent); + if ((fabs(len - rd) / rd) < b->epsilon) len = rd; // Rounding. + if (len < rd) { + printf(" !! !! Non-conforming subface: (%d, %d, %d)\n", + pointmark(pa), pointmark(pb), pointmark(pc)); + encsubfaces++; + enq = true; break; + } + } + sesymself(shloop); + } + } + shloop.sh = shellfacetraverse(subfaces); + } - subsegs->traversalinit(); - segloop.sh = shellfacetraverse(subsegs); - while (segloop.sh != (shellface *) NULL) { - p1 = sorg(segloop); - p2 = sdest(segloop); - fprintf(outfile, "%5d %5d", pointmark(p1), pointmark(p2)); - fprintf(outfile, " 0\n"); - segloop.sh = shellfacetraverse(subsegs); + if (encsubfaces == 0) { + if (!b->quiet) { + printf(" The subfaces are conforming Delaunay.\n"); + } + } else { + printf(" !! !! %d subfaces are non-conforming.\n", encsubfaces); } - } + } // if (flag & 2) - fprintf(outfile, "\nEnd\n"); - fclose(outfile); + return encsubsegs + encsubfaces; } /////////////////////////////////////////////////////////////////////////////// // // -// outmesh2gid() Write mesh to a .ele.msh file and a .face.msh file, // -// which can be imported and rendered by Gid. // -// // -// You can specify a filename (without suffix) in 'gfilename'. If you don't // -// supply a filename (let gfilename be NULL), the default name stored in // -// 'tetgenbehavior' will be used. The suffixes (.ele.msh and .face.msh) will // -// be automatically added. // +// qualitystatistics() Print statistics about the quality of the mesh. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::outmesh2gid(char* gfilename) +void tetgenmesh::qualitystatistics() { - FILE *outfile; - char gidfilename[FILENAMESIZE]; - tetrahedron* tetptr; - triface tface, tsymface; - face sface; - point ptloop, p1, p2, p3, p4; - int pointnumber; - int elementnumber; + triface tetloop, neightet; + point p[4]; + char sbuf[128]; + REAL radiusratiotable[12]; + REAL aspectratiotable[12]; + REAL A[4][4], rhs[4], D; + REAL V[6][3], N[4][3], H[4]; // edge-vectors, face-normals, face-heights. + REAL edgelength[6], alldihed[6], faceangle[3]; + REAL shortest, longest; + REAL smallestvolume, biggestvolume; + REAL smallestratio, biggestratio; + REAL smallestdiangle, biggestdiangle; + REAL smallestfaangle, biggestfaangle; + REAL total_tet_vol, total_tetprism_vol; + REAL tetvol, minaltitude; + REAL cirradius, minheightinv; // insradius; + REAL shortlen, longlen; + REAL tetaspect, tetradius; + REAL smalldiangle, bigdiangle; + REAL smallfaangle, bigfaangle; + unsigned long radiustable[12]; + unsigned long aspecttable[16]; + unsigned long dihedangletable[18]; + unsigned long faceangletable[18]; + int indx[4]; + int radiusindex; + int aspectindex; + int tendegree; + int i, j; - if (gfilename != (char *) NULL && gfilename[0] != '\0') { - strcpy(gidfilename, gfilename); - } else if (b->outfilename[0] != '\0') { - strcpy(gidfilename, b->outfilename); - } else { - strcpy(gidfilename, "unnamed"); - } - strcat(gidfilename, ".ele.msh"); + printf("Mesh quality statistics:\n\n"); - if (!b->quiet) { - printf("Writing %s.\n", gidfilename); - } - outfile = fopen(gidfilename, "w"); - if (outfile == (FILE *) NULL) { - printf("File I/O Error: Cannot create file %s.\n", gidfilename); - return; - } + shortlen = longlen = 0.0; + smalldiangle = bigdiangle = 0.0; + total_tet_vol = 0.0; + total_tetprism_vol = 0.0; - fprintf(outfile, "mesh dimension = 3 elemtype tetrahedron nnode = 4\n"); - fprintf(outfile, "coordinates\n"); + radiusratiotable[0] = 0.707; radiusratiotable[1] = 1.0; + radiusratiotable[2] = 1.1; radiusratiotable[3] = 1.2; + radiusratiotable[4] = 1.4; radiusratiotable[5] = 1.6; + radiusratiotable[6] = 1.8; radiusratiotable[7] = 2.0; + radiusratiotable[8] = 2.5; radiusratiotable[9] = 3.0; + radiusratiotable[10] = 10.0; radiusratiotable[11] = 0.0; - points->traversalinit(); - ptloop = pointtraverse(); - pointnumber = 1; // Gid need start number form 1. - while (ptloop != (point) NULL) { - // Point coordinates. - fprintf(outfile, "%4d %.17g %.17g %.17g", pointnumber, - ptloop[0], ptloop[1], ptloop[2]); - if (in->numberofpointattributes > 0) { - // Write an attribute, ignore others if more than one. - fprintf(outfile, " %.17g", ptloop[3]); - } - fprintf(outfile, "\n"); - setpointmark(ptloop, pointnumber); - ptloop = pointtraverse(); - pointnumber++; - } + aspectratiotable[0] = 1.5; aspectratiotable[1] = 2.0; + aspectratiotable[2] = 2.5; aspectratiotable[3] = 3.0; + aspectratiotable[4] = 4.0; aspectratiotable[5] = 6.0; + aspectratiotable[6] = 10.0; aspectratiotable[7] = 15.0; + aspectratiotable[8] = 25.0; aspectratiotable[9] = 50.0; + aspectratiotable[10] = 100.0; aspectratiotable[11] = 0.0; + + for (i = 0; i < 12; i++) radiustable[i] = 0l; + for (i = 0; i < 12; i++) aspecttable[i] = 0l; + for (i = 0; i < 18; i++) dihedangletable[i] = 0l; + for (i = 0; i < 18; i++) faceangletable[i] = 0l; - fprintf(outfile, "end coordinates\n"); - fprintf(outfile, "elements\n"); + minaltitude = xmax - xmin + ymax - ymin + zmax - zmin; + minaltitude = minaltitude * minaltitude; + shortest = minaltitude; + longest = 0.0; + smallestvolume = minaltitude; + biggestvolume = 0.0; + smallestratio = 1e+16; // minaltitude; + biggestratio = 0.0; + smallestdiangle = smallestfaangle = 180.0; + biggestdiangle = biggestfaangle = 0.0; - tetrahedrons->traversalinit(); - tetptr = tetrahedrontraverse(); - elementnumber = 1; - while (tetptr != (tetrahedron *) NULL) { - p1 = (point) tetptr[4]; - p2 = (point) tetptr[5]; - p3 = (point) tetptr[6]; - p4 = (point) tetptr[7]; - fprintf(outfile, "%5d %5d %5d %5d %5d", elementnumber, - pointmark(p1), pointmark(p2), pointmark(p3), pointmark(p4)); - if (in->numberoftetrahedronattributes > 0) { - fprintf(outfile, " %.17g", elemattribute(tetptr, 0)); - } - fprintf(outfile, "\n"); - tetptr = tetrahedrontraverse(); - elementnumber++; - } - fprintf(outfile, "end elements\n"); - fclose(outfile); - - if (gfilename != (char *) NULL && gfilename[0] != '\0') { - strcpy(gidfilename, gfilename); - } else if (b->outfilename[0] != '\0') { - strcpy(gidfilename, b->outfilename); - } else { - strcpy(gidfilename, "unnamed"); - } - strcat(gidfilename, ".face.msh"); - - if (!b->quiet) { - printf("Writing %s.\n", gidfilename); - } - outfile = fopen(gidfilename, "w"); - if (outfile == (FILE *) NULL) { - printf("File I/O Error: Cannot create file %s.\n", gidfilename); - return; - } - - fprintf(outfile, "mesh dimension = 3 elemtype triangle nnode = 3\n"); - fprintf(outfile, "coordinates\n"); - - points->traversalinit(); - ptloop = pointtraverse(); - pointnumber = 1; // Gid need start number form 1. - while (ptloop != (point) NULL) { - // Point coordinates. - fprintf(outfile, "%4d %.17g %.17g %.17g", pointnumber, - ptloop[0], ptloop[1], ptloop[2]); - if (in->numberofpointattributes > 0) { - // Write an attribute, ignore others if more than one. - fprintf(outfile, " %.17g", ptloop[3]); - } - fprintf(outfile, "\n"); - setpointmark(ptloop, pointnumber); - ptloop = pointtraverse(); - pointnumber++; - } - - fprintf(outfile, "end coordinates\n"); - fprintf(outfile, "elements\n"); + int attrnum = numelemattrib - 1; + // Loop all elements, calculate quality parameters for each element. tetrahedrons->traversalinit(); - tface.tet = tetrahedrontraverse(); - elementnumber = 1; - while (tface.tet != (tetrahedron *) NULL) { - for (tface.loc = 0; tface.loc < 4; tface.loc ++) { - sym(tface, tsymface); - if ((tface.tet < tsymface.tet) || (tsymface.tet == dummytet)) { - p1 = org(tface); - p2 = dest(tface); - p3 = apex(tface); - if (tsymface.tet == dummytet) { - // It's a hull face, output it. - fprintf(outfile, "%5d %d %d %d\n", elementnumber, - pointmark(p1), pointmark(p2), pointmark(p3)); - elementnumber++; - } else if (b->useshelles) { - // Only output it if it's a subface. - tspivot(tface, sface); - if (sface.sh != dummysh) { - fprintf(outfile, "%5d %d %d %d\n", elementnumber, - pointmark(p1), pointmark(p2), pointmark(p3)); - elementnumber++; - } - } + tetloop.tet = tetrahedrontraverse(); + while (tetloop.tet != (tetrahedron *) NULL) { + + if (b->convex) { + // Skip tets in the exterior. + if (elemattribute(tetloop.tet, attrnum) == -1.0) { + tetloop.tet = tetrahedrontraverse(); + continue; } } - tface.tet = tetrahedrontraverse(); - } - fprintf(outfile, "end elements\n"); - fclose(outfile); -} + // Get four vertices: p0, p1, p2, p3. + for (i = 0; i < 4; i++) p[i] = (point) tetloop.tet[4 + i]; -/////////////////////////////////////////////////////////////////////////////// -// // -// outmesh2off() Write the mesh to an .off file. // -// // -// .off, the Object File Format, is one of the popular file formats from the // -// Geometry Center's Geomview package (http://www.geomview.org). // -// // -/////////////////////////////////////////////////////////////////////////////// + // Get the tet volume. + tetvol = orient3dfast(p[1], p[0], p[2], p[3]) / 6.0; + total_tet_vol += tetvol; + total_tetprism_vol += tetprismvol(p[0], p[1], p[2], p[3]); -void tetgenmesh::outmesh2off(char* ofilename) -{ - FILE *outfile; - char offfilename[FILENAMESIZE]; - triface tface, tsymface; - point ptloop, p1, p2, p3; - long faces; - int shift; + // Calculate the largest and smallest volume. + if (tetvol < smallestvolume) { + smallestvolume = tetvol; + } + if (tetvol > biggestvolume) { + biggestvolume = tetvol; + } - if (ofilename != (char *) NULL && ofilename[0] != '\0') { - strcpy(offfilename, ofilename); - } else if (b->outfilename[0] != '\0') { - strcpy(offfilename, b->outfilename); - } else { - strcpy(offfilename, "unnamed"); - } - strcat(offfilename, ".off"); + // Set the edge vectors: V[0], ..., V[5] + for (i = 0; i < 3; i++) V[0][i] = p[0][i] - p[3][i]; // V[0]: p3->p0. + for (i = 0; i < 3; i++) V[1][i] = p[1][i] - p[3][i]; // V[1]: p3->p1. + for (i = 0; i < 3; i++) V[2][i] = p[2][i] - p[3][i]; // V[2]: p3->p2. + for (i = 0; i < 3; i++) V[3][i] = p[1][i] - p[0][i]; // V[3]: p0->p1. + for (i = 0; i < 3; i++) V[4][i] = p[2][i] - p[1][i]; // V[4]: p1->p2. + for (i = 0; i < 3; i++) V[5][i] = p[0][i] - p[2][i]; // V[5]: p2->p0. - if (!b->quiet) { - printf("Writing %s.\n", offfilename); - } - outfile = fopen(offfilename, "w"); - if (outfile == (FILE *) NULL) { - printf("File I/O Error: Cannot create file %s.\n", offfilename); - return; - } + // Get the squares of the edge lengths. + for (i = 0; i < 6; i++) edgelength[i] = dot(V[i], V[i]); - // Calculate the number of triangular faces in the tetrahedral mesh. - faces = (4l * tetrahedrons->items + hullsize) / 2l; + // Calculate the longest and shortest edge length. + for (i = 0; i < 6; i++) { + if (i == 0) { + shortlen = longlen = edgelength[i]; + } else { + shortlen = edgelength[i] < shortlen ? edgelength[i] : shortlen; + longlen = edgelength[i] > longlen ? edgelength[i] : longlen; + } + if (edgelength[i] > longest) { + longest = edgelength[i]; + } + if (edgelength[i] < shortest) { + shortest = edgelength[i]; + } + } - // Number of points, faces, and edges(not used, here show hullsize). - fprintf(outfile, "OFF\n%ld %ld %ld\n", points->items, faces, hullsize); + // Set the matrix A = [V[0], V[1], V[2]]^T. + for (j = 0; j < 3; j++) { + for (i = 0; i < 3; i++) A[j][i] = V[j][i]; + } - // Write the points. - points->traversalinit(); - ptloop = pointtraverse(); - while (ptloop != (point) NULL) { - fprintf(outfile, " %.17g %.17g %.17g\n",ptloop[0], ptloop[1], ptloop[2]); - ptloop = pointtraverse(); - } + // Decompose A just once. + if (lu_decmp(A, 3, indx, &D, 0)) { + // Get the three faces normals. + 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 face 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]; + // Get the radius of the circumsphere. + for (i = 0; i < 3; i++) rhs[i] = 0.5 * dot(V[i], V[i]); + lu_solve(A, 3, indx, rhs, 0); + cirradius = sqrt(dot(rhs, rhs)); + // Normalize the face normals. + 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])); + 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]; + } + } else { + // A nearly degenerated tet. + if (tetvol <= 0.0) { + // assert(tetvol != 0.0); + printf(" !! Warning: A %s tet (%d,%d,%d,%d).\n", + tetvol < 0 ? "inverted" : "degenerated", pointmark(p[0]), + pointmark(p[1]), pointmark(p[2]), pointmark(p[3])); + // Skip it. + tetloop.tet = tetrahedrontraverse(); + continue; + } + // Calculate the four face normals. + facenormal(p[2], p[1], p[3], N[0], 1, NULL); + facenormal(p[0], p[2], p[3], N[1], 1, NULL); + facenormal(p[1], p[0], p[3], N[2], 1, NULL); + facenormal(p[0], p[1], p[2], N[3], 1, NULL); + // Normalize the face normals. + for (i = 0; i < 4; i++) { + // H[i] is the twice of the area of the face. + H[i] = sqrt(dot(N[i], N[i])); + for (j = 0; j < 3; j++) N[i][j] /= H[i]; + } + // Get the biggest H[i] / tetvol (corresponding to the smallest height). + minheightinv = (H[0] / tetvol); + for (i = 1; i < 3; i++) { + if ((H[i] / tetvol) > minheightinv) minheightinv = (H[i] / tetvol); + } + // Let the circumradius to be the half of its longest edge length. + cirradius = 0.5 * sqrt(longlen); + } - // OFF always use zero as the first index. - shift = in->firstnumber == 1 ? 1 : 0; + // Get the dihedrals (in degree) at each edges. + j = 0; + for (i = 1; i < 4; i++) { + alldihed[j] = -dot(N[0], N[i]); // Edge cd, bd, bc. + if (alldihed[j] < -1.0) alldihed[j] = -1; // Rounding. + else if (alldihed[j] > 1.0) alldihed[j] = 1; + alldihed[j] = acos(alldihed[j]) / PI * 180.0; + j++; + } + for (i = 2; i < 4; i++) { + alldihed[j] = -dot(N[1], N[i]); // Edge ad, ac. + if (alldihed[j] < -1.0) alldihed[j] = -1; // Rounding. + else if (alldihed[j] > 1.0) alldihed[j] = 1; + alldihed[j] = acos(alldihed[j]) / PI * 180.0; + j++; + } + alldihed[j] = -dot(N[2], N[3]); // Edge ab. + if (alldihed[j] < -1.0) alldihed[j] = -1; // Rounding. + else if (alldihed[j] > 1.0) alldihed[j] = 1; + alldihed[j] = acos(alldihed[j]) / PI * 180.0; - tetrahedrons->traversalinit(); - tface.tet = tetrahedrontraverse(); - // To loop over the set of faces, loop over all tetrahedra, and look at - // the four faces of each tetrahedron. If there isn't another tetrahedron - // adjacent to the face, operate on the face. If there is another adj- - // acent tetrahedron, operate on the face only if the current tetrahedron - // has a smaller pointer than its neighbor. This way, each face is - // considered only once. - while (tface.tet != (tetrahedron *) NULL) { - for (tface.loc = 0; tface.loc < 4; tface.loc ++) { - sym(tface, tsymface); - if ((tface.tet < tsymface.tet) || (tsymface.tet == dummytet)) { - p1 = org(tface); - p2 = dest(tface); - p3 = apex(tface); - // Face number, indices of three vertexs. - fprintf(outfile, "3 %4d %4d %4d\n", pointmark(p1) - shift, - pointmark(p2) - shift, pointmark(p3) - shift); + // Calculate the largest and smallest dihedral angles. + for (i = 0; i < 6; i++) { + if (i == 0) { + smalldiangle = bigdiangle = alldihed[i]; + } else { + smalldiangle = alldihed[i] < smalldiangle ? alldihed[i] : smalldiangle; + bigdiangle = alldihed[i] > bigdiangle ? alldihed[i] : bigdiangle; + } + if (alldihed[i] < smallestdiangle) { + smallestdiangle = alldihed[i]; + } + if (alldihed[i] > biggestdiangle) { + biggestdiangle = alldihed[i]; + } + // Accumulate the corresponding number in the dihedral angle histogram. + if (alldihed[i] < 5.0) { + tendegree = 0; + } else if (alldihed[i] >= 5.0 && alldihed[i] < 10.0) { + tendegree = 1; + } else if (alldihed[i] >= 80.0 && alldihed[i] < 110.0) { + tendegree = 9; // Angles between 80 to 110 degree are in one entry. + } else if (alldihed[i] >= 170.0 && alldihed[i] < 175.0) { + tendegree = 16; + } else if (alldihed[i] >= 175.0) { + tendegree = 17; + } else { + tendegree = (int) (alldihed[i] / 10.); + if (alldihed[i] < 80.0) { + tendegree++; // In the left column. + } else { + tendegree--; // In the right column. + } } + dihedangletable[tendegree]++; } - tface.tet = tetrahedrontraverse(); - } - - fprintf(outfile, "# Generated by %s\n", b->commandline); - fclose(outfile); -} -/////////////////////////////////////////////////////////////////////////////// -// // -// outmesh2vtk() Save mesh to file in VTK Legacy format. // -// // -// This function was contributed by Bryn Llyod from ETH, 2007. // -// // -/////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::outmesh2vtk(char* ofilename) -{ - 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; + // Calculate the largest and smallest face angles. + for (tetloop.ver = 0; tetloop.ver < 4; tetloop.ver++) { + fsym(tetloop, neightet); + // Only do the calulation once for a face. + if (((point) neightet.tet[7] == dummypoint) || + (tetloop.tet < neightet.tet)) { + p[0] = org(tetloop); + p[1] = dest(tetloop); + p[2] = apex(tetloop); + faceangle[0] = interiorangle(p[0], p[1], p[2], NULL); + faceangle[1] = interiorangle(p[1], p[2], p[0], NULL); + faceangle[2] = PI - (faceangle[0] + faceangle[1]); + // Translate angles into degrees. + for (i = 0; i < 3; i++) { + faceangle[i] = (faceangle[i] * 180.0) / PI; + } + // Calculate the largest and smallest face angles. + for (i = 0; i < 3; i++) { + if (i == 0) { + smallfaangle = bigfaangle = faceangle[i]; + } else { + smallfaangle = faceangle[i] < smallfaangle ? + faceangle[i] : smallfaangle; + bigfaangle = faceangle[i] > bigfaangle ? faceangle[i] : bigfaangle; + } + if (faceangle[i] < smallestfaangle) { + smallestfaangle = faceangle[i]; + } + if (faceangle[i] > biggestfaangle) { + biggestfaangle = faceangle[i]; + } + tendegree = (int) (faceangle[i] / 10.); + faceangletable[tendegree]++; + } + } + } - 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"); + // Calculate aspect ratio and radius-edge ratio for this element. + 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++; + } + aspecttable[aspectindex]++; + radiusindex = 0; + while ((tetradius > radiusratiotable[radiusindex]) && (radiusindex < 11)) { + radiusindex++; + } + radiustable[radiusindex]++; - 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; + tetloop.tet = tetrahedrontraverse(); } - //always write big endian - //bool ImALittleEndian = !testIsBigEndian(); + shortest = sqrt(shortest); + longest = sqrt(longest); + minaltitude = sqrt(minaltitude); - 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); + printf(" Smallest volume: %16.5g | Largest volume: %16.5g\n", + smallestvolume, biggestvolume); + printf(" Shortest edge: %16.5g | Longest edge: %16.5g\n", + shortest, longest); + printf(" Smallest asp.ratio: %13.5g | Largest asp.ratio: %13.5g\n", + smallestratio, biggestratio); + sprintf(sbuf, "%.17g", biggestfaangle); + if (strlen(sbuf) > 8) { + sbuf[8] = '\0'; + } + printf(" Smallest facangle: %14.5g | Largest facangle: %s\n", + smallestfaangle, sbuf); + sprintf(sbuf, "%.17g", biggestdiangle); + if (strlen(sbuf) > 8) { + sbuf[8] = '\0'; + } + printf(" Smallest dihedral: %14.5g | Largest dihedral: %s\n\n", + smallestdiangle, sbuf); - 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; + printf(" Face angle histogram:\n"); + for (i = 0; i < 9; i++) { + printf(" %3d - %3d degrees: %8ld | %3d - %3d degrees: %8ld\n", + i * 10, i * 10 + 10, faceangletable[i], + i * 10 + 90, i * 10 + 100, faceangletable[i + 9]); } - 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(); + if (minfaceang != PI) { + printf(" Minimum input face angle is %g (degree).\n", + minfaceang / PI * 180.0); } - fprintf(outfile, "\n"); + printf("\n"); - fprintf(outfile, "CELL_TYPES %d\n", NEL); - for(int tid=0; tidquiet) { - printf(" Checking consistency of mesh...\n"); + printf("Memory usage statistics:\n\n"); + + // Count the number of blocks of tetrahedra. + int tetblocks = 0; + tetrahedrons->pathblock = tetrahedrons->firstblock; + while (tetrahedrons->pathblock != NULL) { + tetblocks++; + tetrahedrons->pathblock = (void **) *(tetrahedrons->pathblock); + } + + // Calculate the total memory (in bytes) used by storing meshes. + unsigned long totalmeshmemory = 0l, totalt2shmemory = 0l; + totalmeshmemory = points->maxitems * points->itembytes + + tetrahedrons->maxitems * tetrahedrons->itembytes; + if (b->plc || b->refine) { + totalmeshmemory += (subfaces->maxitems * subfaces->itembytes + + subsegs->maxitems * subsegs->itembytes); + totalt2shmemory = (tet2subpool->maxitems * tet2subpool->itembytes + + tet2segpool->maxitems * tet2segpool->itembytes); } - horrors = 0; - // Run through the list of tetrahedra, checking each one. - tetrahedrons->traversalinit(); - tetraloop.tet = tetrahedrontraverse(); - while (tetraloop.tet != (tetrahedron *) NULL) { - // Check all four faces of the tetrahedron. - for (tetraloop.loc = 0; tetraloop.loc < 4; tetraloop.loc++) { - tetorg = org(tetraloop); - tetdest = dest(tetraloop); - tetapex = apex(tetraloop); - tetoppo = oppo(tetraloop); - if (tetraloop.loc == 0) { // Only test for inversion once. - oritest = orient3d(tetorg, tetdest, tetapex, tetoppo); - if (oritest >= 0.0) { - printf(" !! !! %s ", oritest > 0.0 ? "Inverted" : "Degenerated"); - printtet(&tetraloop); - printf(" orient3d = %.17g.\n", oritest); - horrors++; - } - } - // Find the neighboring tetrahedron on this face. - sym(tetraloop, oppotet); - if (oppotet.tet != dummytet) { - // Check if it is a dead tet. - if (!isdead(&oppotet)) { - // Check that the tetrahedron's neighbor knows it's a neighbor. - sym(oppotet, oppooppotet); - if ((tetraloop.tet != oppooppotet.tet) - || (tetraloop.loc != oppooppotet.loc)) { - printf(" !! !! Asymmetric tetra-tetra bond:\n"); - if (tetraloop.tet == oppooppotet.tet) { - printf(" (Right tetrahedron, wrong orientation)\n"); - } - printf(" First "); - printtet(&tetraloop); - printf(" Second (nonreciprocating) "); - printtet(&oppotet); - horrors++; - } - } else { - printf(" !! !! A dead neighbor:\n"); - printtet(&tetraloop); - horrors++; - } - } - } - if (infected(tetraloop)) { - pts = (point *) &(tetraloop.tet[4]); - printf(" !! tet (%d, %d, %d, %d) is infected.\n", pointmark(pts[0]), - pointmark(pts[1]), pointmark(pts[2]), pointmark(pts[3])); - horrors++; - } - if (marktested(tetraloop)) { - pts = (point *) &(tetraloop.tet[4]); - printf(" !! tet (%d, %d, %d, %d) is marktested.\n", pointmark(pts[0]), - pointmark(pts[1]), pointmark(pts[2]), pointmark(pts[3])); - horrors++; - } - tetraloop.tet = tetrahedrontraverse(); - } - if (horrors == 0) { - if (!b->quiet) { - printf(" In my studied opinion, the mesh appears to be consistent.\n"); - } - } else if (horrors == 1) { - printf(" !! !! !! !! Precisely one festering wound discovered.\n"); + unsigned long totalalgomemory = 0l; + totalalgomemory = cavetetlist->totalmemory + cavebdrylist->totalmemory + + caveoldtetlist->totalmemory + + flippool->maxitems * flippool->itembytes; + if (b->plc || b->refine) { + totalalgomemory += (subsegstack->totalmemory + subfacstack->totalmemory + + subvertstack->totalmemory + + caveshlist->totalmemory + caveshbdlist->totalmemory + + cavesegshlist->totalmemory + + cavetetshlist->totalmemory + + cavetetseglist->totalmemory + + caveencshlist->totalmemory + + caveencseglist->totalmemory + + cavetetvertlist->totalmemory + + unflipqueue->totalmemory); + } + + printf(" Maximum number of tetrahedra: %ld\n", tetrahedrons->maxitems); + printf(" Maximum number of tet blocks (blocksize = %d): %d\n", + b->tetrahedraperblock, tetblocks); + /* + if (b->plc || b->refine) { + printf(" Approximate memory for tetrahedral mesh (bytes): %ld\n", + totalmeshmemory); + + printf(" Approximate memory for extra pointers (bytes): %ld\n", + totalt2shmemory); } else { - printf(" !! !! !! !! %d abominations witnessed.\n", horrors); - } + printf(" Approximate memory for tetrahedralization (bytes): %ld\n", + totalmeshmemory); + } + printf(" Approximate memory for algorithms (bytes): %ld\n", + totalalgomemory); + printf(" Approximate memory for working arrays (bytes): %ld\n", + totalworkmemory); + printf(" Approximate total used memory (bytes): %ld\n", + totalmeshmemory + totalt2shmemory + totalalgomemory + + totalworkmemory); + */ + if (b->plc || b->refine) { + printf(" Approximate memory for tetrahedral mesh (bytes): "); + printfcomma(totalmeshmemory); printf("\n"); + + printf(" Approximate memory for extra pointers (bytes): "); + printfcomma(totalt2shmemory); printf("\n"); + } else { + printf(" Approximate memory for tetrahedralization (bytes): "); + printfcomma(totalmeshmemory); printf("\n"); + } + printf(" Approximate memory for algorithms (bytes): "); + printfcomma(totalalgomemory); printf("\n"); + printf(" Approximate memory for working arrays (bytes): "); + printfcomma(totalworkmemory); printf("\n"); + printf(" Approximate total used memory (bytes): "); + printfcomma(totalmeshmemory + totalt2shmemory + totalalgomemory + + totalworkmemory); + printf("\n"); - return horrors; + printf("\n"); } /////////////////////////////////////////////////////////////////////////////// // // -// checkshells() Test the boundary mesh for topological consistency. // +// statistics() Print all sorts of cool facts. // // // /////////////////////////////////////////////////////////////////////////////// -int tetgenmesh::checkshells() +void tetgenmesh::statistics() { - triface oppotet, oppooppotet, testtet; - face shloop, segloop, spin; - face testsh, testseg, testshsh; - point shorg, shdest, segorg, segdest; - REAL checksign; - bool same; - int horrors; - int i, j; + long tetnumber, facenumber; - if (!b->quiet) { - printf(" Checking consistency of the mesh boundary...\n"); + printf("\nStatistics:\n\n"); + printf(" Input points: %d\n", in->numberofpoints); + if (b->refine) { + printf(" Input tetrahedra: %d\n", in->numberoftetrahedra); + } + if (b->plc) { + printf(" Input facets: %d\n", in->numberoffacets); + printf(" Input segments: %ld\n", insegments); + printf(" Input holes: %d\n", in->numberofholes); + printf(" Input regions: %d\n", in->numberofregions); } - horrors = 0; - // Run through the list of subfaces, checking each one. - subfaces->traversalinit(); - shloop.sh = shellfacetraverse(subfaces); - while (shloop.sh != (shellface *) NULL) { - // Check two connected tetrahedra if they exist. - 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"); - printf(" Tetra: "); - printtet(&oppotet); - printf(" Subface: "); - printsh(&shloop); - horrors++; - } - if (oppo(oppotet) != (point) NULL) { - adjustedgering(oppotet, CCW); - checksign = orient3d(sorg(shloop), sdest(shloop), sapex(shloop), - oppo(oppotet)); - if (checksign >= 0.0) { - printf(" !! !! Wrong subface orientation.\n"); - printf(" Subface: "); - printsh(&shloop); - horrors++; - } - } + tetnumber = tetrahedrons->items - hullsize; + facenumber = (tetnumber * 4l + hullsize) / 2l; + + if (b->weighted) { // -w option + printf("\n Mesh points: %ld\n", points->items - nonregularcount); + } else { + printf("\n Mesh points: %ld\n", points->items); + } + printf(" Mesh tetrahedra: %ld\n", tetnumber); + printf(" Mesh faces: %ld\n", facenumber); + if (meshedges > 0l) { + printf(" Mesh edges: %ld\n", meshedges); + } else { + if (!nonconvex) { + long vsize = points->items - dupverts - unuverts; + if (b->weighted) vsize -= nonregularcount; + meshedges = vsize + facenumber - tetnumber - 1; + printf(" Mesh edges: %ld\n", meshedges); } - sesymself(shloop); - stpivot(shloop, oppooppotet); - if (oppooppotet.tet != dummytet) { - tspivot(oppooppotet, testsh); - if (testsh.sh != shloop.sh) { - printf(" !! !! Wrong tetra-subface connection.\n"); - printf(" Tetra: "); - printtet(&oppooppotet); - printf(" Subface: "); - printsh(&shloop); - horrors++; - } - if (oppotet.tet != dummytet) { - sym(oppotet, testtet); - if (testtet.tet != oppooppotet.tet) { - printf(" !! !! Wrong tetra-subface-tetra connection.\n"); - printf(" Tetra 1: "); - printtet(&oppotet); - printf(" Subface: "); - printsh(&shloop); - printf(" Tetra 2: "); - printtet(&oppooppotet); - horrors++; - } - } - if (oppo(oppooppotet) != (point) NULL) { - adjustedgering(oppooppotet, CCW); - checksign = orient3d(sorg(shloop), sdest(shloop), sapex(shloop), - oppo(oppooppotet)); - if (checksign >= 0.0) { - printf(" !! !! Wrong subface orientation.\n"); - printf(" Subface: "); - printsh(&shloop); - horrors++; - } - } + } + + if (b->plc || b->refine) { + printf(" Mesh faces on facets: %ld\n", subfaces->items); + printf(" Mesh edges on segments: %ld\n", subsegs->items); + if (st_volref_count > 0l) { + printf(" Steiner points inside domain: %ld\n", st_volref_count); } - // Check connection between subfaces. - shloop.shver = 0; - for (i = 0; i < 3; i++) { - shorg = sorg(shloop); - shdest = sdest(shloop); - sspivot(shloop, testseg); - if (testseg.sh != dummysh) { - segorg = sorg(testseg); - segdest = sdest(testseg); - same = ((shorg == segorg) && (shdest == segdest)) - || ((shorg == segdest) && (shdest == segorg)); - if (!same) { - printf(" !! !! Wrong subface-subsegment connection.\n"); - printf(" Subface: "); - printsh(&shloop); - printf(" Subsegment: "); - printsh(&testseg); - horrors++; - } - } - 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)) - || ((shorg == segdest) && (shdest == segorg)); - if (!same) { - printf(" !! !! Wrong subface-subface connection.\n"); - printf(" Subface 1: "); - printsh(&shloop); - printf(" Subface 2: "); - printsh(&testsh); - horrors++; - } - spivot(testsh, testshsh); - shorg = sorg(testshsh); - shdest = sdest(testshsh); - same = ((shorg == segorg) && (shdest == segdest)) - || ((shorg == segdest) && (shdest == segorg)); - if (!same) { - printf(" !! !! Wrong subface-subface connection.\n"); - printf(" Subface 1: "); - printsh(&testsh); - printf(" Subface 2: "); - printsh(&testshsh); - horrors++; - } - if (testseg.sh == dummysh) { - if (testshsh.sh != shloop.sh) { - printf(" !! !! Wrong subface-subface connection.\n"); - printf(" Subface 1: "); - printsh(&shloop); - printf(" Subface 2: "); - printsh(&testsh); - horrors++; - } - } - } - senextself(shloop); + if (st_facref_count > 0l) { + printf(" Steiner points on facets: %ld\n", st_facref_count); } - 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++; - } + if (st_segref_count > 0l) { + printf(" Steiner points on segments: %ld\n", st_segref_count); + } + } else { + printf(" Convex hull faces: %ld\n", hullsize); + if (meshhulledges > 0l) { + printf(" Convex hull edges: %ld\n", meshhulledges); } - shloop.sh = shellfacetraverse(subfaces); } - - if (horrors > 0) { - return horrors; + if (b->weighted) { // -w option + printf(" Skipped non-regular points: %ld\n", nonregularcount); } + printf("\n"); - // Run through the list of subsegs, checking each one. - subsegs->traversalinit(); - segloop.sh = shellfacetraverse(subsegs); - while (segloop.sh != (shellface *) NULL) { - segorg = sorg(segloop); - segdest = sdest(segloop); - spivot(segloop, testsh); - if (testsh.sh == dummysh) { - printf(" !! !! Wrong subsegment-subface connection.\n"); - printf(" Subsegment: "); - printsh(&segloop); - horrors++; - segloop.sh = shellfacetraverse(subsegs); - continue; - } - shorg = sorg(testsh); - shdest = sdest(testsh); - 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++; - segloop.sh = shellfacetraverse(subsegs); - continue; - } - // Check the connection of face loop around this subsegment. - spin = testsh; - 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 { - break; + + if (b->verbose > 0) { + if (b->plc || b->refine) { // -p or -r + if (tetrahedrons->items > 0l) { + qualitystatistics(); } - } while (spin.sh != testsh.sh && i < 1000); - if (i >= 1000) { - printf(" !! !! Wrong subsegment-subface connection.\n"); - printf(" Subsegment : "); - printsh(&segloop); - horrors++; } - segloop.sh = shellfacetraverse(subsegs); - } - if (horrors == 0) { - if (!b->quiet) { - printf(" Mesh boundaries connected correctly.\n"); + if (tetrahedrons->items > 0l) { + memorystatistics(); } - } else { - printf(" !! !! !! !! %d boundary connection viewed with horror.\n", - horrors); } - return horrors; } +//// //// +//// //// +//// meshstat_cxx ///////////////////////////////////////////////////////////// + +//// output_cxx /////////////////////////////////////////////////////////////// +//// //// +//// //// + /////////////////////////////////////////////////////////////////////////////// // // -// checksegments() Check the connections between tetrahedra and segments. // +// jettisonnodes() Jettison unused or duplicated vertices. // +// // +// Unused points are those input points which are outside the mesh domain or // +// have no connection (isolated) to the mesh. Duplicated points exist for // +// example if the input PLC is read from a .stl mesh file (marked during the // +// Delaunay tetrahedralization step. This routine remove these points from // +// points list. All existing points are reindexed. // // // /////////////////////////////////////////////////////////////////////////////// -int tetgenmesh::checksegments() +void tetgenmesh::jettisonnodes() { - triface tetloop, neightet; - face sseg, checkseg; - point pa, pb; - int hitbdry; - int horrors, i; + point pointloop; + bool jetflag; + int oldidx, newidx; + int remcount; if (!b->quiet) { - printf(" Checking tet-seg connections...\n"); + printf("Jettisoning redundant points.\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); - } + points->traversalinit(); + pointloop = pointtraverse(); + oldidx = newidx = 0; // in->firstnumber; + remcount = 0; + while (pointloop != (point) NULL) { + jetflag = (pointtype(pointloop) == DUPLICATEDVERTEX) || + (pointtype(pointloop) == UNUSEDVERTEX); + if (jetflag) { + // It is a duplicated or unused point, delete it. + pointdealloc(pointloop); + remcount++; + } else { + // Re-index it. + setpointmark(pointloop, newidx + in->firstnumber); + if (in->pointmarkerlist != (int *) NULL) { + if (oldidx < in->numberofpoints) { + // Re-index the point marker as well. + in->pointmarkerlist[newidx] = in->pointmarkerlist[oldidx]; } } + newidx++; } - tetloop.tet = tetrahedrontraverse(); + oldidx++; + pointloop = pointtraverse(); } - - if (horrors == 0) { - printf(" Segments are connected properly.\n"); - } else { - printf(" !! !! !! !! Found %d missing connections.\n", horrors); + if (b->verbose) { + printf(" %ld duplicated vertices are removed.\n", dupverts); + printf(" %ld unused vertices are removed.\n", unuverts); } + dupverts = 0l; + unuverts = 0l; - return horrors; + // The following line ensures that dead items in the pool of nodes cannot + // be allocated for the new created nodes. This ensures that the input + // nodes will occur earlier in the output files, and have lower indices. + points->deaditemstack = (void *) NULL; } /////////////////////////////////////////////////////////////////////////////// // // -// checkdelaunay() Ensure that the mesh is constrained Delaunay. // +// highorder() Create extra nodes for quadratic subparametric elements. // // // -// If 'flipqueue' is not NULL, non-locally Delaunay faces are saved in it. // +// 'highordertable' is an array (size = numberoftetrahedra * 6) for storing // +// high-order nodes of each tetrahedron. This routine is used only when -o2 // +// switch is used. // // // /////////////////////////////////////////////////////////////////////////////// -int tetgenmesh::checkdelaunay(REAL eps, queue* flipqueue) +void tetgenmesh::highorder() { - triface tetraloop; - triface oppotet; - face opposhelle; - point tetorg, tetdest, tetapex, tetoppo; - point oppooppo; - REAL sign; - int shouldbedelaunay; - int horrors; + triface tetloop, worktet, spintet; + point *extralist, *adjextralist; + point torg, tdest, newpoint; + int highorderindex; + int t1ver; + int i, j; if (!b->quiet) { - printf(" Checking Delaunay property of the mesh...\n"); + printf("Adding vertices for second-order tetrahedra.\n"); } - horrors = 0; - // Run through the list of triangles, checking each one. - tetrahedrons->traversalinit(); - tetraloop.tet = tetrahedrontraverse(); - while (tetraloop.tet != (tetrahedron *) NULL) { - // Check all four faces of the tetrahedron. - for (tetraloop.loc = 0; tetraloop.loc < 4; tetraloop.loc++) { - tetorg = org(tetraloop); - tetdest = dest(tetraloop); - tetapex = apex(tetraloop); - tetoppo = oppo(tetraloop); - sym(tetraloop, oppotet); - oppooppo = oppo(oppotet); - // Only do testif there is an adjoining tetrahedron whose pointer is - // larger (to ensure that each pair isn't tested twice). - shouldbedelaunay = (oppotet.tet != dummytet) - && (tetoppo != (point) NULL) - && (oppooppo != (point) NULL) - && (tetraloop.tet < oppotet.tet); - if (checksubfaces && shouldbedelaunay) { - // If a shell face separates the tetrahedra, then the face is - // constrained, so no local Delaunay test should be done. - tspivot(tetraloop, opposhelle); - if (opposhelle.sh != dummysh){ - shouldbedelaunay = 0; - } - } - if (shouldbedelaunay) { - sign = insphere(tetdest, tetorg, tetapex, tetoppo, oppooppo); - if ((sign > 0.0) && (eps > 0.0)) { - if (iscospheric(tetdest, tetorg, tetapex, tetoppo, oppooppo, sign, - eps)) sign = 0.0; - } - if (sign > 0.0) { - if (flipqueue) { - enqueueflipface(tetraloop, flipqueue); - } - horrors++; - } - } - } - tetraloop.tet = tetrahedrontraverse(); + + // Initialize the 'highordertable'. + highordertable = new point[tetrahedrons->items * 6]; + if (highordertable == (point *) NULL) { + terminatetetgen(this, 1); } - if (flipqueue == (queue *) NULL) { - if (horrors == 0) { - if (!b->quiet) { - printf(" The mesh is %s.\n", - checksubfaces ? "constrained Delaunay" : "Delaunay"); - } - } else { - printf(" !! !! !! !! %d obscenities viewed with horror.\n", horrors); + // This will overwrite the slot for element markers. + highorderindex = 11; + + // The following line ensures that dead items in the pool of nodes cannot + // be allocated for the extra nodes associated with high order elements. + // This ensures that the primary nodes (at the corners of elements) will + // occur earlier in the output files, and have lower indices, than the + // extra nodes. + points->deaditemstack = (void *) NULL; + + // Assign an entry for each tetrahedron to find its extra nodes. At the + // mean while, initialize all extra nodes be NULL. + i = 0; + tetrahedrons->traversalinit(); + tetloop.tet = tetrahedrontraverse(); + while (tetloop.tet != (tetrahedron *) NULL) { + tetloop.tet[highorderindex] = (tetrahedron) &highordertable[i]; + for (j = 0; j < 6; j++) { + highordertable[i + j] = (point) NULL; } + i += 6; + tetloop.tet = tetrahedrontraverse(); } - return horrors; + // To create a unique node on each edge. Loop over all tetrahedra, and + // look at the six edges of each tetrahedron. If the extra node in + // the tetrahedron corresponding to this edge is NULL, create a node + // for this edge, at the same time, set the new node into the extra + // node lists of all other tetrahedra sharing this edge. + tetrahedrons->traversalinit(); + tetloop.tet = tetrahedrontraverse(); + while (tetloop.tet != (tetrahedron *) NULL) { + // Get the list of extra nodes. + extralist = (point *) tetloop.tet[highorderindex]; + worktet.tet = tetloop.tet; + for (i = 0; i < 6; i++) { + if (extralist[i] == (point) NULL) { + // Go to the ith-edge. + worktet.ver = edge2ver[i]; + // Create a new point in the middle of this edge. + torg = org(worktet); + tdest = dest(worktet); + makepoint(&newpoint, FREEVOLVERTEX); + for (j = 0; j < 3 + numpointattrib; j++) { + newpoint[j] = 0.5 * (torg[j] + tdest[j]); + } + // Interpolate its metrics. + for (j = 0; j < in->numberofpointmtrs; j++) { + newpoint[pointmtrindex + j] = + 0.5 * (torg[pointmtrindex + j] + tdest[pointmtrindex + j]); + } + // Set this point into all extra node lists at this edge. + spintet = worktet; + while (1) { + if (!ishulltet(spintet)) { + adjextralist = (point *) spintet.tet[highorderindex]; + adjextralist[ver2edge[spintet.ver]] = newpoint; + } + fnextself(spintet); + if (spintet.tet == worktet.tet) break; + } + } // if (!extralist[i]) + } // i + tetloop.tet = tetrahedrontraverse(); + } } /////////////////////////////////////////////////////////////////////////////// // // -// checkconforming() Ensure that the mesh is conforming Delaunay. // +// 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. // +// // +// NOTE: This routine must be called after outelements(). So all elements // +// have been indexed. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::checkconforming() +void tetgenmesh::numberedges() { - face segloop, shloop; - int encsubsegs, encsubfaces; + triface worktet, spintet; + int ishulledge; + int t1ver; + int i; - if (!b->quiet) { - printf(" Checking conforming Delaunay property of mesh...\n"); - } - encsubsegs = encsubfaces = 0; - // Run through the list of subsegments, check each one. - subsegs->traversalinit(); - segloop.sh = shellfacetraverse(subsegs); - while (segloop.sh != (shellface *) NULL) { - if (checkseg4encroach(&segloop, NULL, NULL, false)) { - printf(" !! !! Non-conforming subsegment: (%d, %d)\n", - pointmark(sorg(segloop)), pointmark(sdest(segloop))); - encsubsegs++; - } - segloop.sh = shellfacetraverse(subsegs); - } - // Run through the list of subfaces, check each one. - subfaces->traversalinit(); - shloop.sh = shellfacetraverse(subfaces); - while (shloop.sh != (shellface *) NULL) { - if (checksub4encroach(&shloop, NULL, false)) { - printf(" !! !! Non-conforming subface: (%d, %d, %d)\n", - pointmark(sorg(shloop)), pointmark(sdest(shloop)), - pointmark(sapex(shloop))); - encsubfaces++; - } - shloop.sh = shellfacetraverse(subfaces); - } - if (encsubsegs == 0 && encsubfaces == 0) { - if (!b->quiet) { - printf(" The mesh is conforming Delaunay.\n"); - } - } else { - if (encsubsegs > 0) { - printf(" !! !! %d subsegments are non-conforming.\n", encsubsegs); - } - if (encsubfaces > 0) { - printf(" !! !! %d subfaces are non-conforming.\n", encsubfaces); + meshedges = meshhulledges = 0l; + + tetrahedrons->traversalinit(); + worktet.tet = tetrahedrontraverse(); + while (worktet.tet != NULL) { + // Count the number of Voronoi faces. Look at the six edges of this + // tet. Count an edge only if this tet's index is smaller than + // those of other non-hull tets which share this edge. + for (i = 0; i < 6; i++) { + worktet.ver = edge2ver[i]; + ishulledge = 0; + fnext(worktet, spintet); + do { + if (!ishulltet(spintet)) { + if (elemindex(spintet.tet) < elemindex(worktet.tet)) break; + } else { + ishulledge = 1; + } + fnextself(spintet); + } while (spintet.tet != worktet.tet); + // Count this edge if no adjacent tets are smaller than this tet. + if (spintet.tet == worktet.tet) { + meshedges++; + if (ishulledge) meshhulledges++; + } } + worktet.tet = tetrahedrontraverse(); } } /////////////////////////////////////////////////////////////////////////////// // // -// algorithmicstatistics() Print statistics about the mesh algorithms. // +// outnodes() Output the points to a .node file or a tetgenio structure. // +// // +// Note: each point has already been numbered on input (the first index is // +// 'in->firstnumber'). // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::algorithmicstatistics() +void tetgenmesh::outnodes(tetgenio* out) { - 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); + FILE *outfile = NULL; + char outnodefilename[FILENAMESIZE]; + face parentsh; + point pointloop; + int nextras, bmark, marker = 0, weightDT = 0; + int coordindex, attribindex; + int pointnumber, firstindex; + int index, i; + + if (out == (tetgenio *) NULL) { + strcpy(outnodefilename, b->outfilename); + strcat(outnodefilename, ".node"); + } + + if (!b->quiet) { + if (out == (tetgenio *) NULL) { + printf("Writing %s.\n", outnodefilename); } 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("Writing nodes.\n"); } } - 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); + nextras = numpointattrib; + if (b->weighted) { // -w + if (b->weighted_param == 0) weightDT = 1; // Weighted DT. } - - // 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"); -} + bmark = !b->nobound && in->pointmarkerlist; -/////////////////////////////////////////////////////////////////////////////// -// // -// qualitystatistics() Print statistics about the quality of the mesh. // -// // -/////////////////////////////////////////////////////////////////////////////// + if (out == (tetgenio *) NULL) { + outfile = fopen(outnodefilename, "w"); + if (outfile == (FILE *) NULL) { + printf("File I/O Error: Cannot create file %s.\n", outnodefilename); + terminatetetgen(this, 1); + } + // Number of points, number of dimensions, number of point attributes, + // and number of boundary markers (zero or one). + fprintf(outfile, "%ld %d %d %d\n", points->items, 3, nextras, bmark); + } else { + // Allocate space for 'pointlist'; + out->pointlist = new REAL[points->items * 3]; + if (out->pointlist == (REAL *) NULL) { + printf("Error: Out of memory.\n"); + terminatetetgen(this, 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(this, 1); + } + } + // Allocate space for 'pointmarkerlist' if necessary; + if (bmark) { + out->pointmarkerlist = new int[points->items]; + if (out->pointmarkerlist == (int *) NULL) { + printf("Error: Out of memory.\n"); + terminatetetgen(this, 1); + } + } + if (b->psc) { + out->pointparamlist = new tetgenio::pointparam[points->items]; + if (out->pointparamlist == NULL) { + printf("Error: Out of memory.\n"); + terminatetetgen(this, 1); + } + } + out->numberofpoints = points->items; + out->numberofpointattributes = nextras; + coordindex = 0; + attribindex = 0; + } + + // Determine the first index (0 or 1). + firstindex = b->zeroindex ? 0 : in->firstnumber; -void tetgenmesh::qualitystatistics() -{ - triface tetloop, neightet; - point p[4]; - char sbuf[128]; - REAL radiusratiotable[12]; - REAL aspectratiotable[12]; - REAL A[4][4], rhs[4], D; - REAL V[6][3], N[4][3], H[4]; // edge-vectors, face-normals, face-heights. - 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; - REAL cirradius, minheightinv; // insradius; - REAL shortlen, longlen; - REAL tetaspect, tetradius; - REAL smalldiangle, bigdiangle; - REAL smallfaangle, bigfaangle; - int radiustable[12]; - int aspecttable[16]; - int dihedangletable[18]; - int faceangletable[18]; - int indx[4]; - int radiusindex; - int aspectindex; - int tendegree; + points->traversalinit(); + pointloop = pointtraverse(); + pointnumber = firstindex; // in->firstnumber; + index = 0; + while (pointloop != (point) NULL) { + if (bmark) { + // Default the vertex has a zero marker. + marker = 0; + // Is it an input vertex? + if (index < in->numberofpoints) { + // Input point's marker is directly copied to output. + marker = in->pointmarkerlist[index]; + } else { + if ((pointtype(pointloop) == FREESEGVERTEX) || + (pointtype(pointloop) == FREEFACETVERTEX)) { + sdecode(point2sh(pointloop), parentsh); + if (parentsh.sh != NULL) { + marker = shellmark(parentsh); + if (pointtype(pointloop) == FREEFACETVERTEX) { + if (in->facetmarkerlist != NULL) { + marker = in->facetmarkerlist[marker - 1]; + } + } + } + } // if (pointtype(...)) + } + } + if (out == (tetgenio *) NULL) { + // Point number, x, y and z coordinates. + fprintf(outfile, "%4d %.17g %.17g %.17g", pointnumber, + pointloop[0], pointloop[1], pointloop[2]); + for (i = 0; i < nextras; i++) { + // Write an attribute. + if ((i == 0) && weightDT) { + fprintf(outfile, " %.17g", pointloop[0] * pointloop[0] + + pointloop[1] * pointloop[1] + pointloop[2] * pointloop[2] + - pointloop[3 + i]); + } else { + fprintf(outfile, " %.17g", pointloop[3 + i]); + } + } + if (bmark) { + // Write the boundary marker. + fprintf(outfile, " %d", marker); + } + if (b->psc) { + fprintf(outfile, " %.8g %.8g %d", pointgeomuv(pointloop, 0), + pointgeomuv(pointloop, 1), pointgeomtag(pointloop)); + if (pointtype(pointloop) == RIDGEVERTEX) { + fprintf(outfile, " 0"); + } else if (pointtype(pointloop) == ACUTEVERTEX) { + fprintf(outfile, " 0"); + } else if (pointtype(pointloop) == FREESEGVERTEX) { + fprintf(outfile, " 1"); + } else if (pointtype(pointloop) == FREEFACETVERTEX) { + fprintf(outfile, " 2"); + } else if (pointtype(pointloop) == FREEVOLVERTEX) { + fprintf(outfile, " 3"); + } else { + fprintf(outfile, " -1"); // Unknown type. + } + } + fprintf(outfile, "\n"); + } else { + // X, y, and z coordinates. + out->pointlist[coordindex++] = pointloop[0]; + out->pointlist[coordindex++] = pointloop[1]; + out->pointlist[coordindex++] = pointloop[2]; + // Point attributes. + for (i = 0; i < nextras; i++) { + // Output an attribute. + if ((i == 0) && weightDT) { + out->pointattributelist[attribindex++] = + pointloop[0] * pointloop[0] + pointloop[1] * pointloop[1] + + pointloop[2] * pointloop[2] - pointloop[3 + i]; + } else { + out->pointattributelist[attribindex++] = pointloop[3 + i]; + } + } + if (bmark) { + // Output the boundary marker. + out->pointmarkerlist[index] = marker; + } + if (b->psc) { + out->pointparamlist[index].uv[0] = pointgeomuv(pointloop, 0); + out->pointparamlist[index].uv[1] = pointgeomuv(pointloop, 1); + out->pointparamlist[index].tag = pointgeomtag(pointloop); + if (pointtype(pointloop) == RIDGEVERTEX) { + out->pointparamlist[index].type = 0; + } else if (pointtype(pointloop) == ACUTEVERTEX) { + out->pointparamlist[index].type = 0; + } else if (pointtype(pointloop) == FREESEGVERTEX) { + out->pointparamlist[index].type = 1; + } else if (pointtype(pointloop) == FREEFACETVERTEX) { + out->pointparamlist[index].type = 2; + } else if (pointtype(pointloop) == FREEVOLVERTEX) { + out->pointparamlist[index].type = 3; + } else { + out->pointparamlist[index].type = -1; // Unknown type. + } + } + } + pointloop = pointtraverse(); + pointnumber++; + index++; + } + + if (out == (tetgenio *) NULL) { + fprintf(outfile, "# Generated by %s\n", b->commandline); + fclose(outfile); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// outmetrics() Output the metric to a file (*.mtr) or a tetgenio obj. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::outmetrics(tetgenio* out) +{ + FILE *outfile = NULL; + char outmtrfilename[FILENAMESIZE]; + point ptloop; + int mtrindex; + + if (out == (tetgenio *) NULL) { + strcpy(outmtrfilename, b->outfilename); + strcat(outmtrfilename, ".mtr"); + } + + if (!b->quiet) { + if (out == (tetgenio *) NULL) { + printf("Writing %s.\n", outmtrfilename); + } else { + printf("Writing metrics.\n"); + } + } + + if (out == (tetgenio *) NULL) { + outfile = fopen(outmtrfilename, "w"); + if (outfile == (FILE *) NULL) { + printf("File I/O Error: Cannot create file %s.\n", outmtrfilename); + terminatetetgen(this, 3); + } + // Number of points, number of point metrices, + // fprintf(outfile, "%ld %d\n", points->items, sizeoftensor + 3); + fprintf(outfile, "%ld %d\n", points->items, 1); + } else { + // Allocate space for 'pointmtrlist' if necessary; + // out->pointmtrlist = new REAL[points->items * (sizeoftensor + 3)]; + out->pointmtrlist = new REAL[points->items]; + if (out->pointmtrlist == (REAL *) NULL) { + terminatetetgen(this, 1); + } + out->numberofpointmtrs = 1; // (sizeoftensor + 3); + mtrindex = 0; + } + + points->traversalinit(); + ptloop = pointtraverse(); + while (ptloop != (point) NULL) { + if (out == (tetgenio *) NULL) { + // for (i = 0; i < sizeoftensor; i++) { + // fprintf(outfile, "%-16.8e ", ptloop[pointmtrindex + i]); + // } + fprintf(outfile, "%-16.8e\n", ptloop[pointmtrindex]); + } else { + // for (i = 0; i < sizeoftensor; i++) { + // out->pointmtrlist[mtrindex++] = ptloop[pointmtrindex + i]; + // } + out->pointmtrlist[mtrindex++] = ptloop[pointmtrindex]; + } + ptloop = pointtraverse(); + } + + if (out == (tetgenio *) NULL) { + fprintf(outfile, "# Generated by %s\n", b->commandline); + fclose(outfile); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// outelements() Output the tetrahedra to an .ele file or a tetgenio // +// structure. // +// // +// This routine also indexes all tetrahedra (exclusing hull tets) (from in-> // +// firstnumber). The total number of mesh edges is counted in 'meshedges'. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::outelements(tetgenio* out) +{ + FILE *outfile = NULL; + char outelefilename[FILENAMESIZE]; + tetrahedron* tptr; + point p1, p2, p3, p4; + point *extralist; + REAL *talist = NULL; + int *tlist = NULL; + long ntets; + int firstindex, shift; + int pointindex, attribindex; + int highorderindex = 11; + int elementnumber; + int eextras; + int i; + + if (out == (tetgenio *) NULL) { + strcpy(outelefilename, b->outfilename); + strcat(outelefilename, ".ele"); + } + + if (!b->quiet) { + if (out == (tetgenio *) NULL) { + printf("Writing %s.\n", outelefilename); + } else { + printf("Writing elements.\n"); + } + } + + // The number of tets excluding hull tets. + ntets = tetrahedrons->items - hullsize; + + eextras = numelemattrib; + if (out == (tetgenio *) NULL) { + outfile = fopen(outelefilename, "w"); + if (outfile == (FILE *) NULL) { + printf("File I/O Error: Cannot create file %s.\n", outelefilename); + terminatetetgen(this, 1); + } + // Number of tetras, points per tetra, attributes per tetra. + fprintf(outfile, "%ld %d %d\n", ntets, b->order == 1 ? 4 : 10, eextras); + } else { + // Allocate memory for output tetrahedra. + out->tetrahedronlist = new int[ntets * (b->order == 1 ? 4 : 10)]; + if (out->tetrahedronlist == (int *) NULL) { + printf("Error: Out of memory.\n"); + terminatetetgen(this, 1); + } + // Allocate memory for output tetrahedron attributes if necessary. + if (eextras > 0) { + out->tetrahedronattributelist = new REAL[ntets * eextras]; + if (out->tetrahedronattributelist == (REAL *) NULL) { + printf("Error: Out of memory.\n"); + terminatetetgen(this, 1); + } + } + out->numberoftetrahedra = ntets; + out->numberofcorners = b->order == 1 ? 4 : 10; + out->numberoftetrahedronattributes = eextras; + tlist = out->tetrahedronlist; + talist = out->tetrahedronattributelist; + pointindex = 0; + attribindex = 0; + } + + // Determine the first index (0 or 1). + firstindex = b->zeroindex ? 0 : in->firstnumber; + shift = 0; // Default no shift. + if ((in->firstnumber == 1) && (firstindex == 0)) { + shift = 1; // Shift the output indices by 1. + } + + tetrahedrons->traversalinit(); + tptr = tetrahedrontraverse(); + elementnumber = firstindex; // in->firstnumber; + while (tptr != (tetrahedron *) NULL) { + if (!b->reversetetori) { + p1 = (point) tptr[4]; + p2 = (point) tptr[5]; + } else { + p1 = (point) tptr[5]; + p2 = (point) tptr[4]; + } + p3 = (point) tptr[6]; + p4 = (point) tptr[7]; + if (out == (tetgenio *) NULL) { + // Tetrahedron number, indices for four points. + fprintf(outfile, "%5d %5d %5d %5d %5d", elementnumber, + pointmark(p1) - shift, pointmark(p2) - shift, + pointmark(p3) - shift, pointmark(p4) - shift); + if (b->order == 2) { + extralist = (point *) tptr[highorderindex]; + // indices for six extra points. + fprintf(outfile, " %5d %5d %5d %5d %5d %5d", + pointmark(extralist[0]) - shift, pointmark(extralist[1]) - shift, + pointmark(extralist[2]) - shift, pointmark(extralist[3]) - shift, + pointmark(extralist[4]) - shift, pointmark(extralist[5]) - shift); + } + for (i = 0; i < eextras; i++) { + fprintf(outfile, " %.17g", elemattribute(tptr, i)); + } + fprintf(outfile, "\n"); + } else { + tlist[pointindex++] = pointmark(p1) - shift; + tlist[pointindex++] = pointmark(p2) - shift; + tlist[pointindex++] = pointmark(p3) - shift; + tlist[pointindex++] = pointmark(p4) - shift; + if (b->order == 2) { + extralist = (point *) tptr[highorderindex]; + tlist[pointindex++] = pointmark(extralist[0]) - shift; + tlist[pointindex++] = pointmark(extralist[1]) - shift; + tlist[pointindex++] = pointmark(extralist[2]) - shift; + tlist[pointindex++] = pointmark(extralist[3]) - shift; + tlist[pointindex++] = pointmark(extralist[4]) - shift; + tlist[pointindex++] = pointmark(extralist[5]) - shift; + } + for (i = 0; i < eextras; i++) { + talist[attribindex++] = elemattribute(tptr, i); + } + } + // Remember the index of this element (for counting edges). + setelemindex(tptr, elementnumber); + tptr = tetrahedrontraverse(); + elementnumber++; + } + + + if (out == (tetgenio *) NULL) { + fprintf(outfile, "# Generated by %s\n", b->commandline); + fclose(outfile); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// outfaces() Output all faces to a .face file or a tetgenio object. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::outfaces(tetgenio* out) +{ + FILE *outfile = NULL; + char facefilename[FILENAMESIZE]; + triface tface, tsymface; + face checkmark; + point torg, tdest, tapex; + long ntets, faces; + int *elist = NULL, *emlist = NULL; + int neigh1 = 0, neigh2 = 0; + int faceid, marker = 0; + int firstindex, shift; + int facenumber; + int index = 0; + + // For -o2 option. + triface workface; + point *extralist, pp[3] = {0,0,0}; + int highorderindex = 11; + int o2index = 0, i; + + if (out == (tetgenio *) NULL) { + strcpy(facefilename, b->outfilename); + strcat(facefilename, ".face"); + } + + if (!b->quiet) { + if (out == (tetgenio *) NULL) { + printf("Writing %s.\n", facefilename); + } else { + printf("Writing faces.\n"); + } + } + + ntets = tetrahedrons->items - hullsize; + faces = (ntets * 4l + hullsize) / 2l; + + if (out == (tetgenio *) NULL) { + outfile = fopen(facefilename, "w"); + if (outfile == (FILE *) NULL) { + printf("File I/O Error: Cannot create file %s.\n", facefilename); + terminatetetgen(this, 1); + } + fprintf(outfile, "%ld %d\n", faces, !b->nobound); + } else { + // Allocate memory for 'trifacelist'. + out->trifacelist = new int[faces * 3]; + if (out->trifacelist == (int *) NULL) { + printf("Error: Out of memory.\n"); + terminatetetgen(this, 1); + } + if (b->order == 2) { + out->o2facelist = new int[faces * 3]; + } + // Allocate memory for 'trifacemarkerlist' if necessary. + if (!b->nobound) { + out->trifacemarkerlist = new int[faces]; + if (out->trifacemarkerlist == (int *) NULL) { + printf("Error: Out of memory.\n"); + terminatetetgen(this, 1); + } + } + if (b->neighout > 1) { + // '-nn' switch. + out->adjtetlist = new int[faces * 2]; + if (out->adjtetlist == (int *) NULL) { + printf("Error: Out of memory.\n"); + terminatetetgen(this, 1); + } + } + out->numberoftrifaces = faces; + elist = out->trifacelist; + emlist = out->trifacemarkerlist; + } + + // 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. + } + + tetrahedrons->traversalinit(); + tface.tet = tetrahedrontraverse(); + facenumber = firstindex; // in->firstnumber; + // To loop over the set of faces, loop over all tetrahedra, and look at + // the four faces of each one. If its adjacent tet is a hull tet, + // operate on the face, otherwise, operate on the face only if the + // current tet has a smaller index than its neighbor. + while (tface.tet != (tetrahedron *) NULL) { + for (tface.ver = 0; tface.ver < 4; tface.ver ++) { + fsym(tface, tsymface); + if (ishulltet(tsymface) || + (elemindex(tface.tet) < elemindex(tsymface.tet))) { + torg = org(tface); + tdest = dest(tface); + tapex = apex(tface); + if (b->order == 2) { // -o2 + // Get the three extra vertices on edges. + extralist = (point *) (tface.tet[highorderindex]); + // The extra vertices are on edges opposite the corners. + enext(tface, workface); + for (i = 0; i < 3; i++) { + pp[i] = extralist[ver2edge[workface.ver]]; + enextself(workface); + } + } + if (!b->nobound) { + // Get the boundary marker of this face. + if (b->plc || b->refine) { + // Shell face is used. + tspivot(tface, checkmark); + if (checkmark.sh == NULL) { + marker = 0; // It is an inner face. It's marker is 0. + } else { + if (in->facetmarkerlist) { + // The facet marker is given, get it. + faceid = shellmark(checkmark) - 1; + marker = in->facetmarkerlist[faceid]; + } else { + marker = 1; // The default marker for subface is 1. + } + } + } else { + // Shell face is not used, only distinguish outer and inner face. + marker = (int) ishulltet(tsymface); + } + } + if (b->neighout > 1) { + // '-nn' switch. Output adjacent tets indices. + neigh1 = elemindex(tface.tet); + if (!ishulltet(tsymface)) { + neigh2 = elemindex(tsymface.tet); + } else { + neigh2 = -1; + } + } + if (out == (tetgenio *) NULL) { + // Face number, indices of three vertices. + fprintf(outfile, "%5d %4d %4d %4d", facenumber, + pointmark(torg) - shift, pointmark(tdest) - shift, + pointmark(tapex) - shift); + if (b->order == 2) { // -o2 + fprintf(outfile, " %4d %4d %4d", pointmark(pp[0]) - shift, + pointmark(pp[1]) - shift, pointmark(pp[2]) - shift); + } + if (!b->nobound) { + // Output a boundary marker. + fprintf(outfile, " %d", marker); + } + if (b->neighout > 1) { + fprintf(outfile, " %5d %5d", neigh1, neigh2); + } + fprintf(outfile, "\n"); + } else { + // Output indices of three vertices. + elist[index++] = pointmark(torg) - shift; + elist[index++] = pointmark(tdest) - shift; + elist[index++] = pointmark(tapex) - shift; + if (b->order == 2) { // -o2 + out->o2facelist[o2index++] = pointmark(pp[0]) - shift; + out->o2facelist[o2index++] = pointmark(pp[1]) - shift; + out->o2facelist[o2index++] = pointmark(pp[2]) - shift; + } + if (!b->nobound) { + emlist[facenumber - in->firstnumber] = marker; + } + if (b->neighout > 1) { + out->adjtetlist[(facenumber - in->firstnumber) * 2] = neigh1; + out->adjtetlist[(facenumber - in->firstnumber) * 2 + 1] = neigh2; + } + } + facenumber++; + } + } + tface.tet = tetrahedrontraverse(); + } + + if (out == (tetgenio *) NULL) { + fprintf(outfile, "# Generated by %s\n", b->commandline); + fclose(outfile); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// outhullfaces() Output hull faces to a .face file or a tetgenio object. // +// // +// The normal of each face is pointing to the outside of the domain. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::outhullfaces(tetgenio* out) +{ + FILE *outfile = NULL; + char facefilename[FILENAMESIZE]; + triface hulltet; + point torg, tdest, tapex; + int *elist = NULL; + int firstindex, shift; + int facenumber; + int index; + + if (out == (tetgenio *) NULL) { + strcpy(facefilename, b->outfilename); + strcat(facefilename, ".face"); + } + + if (!b->quiet) { + if (out == (tetgenio *) NULL) { + printf("Writing %s.\n", facefilename); + } else { + printf("Writing faces.\n"); + } + } + + if (out == (tetgenio *) NULL) { + outfile = fopen(facefilename, "w"); + if (outfile == (FILE *) NULL) { + printf("File I/O Error: Cannot create file %s.\n", facefilename); + terminatetetgen(this, 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(this, 1); + } + out->numberoftrifaces = hullsize; + elist = out->trifacelist; + index = 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. + } + + tetrahedrons->traversalinit(); + hulltet.tet = alltetrahedrontraverse(); + facenumber = firstindex; + while (hulltet.tet != (tetrahedron *) NULL) { + if (ishulltet(hulltet)) { + torg = (point) hulltet.tet[4]; + tdest = (point) hulltet.tet[5]; + tapex = (point) hulltet.tet[6]; + if (out == (tetgenio *) NULL) { + // Face number, indices of three vertices. + fprintf(outfile, "%5d %4d %4d %4d", facenumber, + pointmark(torg) - shift, pointmark(tdest) - shift, + pointmark(tapex) - shift); + fprintf(outfile, "\n"); + } else { + // Output indices of three vertices. + elist[index++] = pointmark(torg) - shift; + elist[index++] = pointmark(tdest) - shift; + elist[index++] = pointmark(tapex) - shift; + } + facenumber++; + } + hulltet.tet = alltetrahedrontraverse(); + } + + if (out == (tetgenio *) NULL) { + fprintf(outfile, "# Generated by %s\n", b->commandline); + fclose(outfile); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// outsubfaces() Output subfaces (i.e. boundary faces) to a .face file or // +// a tetgenio structure. // +// // +// The boundary faces are found in 'subfaces'. For listing triangle vertices // +// in the same sense for all triangles in the mesh, the direction determined // +// by right-hand rule is pointer to the inside of the volume. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::outsubfaces(tetgenio* out) +{ + FILE *outfile = NULL; + char facefilename[FILENAMESIZE]; + int *elist = NULL; + int *emlist = NULL; + int index = 0, index1 = 0, index2 = 0; + triface abuttingtet; + face faceloop; + point torg, tdest, tapex; + int faceid = 0, marker = 0; + int firstindex, shift; + int neigh1 = 0, neigh2 = 0; + int facenumber; + + // For -o2 option. + triface workface; + point *extralist, pp[3] = {0,0,0}; + int highorderindex = 11; + int o2index = 0, i; + + int t1ver; // used by fsymself() + + if (out == (tetgenio *) NULL) { + strcpy(facefilename, b->outfilename); + strcat(facefilename, ".face"); + } + + if (!b->quiet) { + if (out == (tetgenio *) NULL) { + printf("Writing %s.\n", facefilename); + } else { + printf("Writing faces.\n"); + } + } + + if (out == (tetgenio *) NULL) { + outfile = fopen(facefilename, "w"); + if (outfile == (FILE *) NULL) { + printf("File I/O Error: Cannot create file %s.\n", facefilename); + terminatetetgen(this, 3); + } + // Number of subfaces. + fprintf(outfile, "%ld %d\n", subfaces->items, !b->nobound); + } else { + // Allocate memory for 'trifacelist'. + out->trifacelist = new int[subfaces->items * 3]; + if (out->trifacelist == (int *) NULL) { + terminatetetgen(this, 1); + } + if (b->order == 2) { + out->o2facelist = new int[subfaces->items * 3]; + } + if (!b->nobound) { + // Allocate memory for 'trifacemarkerlist'. + out->trifacemarkerlist = new int[subfaces->items]; + if (out->trifacemarkerlist == (int *) NULL) { + terminatetetgen(this, 1); + } + } + if (b->neighout > 1) { + // '-nn' switch. + out->adjtetlist = new int[subfaces->items * 2]; + if (out->adjtetlist == (int *) NULL) { + terminatetetgen(this, 1); + } + } + out->numberoftrifaces = subfaces->items; + elist = out->trifacelist; + emlist = out->trifacemarkerlist; + } + + // 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. + } + + subfaces->traversalinit(); + faceloop.sh = shellfacetraverse(subfaces); + facenumber = firstindex; // in->firstnumber; + while (faceloop.sh != (shellface *) NULL) { + stpivot(faceloop, abuttingtet); + // If there is a tetrahedron containing this subface, orient it so + // that the normal of this face points to inside of the volume by + // right-hand rule. + if (abuttingtet.tet != NULL) { + if (ishulltet(abuttingtet)) { + fsymself(abuttingtet); + assert(!ishulltet(abuttingtet)); + } + } + if (abuttingtet.tet != NULL) { + torg = org(abuttingtet); + tdest = dest(abuttingtet); + tapex = apex(abuttingtet); + if (b->order == 2) { // -o2 + // Get the three extra vertices on edges. + extralist = (point *) (abuttingtet.tet[highorderindex]); + workface = abuttingtet; + for (i = 0; i < 3; i++) { + pp[i] = extralist[ver2edge[workface.ver]]; + enextself(workface); + } + } + } else { + // This may happen when only a surface mesh be generated. + torg = sorg(faceloop); + tdest = sdest(faceloop); + tapex = sapex(faceloop); + if (b->order == 2) { // -o2 + // There is no extra node list available. + pp[0] = torg; + pp[1] = tdest; + pp[2] = tapex; + } + } + if (!b->nobound) { + if (b->refine) { // -r option. + if (in->trifacemarkerlist) { + marker = shellmark(faceloop); + } else { + marker = 1; // Default marker for a subface is 1. + } + } else { + if (in->facetmarkerlist) { + faceid = shellmark(faceloop) - 1; + marker = in->facetmarkerlist[faceid]; + } else { + marker = 1; // Default marker for a subface is 1. + } + } + } + if (b->neighout > 1) { + // '-nn' switch. Output adjacent tets indices. + neigh1 = -1; + neigh2 = -1; + stpivot(faceloop, abuttingtet); + if (abuttingtet.tet != NULL) { + neigh1 = elemindex(abuttingtet.tet); + fsymself(abuttingtet); + if (!ishulltet(abuttingtet)) { + neigh2 = elemindex(abuttingtet.tet); + } + } + } + if (out == (tetgenio *) NULL) { + fprintf(outfile, "%5d %4d %4d %4d", facenumber, + pointmark(torg) - shift, pointmark(tdest) - shift, + pointmark(tapex) - shift); + if (b->order == 2) { // -o2 + fprintf(outfile, " %4d %4d %4d", pointmark(pp[0]) - shift, + pointmark(pp[1]) - shift, pointmark(pp[2]) - shift); + } + if (!b->nobound) { + fprintf(outfile, " %d", marker); + } + if (b->neighout > 1) { + fprintf(outfile, " %5d %5d", neigh1, neigh2); + } + fprintf(outfile, "\n"); + } else { + // Output three vertices of this face; + elist[index++] = pointmark(torg) - shift; + elist[index++] = pointmark(tdest) - shift; + elist[index++] = pointmark(tapex) - shift; + if (b->order == 2) { // -o2 + out->o2facelist[o2index++] = pointmark(pp[0]) - shift; + out->o2facelist[o2index++] = pointmark(pp[1]) - shift; + out->o2facelist[o2index++] = pointmark(pp[2]) - shift; + } + if (!b->nobound) { + emlist[index1++] = marker; + } + if (b->neighout > 1) { + out->adjtetlist[index2++] = neigh1; + out->adjtetlist[index2++] = neigh2; + } + } + facenumber++; + faceloop.sh = shellfacetraverse(subfaces); + } + + if (out == (tetgenio *) NULL) { + fprintf(outfile, "# Generated by %s\n", b->commandline); + fclose(outfile); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// outedges() Output all edges to a .edge file or a tetgenio object. // +// // +// Note: This routine must be called after outelements(), so that the total // +// number of edges 'meshedges' has been counted. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::outedges(tetgenio* out) +{ + FILE *outfile = NULL; + char edgefilename[FILENAMESIZE]; + triface tetloop, worktet, spintet; + face checkseg; + point torg, tdest; + int *elist = NULL, *emlist = NULL; + int ishulledge; + int firstindex, shift; + int edgenumber, marker; + int index = 0, index1 = 0, index2 = 0; + int t1ver; + int i; + + // For -o2 option. + point *extralist, pp = NULL; + int highorderindex = 11; + int o2index = 0; + + if (out == (tetgenio *) NULL) { + strcpy(edgefilename, b->outfilename); + strcat(edgefilename, ".edge"); + } + + if (!b->quiet) { + if (out == (tetgenio *) NULL) { + printf("Writing %s.\n", edgefilename); + } else { + printf("Writing edges.\n"); + } + } + + if (meshedges == 0l) { + if (nonconvex) { + numberedges(); // Count the edges. + } else { + // Use Euler's characteristic to get the numbe of edges. + // It states V - E + F - C = 1, hence E = V + F - C - 1. + long tsize = tetrahedrons->items - hullsize; + long fsize = (tsize * 4l + hullsize) / 2l; + long vsize = points->items - dupverts - unuverts; + if (b->weighted) vsize -= nonregularcount; + meshedges = vsize + fsize - tsize - 1; + } + } + + if (out == (tetgenio *) NULL) { + outfile = fopen(edgefilename, "w"); + if (outfile == (FILE *) NULL) { + printf("File I/O Error: Cannot create file %s.\n", edgefilename); + terminatetetgen(this, 1); + } + // Write the number of edges, boundary markers (0 or 1). + fprintf(outfile, "%ld %d\n", meshedges, !b->nobound); + } else { + // Allocate memory for 'edgelist'. + out->edgelist = new int[meshedges * 2]; + if (out->edgelist == (int *) NULL) { + printf("Error: Out of memory.\n"); + terminatetetgen(this, 1); + } + if (b->order == 2) { // -o2 switch + out->o2edgelist = new int[meshedges]; + } + if (!b->nobound) { + out->edgemarkerlist = new int[meshedges]; + } + if (b->neighout > 1) { // '-nn' switch. + out->edgeadjtetlist = new int[meshedges]; + } + out->numberofedges = meshedges; + elist = out->edgelist; + emlist = out->edgemarkerlist; + } + + // 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 (reduce) the output indices by 1. + } + + tetrahedrons->traversalinit(); + tetloop.tet = tetrahedrontraverse(); + edgenumber = firstindex; // in->firstnumber; + while (tetloop.tet != (tetrahedron *) NULL) { + // Count the number of Voronoi faces. + worktet.tet = tetloop.tet; + for (i = 0; i < 6; i++) { + worktet.ver = edge2ver[i]; + ishulledge = 0; + fnext(worktet, spintet); + do { + if (!ishulltet(spintet)) { + if (elemindex(spintet.tet) < elemindex(worktet.tet)) break; + } else { + ishulledge = 1; + } + fnextself(spintet); + } while (spintet.tet != worktet.tet); + // Count this edge if no adjacent tets are smaller than this tet. + if (spintet.tet == worktet.tet) { + torg = org(worktet); + tdest = dest(worktet); + if (b->order == 2) { // -o2 + // Get the extra vertex on this edge. + extralist = (point *) worktet.tet[highorderindex]; + pp = extralist[ver2edge[worktet.ver]]; + } + if (out == (tetgenio *) NULL) { + fprintf(outfile, "%5d %4d %4d", edgenumber, + pointmark(torg) - shift, pointmark(tdest) - shift); + if (b->order == 2) { // -o2 + fprintf(outfile, " %4d", pointmark(pp) - shift); + } + } else { + // Output three vertices of this face; + elist[index++] = pointmark(torg) - shift; + elist[index++] = pointmark(tdest) - shift; + if (b->order == 2) { // -o2 + out->o2edgelist[o2index++] = pointmark(pp) - shift; + } + } + if (!b->nobound) { + if (b->plc || b->refine) { + // Check if the edge is a segment. + tsspivot1(worktet, checkseg); + if (checkseg.sh != NULL) { + marker = shellmark(checkseg); + if (marker == 0) { // Does it have no marker? + marker = 1; // Set the default marker for this segment. + } + } else { + marker = 0; // It's not a segment. + } + } else { + // Mark it if it is a hull edge. + marker = ishulledge ? 1 : 0; + } + if (out == (tetgenio *) NULL) { + fprintf(outfile, " %d", marker); + } else { + emlist[index1++] = marker; + } + } + if (b->neighout > 1) { // '-nn' switch. + if (out == (tetgenio *) NULL) { + fprintf(outfile, " %d", elemindex(tetloop.tet)); + } else { + out->edgeadjtetlist[index2++] = elemindex(tetloop.tet); + } + } + if (out == (tetgenio *) NULL) { + fprintf(outfile, "\n"); + } + edgenumber++; + } + } + tetloop.tet = tetrahedrontraverse(); + } + + if (out == (tetgenio *) NULL) { + fprintf(outfile, "# Generated by %s\n", b->commandline); + fclose(outfile); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// outsubsegments() Output segments to a .edge file or a structure. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::outsubsegments(tetgenio* out) +{ + FILE *outfile = NULL; + char edgefilename[FILENAMESIZE]; + int *elist = NULL; + int index, i; + face edgeloop; + point torg, tdest; + int firstindex, shift; + int marker; + int edgenumber; + + // For -o2 option. + triface workface, spintet; + point *extralist, pp = NULL; + int highorderindex = 11; + int o2index = 0; + + // For -nn option. + int neigh = -1; + int index2 = 0; + + int t1ver; // used by fsymself() + + if (out == (tetgenio *) NULL) { + strcpy(edgefilename, b->outfilename); + strcat(edgefilename, ".edge"); + } + + if (!b->quiet) { + if (out == (tetgenio *) NULL) { + printf("Writing %s.\n", edgefilename); + } else { + printf("Writing edges.\n"); + } + } + + if (out == (tetgenio *) NULL) { + outfile = fopen(edgefilename, "w"); + if (outfile == (FILE *) NULL) { + printf("File I/O Error: Cannot create file %s.\n", edgefilename); + terminatetetgen(this, 3); + } + // Number of subsegments. + fprintf(outfile, "%ld 1\n", subsegs->items); + } else { + // Allocate memory for 'edgelist'. + out->edgelist = new int[subsegs->items * (b->order == 1 ? 2 : 3)]; + if (out->edgelist == (int *) NULL) { + terminatetetgen(this, 1); + } + if (b->order == 2) { + out->o2edgelist = new int[subsegs->items]; + } + out->edgemarkerlist = new int[subsegs->items]; + if (out->edgemarkerlist == (int *) NULL) { + terminatetetgen(this, 1); + } + if (b->neighout > 1) { + out->edgeadjtetlist = new int[subsegs->items]; + } + out->numberofedges = subsegs->items; + elist = out->edgelist; + } + + // 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. + } + index = 0; + i = 0; + + subsegs->traversalinit(); + edgeloop.sh = shellfacetraverse(subsegs); + edgenumber = firstindex; // in->firstnumber; + while (edgeloop.sh != (shellface *) NULL) { + torg = sorg(edgeloop); + tdest = sdest(edgeloop); + if ((b->order == 2) || (b->neighout > 1)) { + sstpivot1(edgeloop, workface); + if (workface.tet != NULL) { + // We must find a non-hull tet. + if (ishulltet(workface)) { + spintet = workface; + while (1) { + fnextself(spintet); + if (!ishulltet(spintet)) break; + if (spintet.tet == workface.tet) break; + } + assert(!ishulltet(spintet)); + workface = spintet; + } + } + } + if (b->order == 2) { // -o2 + // Get the extra vertex on this edge. + if (workface.tet != NULL) { + extralist = (point *) workface.tet[highorderindex]; + pp = extralist[ver2edge[workface.ver]]; + } else { + pp = torg; // There is no extra node available. + } + } + if (b->neighout > 1) { // -nn + if (workface.tet != NULL) { + neigh = elemindex(workface.tet); + } else { + neigh = -1; + } + } + marker = shellmark(edgeloop); + if (marker == 0) { + marker = 1; // Default marker of a boundary edge is 1. + } + if (out == (tetgenio *) NULL) { + fprintf(outfile, "%5d %4d %4d", edgenumber, + pointmark(torg) - shift, pointmark(tdest) - shift); + if (b->order == 2) { // -o2 + fprintf(outfile, " %4d", pointmark(pp) - shift); + } + fprintf(outfile, " %d", marker); + if (b->neighout > 1) { // -nn + fprintf(outfile, " %4d", neigh); + } + fprintf(outfile, "\n"); + } else { + // Output three vertices of this face; + elist[index++] = pointmark(torg) - shift; + elist[index++] = pointmark(tdest) - shift; + if (b->order == 2) { // -o2 + out->o2edgelist[o2index++] = pointmark(pp) - shift; + } + out->edgemarkerlist[i++] = marker; + if (b->neighout > 1) { // -nn + out->edgeadjtetlist[index2++] = neigh; + } + } + edgenumber++; + edgeloop.sh = shellfacetraverse(subsegs); + } + + if (out == (tetgenio *) NULL) { + fprintf(outfile, "# Generated by %s\n", b->commandline); + fclose(outfile); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// outneighbors() Output tet neighbors to a .neigh file or a structure. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::outneighbors(tetgenio* out) +{ + FILE *outfile = NULL; + char neighborfilename[FILENAMESIZE]; + int *nlist = NULL; + int index = 0; + triface tetloop, tetsym; + int neighbori[4]; + int firstindex; + int elementnumber; + long ntets; + + if (out == (tetgenio *) NULL) { + strcpy(neighborfilename, b->outfilename); + strcat(neighborfilename, ".neigh"); + } + + if (!b->quiet) { + if (out == (tetgenio *) NULL) { + printf("Writing %s.\n", neighborfilename); + } else { + printf("Writing neighbors.\n"); + } + } + + ntets = tetrahedrons->items - hullsize; + + if (out == (tetgenio *) NULL) { + outfile = fopen(neighborfilename, "w"); + if (outfile == (FILE *) NULL) { + printf("File I/O Error: Cannot create file %s.\n", neighborfilename); + terminatetetgen(this, 1); + } + // Number of tetrahedra, four faces per tetrahedron. + fprintf(outfile, "%ld %d\n", ntets, 4); + } else { + // Allocate memory for 'neighborlist'. + out->neighborlist = new int[ntets * 4]; + if (out->neighborlist == (int *) NULL) { + printf("Error: Out of memory.\n"); + terminatetetgen(this, 1); + } + nlist = out->neighborlist; + } + + // Determine the first index (0 or 1). + firstindex = b->zeroindex ? 0 : in->firstnumber; + + tetrahedrons->traversalinit(); + tetloop.tet = tetrahedrontraverse(); + elementnumber = firstindex; // in->firstnumber; + while (tetloop.tet != (tetrahedron *) NULL) { + for (tetloop.ver = 0; tetloop.ver < 4; tetloop.ver++) { + fsym(tetloop, tetsym); + if (!ishulltet(tetsym)) { + neighbori[tetloop.ver] = elemindex(tetsym.tet); + } else { + neighbori[tetloop.ver] = -1; + } + } + if (out == (tetgenio *) NULL) { + // Tetrahedra number, neighboring tetrahedron numbers. + fprintf(outfile, "%4d %4d %4d %4d %4d\n", elementnumber, + neighbori[0], neighbori[1], neighbori[2], neighbori[3]); + } else { + nlist[index++] = neighbori[0]; + nlist[index++] = neighbori[1]; + nlist[index++] = neighbori[2]; + nlist[index++] = neighbori[3]; + } + tetloop.tet = tetrahedrontraverse(); + elementnumber++; + } + + if (out == (tetgenio *) NULL) { + fprintf(outfile, "# Generated by %s\n", b->commandline); + fclose(outfile); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// outvoronoi() Output the Voronoi diagram to .v.node, .v.edge, v.face, // +// and .v.cell. // +// // +// The Voronoi diagram is the geometric dual of the Delaunay triangulation. // +// The Voronoi vertices are the circumcenters of Delaunay tetrahedra. Each // +// Voronoi edge connects two Voronoi vertices at two sides of a common Dela- // +// unay face. At a face of convex hull, it becomes a ray (goto the infinity).// +// A Voronoi face is the convex hull of all Voronoi vertices around a common // +// Delaunay edge. It is a closed polygon for any internal Delaunay edge. At a// +// ridge, it is unbounded. Each Voronoi cell is the convex hull of all Vor- // +// onoi vertices around a common Delaunay vertex. It is a polytope for any // +// internal Delaunay vertex. It is an unbounded polyhedron for a Delaunay // +// vertex belonging to the convex hull. // +// // +// NOTE: This routine is only used when the input is only a set of point. // +// Comment: Special thanks to Victor Liu for finding and fixing few bugs. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::outvoronoi(tetgenio* out) +{ + FILE *outfile = NULL; + char outfilename[FILENAMESIZE]; + tetgenio::voroedge *vedge = NULL; + tetgenio::vorofacet *vfacet = NULL; + arraypool *tetlist, *ptlist; + triface tetloop, worktet, spintet, firsttet; + point pt[4], ploop, neipt; + REAL ccent[3], infvec[3], vec1[3], vec2[3], L; + long ntets, faces, edges; + int *indexarray, *fidxs, *eidxs; + int arraysize, *vertarray = NULL; + int vpointcount, vedgecount, vfacecount, tcount; + int ishullvert, ishullface; + int index, shift, end1, end2; int i, j; - printf("Mesh quality statistics:\n\n"); + int t1ver; // used by fsymself() - // Avoid compile warnings. - shortlen = longlen = 0.0; - smalldiangle = bigdiangle = 0.0; + // Output Voronoi vertices to .v.node file. + if (out == (tetgenio *) NULL) { + strcpy(outfilename, b->outfilename); + strcat(outfilename, ".v.node"); + } - radiusratiotable[0] = 0.707; radiusratiotable[1] = 1.0; - radiusratiotable[2] = 1.1; radiusratiotable[3] = 1.2; - radiusratiotable[4] = 1.4; radiusratiotable[5] = 1.6; - radiusratiotable[6] = 1.8; radiusratiotable[7] = 2.0; - radiusratiotable[8] = 2.5; radiusratiotable[9] = 3.0; - radiusratiotable[10] = 10.0; radiusratiotable[11] = 0.0; + if (!b->quiet) { + if (out == (tetgenio *) NULL) { + printf("Writing %s.\n", outfilename); + } else { + printf("Writing Voronoi vertices.\n"); + } + } + + // Determine the first index (0 or 1). + shift = (b->zeroindex ? 0 : in->firstnumber); + + // Each face and edge of the tetrahedral mesh will be indexed for indexing + // the Voronoi edges and facets. Indices of faces and edges are saved in + // each tetrahedron (including hull tets). + + // Allocate the total space once. + indexarray = new int[tetrahedrons->items * 10]; + + // Allocate space (10 integers) into each tetrahedron. It re-uses the slot + // for element markers, flags. + i = 0; + tetrahedrons->traversalinit(); + tetloop.tet = alltetrahedrontraverse(); + while (tetloop.tet != NULL) { + tetloop.tet[11] = (tetrahedron) &(indexarray[i * 10]); + i++; + tetloop.tet = alltetrahedrontraverse(); + } + + // The number of tetrahedra (excluding hull tets) (Voronoi vertices). + ntets = tetrahedrons->items - hullsize; + // The number of Delaunay faces (Voronoi edges). + faces = (4l * ntets + hullsize) / 2l; + // The number of Delaunay edges (Voronoi faces). + long vsize = points->items - dupverts - unuverts; + if (b->weighted) vsize -= nonregularcount; + edges = vsize + faces - ntets - 1; + + if (out == (tetgenio *) NULL) { + outfile = fopen(outfilename, "w"); + if (outfile == (FILE *) NULL) { + printf("File I/O Error: Cannot create file %s.\n", outfilename); + terminatetetgen(this, 3); + } + // Number of voronoi points, 3 dim, no attributes, no marker. + fprintf(outfile, "%ld 3 0 0\n", ntets); + } else { + // Allocate space for 'vpointlist'. + out->numberofvpoints = (int) ntets; + out->vpointlist = new REAL[out->numberofvpoints * 3]; + if (out->vpointlist == (REAL *) NULL) { + terminatetetgen(this, 1); + } + } + + // Output Voronoi vertices (the circumcenters of tetrahedra). + tetrahedrons->traversalinit(); + tetloop.tet = tetrahedrontraverse(); + vpointcount = 0; // The (internal) v-index always starts from 0. + index = 0; + while (tetloop.tet != (tetrahedron *) NULL) { + for (i = 0; i < 4; i++) { + pt[i] = (point) tetloop.tet[4 + i]; + setpoint2tet(pt[i], encode(tetloop)); + } + if (b->weighted) { + orthosphere(pt[0], pt[1], pt[2], pt[3], pt[0][3], pt[1][3], pt[2][3], + pt[3][3], ccent, NULL); + } else { + circumsphere(pt[0], pt[1], pt[2], pt[3], ccent, NULL); + } + if (out == (tetgenio *) NULL) { + fprintf(outfile, "%4d %16.8e %16.8e %16.8e\n", vpointcount + shift, + ccent[0], ccent[1], ccent[2]); + } else { + out->vpointlist[index++] = ccent[0]; + out->vpointlist[index++] = ccent[1]; + out->vpointlist[index++] = ccent[2]; + } + setelemindex(tetloop.tet, vpointcount); + vpointcount++; + tetloop.tet = tetrahedrontraverse(); + } + + if (out == (tetgenio *) NULL) { + fprintf(outfile, "# Generated by %s\n", b->commandline); + fclose(outfile); + } + + // Output Voronoi edges to .v.edge file. + if (out == (tetgenio *) NULL) { + strcpy(outfilename, b->outfilename); + strcat(outfilename, ".v.edge"); + } + + if (!b->quiet) { + if (out == (tetgenio *) NULL) { + printf("Writing %s.\n", outfilename); + } else { + printf("Writing Voronoi edges.\n"); + } + } + + if (out == (tetgenio *) NULL) { + outfile = fopen(outfilename, "w"); + if (outfile == (FILE *) NULL) { + printf("File I/O Error: Cannot create file %s.\n", outfilename); + terminatetetgen(this, 3); + } + // Number of Voronoi edges, no marker. + fprintf(outfile, "%ld 0\n", faces); + } else { + // Allocate space for 'vpointlist'. + out->numberofvedges = (int) faces; + out->vedgelist = new tetgenio::voroedge[out->numberofvedges]; + } + + // Output the Voronoi edges. + tetrahedrons->traversalinit(); + tetloop.tet = tetrahedrontraverse(); + vedgecount = 0; // D-Face (V-edge) index (from zero). + index = 0; // The Delaunay-face index. + while (tetloop.tet != (tetrahedron *) NULL) { + // Count the number of Voronoi edges. Look at the four faces of each + // tetrahedron. Count the face if the tetrahedron's index is + // smaller than its neighbor's or the neighbor is outside. + end1 = elemindex(tetloop.tet); + for (tetloop.ver = 0; tetloop.ver < 4; tetloop.ver++) { + fsym(tetloop, worktet); + if (ishulltet(worktet) || + (elemindex(tetloop.tet) < elemindex(worktet.tet))) { + // Found a Voronoi edge. Operate on it. + if (out == (tetgenio *) NULL) { + fprintf(outfile, "%4d %4d", vedgecount + shift, end1 + shift); + } else { + vedge = &(out->vedgelist[index++]); + vedge->v1 = end1 + shift; + } + if (!ishulltet(worktet)) { + end2 = elemindex(worktet.tet); + } else { + end2 = -1; + } + // Note that end2 may be -1 (worktet.tet is outside). + if (end2 == -1) { + // Calculate the out normal of this hull face. + pt[0] = dest(worktet); + pt[1] = org(worktet); + pt[2] = apex(worktet); + for (j = 0; j < 3; j++) vec1[j] = pt[1][j] - pt[0][j]; + for (j = 0; j < 3; j++) vec2[j] = pt[2][j] - pt[0][j]; + cross(vec1, vec2, infvec); + // Normalize it. + L = sqrt(infvec[0] * infvec[0] + infvec[1] * infvec[1] + + infvec[2] * infvec[2]); + if (L > 0) for (j = 0; j < 3; j++) infvec[j] /= L; + if (out == (tetgenio *) NULL) { + fprintf(outfile, " -1"); + fprintf(outfile, " %g %g %g\n", infvec[0], infvec[1], infvec[2]); + } else { + vedge->v2 = -1; + vedge->vnormal[0] = infvec[0]; + vedge->vnormal[1] = infvec[1]; + vedge->vnormal[2] = infvec[2]; + } + } else { + if (out == (tetgenio *) NULL) { + fprintf(outfile, " %4d\n", end2 + shift); + } else { + vedge->v2 = end2 + shift; + vedge->vnormal[0] = 0.0; + vedge->vnormal[1] = 0.0; + vedge->vnormal[2] = 0.0; + } + } + // Save the V-edge index in this tet and its neighbor. + fidxs = (int *) (tetloop.tet[11]); + fidxs[tetloop.ver] = vedgecount; + fidxs = (int *) (worktet.tet[11]); + fidxs[worktet.ver & 3] = vedgecount; + vedgecount++; + } + } // tetloop.ver + tetloop.tet = tetrahedrontraverse(); + } + + if (out == (tetgenio *) NULL) { + fprintf(outfile, "# Generated by %s\n", b->commandline); + fclose(outfile); + } - aspectratiotable[0] = 1.5; aspectratiotable[1] = 2.0; - aspectratiotable[2] = 2.5; aspectratiotable[3] = 3.0; - aspectratiotable[4] = 4.0; aspectratiotable[5] = 6.0; - aspectratiotable[6] = 10.0; aspectratiotable[7] = 15.0; - aspectratiotable[8] = 25.0; aspectratiotable[9] = 50.0; - aspectratiotable[10] = 100.0; aspectratiotable[11] = 0.0; + // Output Voronoi faces to .v.face file. + if (out == (tetgenio *) NULL) { + strcpy(outfilename, b->outfilename); + strcat(outfilename, ".v.face"); + } - for (i = 0; i < 12; i++) radiustable[i] = 0; - for (i = 0; i < 12; i++) aspecttable[i] = 0; - for (i = 0; i < 18; i++) dihedangletable[i] = 0; - for (i = 0; i < 18; i++) faceangletable[i] = 0; + if (!b->quiet) { + if (out == (tetgenio *) NULL) { + printf("Writing %s.\n", outfilename); + } else { + printf("Writing Voronoi faces.\n"); + } + } - minaltitude = xmax - xmin + ymax - ymin + zmax - zmin; - minaltitude = minaltitude * minaltitude; - shortest = minaltitude; - longest = 0.0; - smallestvolume = minaltitude; - biggestvolume = 0.0; - smallestratio = minaltitude; - biggestratio = 0.0; - smallestdiangle = smallestfaangle = 180.0; - biggestdiangle = biggestfaangle = 0.0; + if (out == (tetgenio *) NULL) { + outfile = fopen(outfilename, "w"); + if (outfile == (FILE *) NULL) { + printf("File I/O Error: Cannot create file %s.\n", outfilename); + terminatetetgen(this, 3); + } + // Number of Voronoi faces. + fprintf(outfile, "%ld 0\n", edges); + } else { + out->numberofvfacets = edges; + out->vfacetlist = new tetgenio::vorofacet[out->numberofvfacets]; + if (out->vfacetlist == (tetgenio::vorofacet *) NULL) { + terminatetetgen(this, 1); + } + } - // Loop all elements, calculate quality parameters for each element. + // Output the Voronoi facets. tetrahedrons->traversalinit(); tetloop.tet = tetrahedrontraverse(); + vfacecount = 0; // D-edge (V-facet) index (from zero). 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 index is + // smaller than those of all other tetrahedra that share the edge. + worktet.tet = tetloop.tet; + for (i = 0; i < 6; i++) { + worktet.ver = edge2ver[i]; + // Count the number of faces at this edge. If the edge is a hull edge, + // the face containing dummypoint is also counted. + //ishulledge = 0; // Is it a hull edge. + tcount = 0; + firsttet = worktet; + spintet = worktet; + while (1) { + tcount++; + fnextself(spintet); + if (spintet.tet == worktet.tet) break; + if (!ishulltet(spintet)) { + if (elemindex(spintet.tet) < elemindex(worktet.tet)) break; + } else { + //ishulledge = 1; + if (apex(spintet) == dummypoint) { + // We make this V-edge appear in the end of the edge list. + fnext(spintet, firsttet); + } + } + } // while (1) + if (spintet.tet == worktet.tet) { + // Found a Voronoi facet. Operate on it. + pt[0] = org(worktet); + pt[1] = dest(worktet); + end1 = pointmark(pt[0]) - in->firstnumber; // V-cell index + end2 = pointmark(pt[1]) - in->firstnumber; + if (out == (tetgenio *) NULL) { + fprintf(outfile, "%4d %4d %4d %-2d ", vfacecount + shift, + end1 + shift, end2 + shift, tcount); + } else { + vfacet = &(out->vfacetlist[vfacecount]); + vfacet->c1 = end1 + shift; + vfacet->c2 = end2 + shift; + vfacet->elist = new int[tcount + 1]; + vfacet->elist[0] = tcount; + index = 1; + } + // Output V-edges of this V-facet. + spintet = firsttet; //worktet; + while (1) { + fidxs = (int *) (spintet.tet[11]); + if (apex(spintet) != dummypoint) { + vedgecount = fidxs[spintet.ver & 3]; + ishullface = 0; + } else { + ishullface = 1; // It's not a real face. + } + if (out == (tetgenio *) NULL) { + fprintf(outfile, " %d", !ishullface ? (vedgecount + shift) : -1); + } else { + vfacet->elist[index++] = !ishullface ? (vedgecount + shift) : -1; + } + // Save the V-facet index in this tet at this edge. + eidxs = &(fidxs[4]); + eidxs[ver2edge[spintet.ver]] = vfacecount; + // Go to the next face. + fnextself(spintet); + if (spintet.tet == firsttet.tet) break; + } // while (1) + if (out == (tetgenio *) NULL) { + fprintf(outfile, "\n"); + } + vfacecount++; + } // if (spintet.tet == worktet.tet) + } // if (i = 0; i < 6; i++) + tetloop.tet = tetrahedrontraverse(); + } - // 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] - for (i = 0; i < 3; i++) V[0][i] = p[0][i] - p[3][i]; // V[0]: p3->p0. - for (i = 0; i < 3; i++) V[1][i] = p[1][i] - p[3][i]; // V[1]: p3->p1. - for (i = 0; i < 3; i++) V[2][i] = p[2][i] - p[3][i]; // V[2]: p3->p2. - for (i = 0; i < 3; i++) V[3][i] = p[1][i] - p[0][i]; // V[3]: p0->p1. - for (i = 0; i < 3; i++) V[4][i] = p[2][i] - p[1][i]; // V[4]: p1->p2. - for (i = 0; i < 3; i++) V[5][i] = p[0][i] - p[2][i]; // V[5]: p2->p0. - // Set the matrix A = [V[0], V[1], V[2]]^T. - for (j = 0; j < 3; j++) { - for (i = 0; i < 3; i++) A[j][i] = V[j][i]; - } - // Decompose A just once. - lu_decmp(A, 3, indx, &D, 0); - // Get the tet volume. - tetvol = fabs(A[indx[0]][0] * A[indx[1]][1] * A[indx[2]][2]) / 6.0; - // Get the three faces normals. - 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]; + if (out == (tetgenio *) NULL) { + fprintf(outfile, "# Generated by %s\n", b->commandline); + fclose(outfile); + } + + // Output Voronoi cells to .v.cell file. + if (out == (tetgenio *) NULL) { + strcpy(outfilename, b->outfilename); + strcat(outfilename, ".v.cell"); + } + + if (!b->quiet) { + if (out == (tetgenio *) NULL) { + printf("Writing %s.\n", outfilename); + } else { + printf("Writing Voronoi cells.\n"); } - // Get the fourth face 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]; - // Get the radius of the circumsphere. - for (i = 0; i < 3; i++) rhs[i] = 0.5 * dot(V[i], V[i]); - lu_solve(A, 3, indx, rhs, 0); - cirradius = sqrt(dot(rhs, rhs)); - // Normalize the face normals. - 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])); - 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]; - } - // Get the squares of the edge lengthes. - for (i = 0; i < 6; i++) edgelength[i] = dot(V[i], V[i]); - // Get the dihedrals (in degree) at each edges. - j = 0; - for (i = 1; i < 4; i++) { - alldihed[j] = -dot(N[0], N[i]); // Edge cd, bd, bc. - if (alldihed[j] < -1.0) alldihed[j] = -1; // Rounding. - else if (alldihed[j] > 1.0) alldihed[j] = 1; - alldihed[j] = acos(alldihed[j]) / PI * 180.0; - j++; + } + + if (out == (tetgenio *) NULL) { + outfile = fopen(outfilename, "w"); + if (outfile == (FILE *) NULL) { + printf("File I/O Error: Cannot create file %s.\n", outfilename); + terminatetetgen(this, 3); } - for (i = 2; i < 4; i++) { - alldihed[j] = -dot(N[1], N[i]); // Edge ad, ac. - if (alldihed[j] < -1.0) alldihed[j] = -1; // Rounding. - else if (alldihed[j] > 1.0) alldihed[j] = 1; - alldihed[j] = acos(alldihed[j]) / PI * 180.0; - j++; + // Number of Voronoi cells. + fprintf(outfile, "%ld\n", points->items - unuverts - dupverts); + } else { + out->numberofvcells = points->items - unuverts - dupverts; + out->vcelllist = new int*[out->numberofvcells]; + if (out->vcelllist == (int **) NULL) { + terminatetetgen(this, 1); } - alldihed[j] = -dot(N[2], N[3]); // Edge ab. - if (alldihed[j] < -1.0) alldihed[j] = -1; // Rounding. - else if (alldihed[j] > 1.0) alldihed[j] = 1; - alldihed[j] = acos(alldihed[j]) / PI * 180.0; - - // Calculate the longest and shortest edge length. - for (i = 0; i < 6; i++) { - if (i == 0) { - shortlen = longlen = edgelength[i]; - } else { - shortlen = edgelength[i] < shortlen ? edgelength[i] : shortlen; - longlen = edgelength[i] > longlen ? edgelength[i] : longlen; - } - if (edgelength[i] > longest) { - longest = edgelength[i]; - } - if (edgelength[i] < shortest) { - shortest = edgelength[i]; + } + + // Output Voronoi cells. + tetlist = cavetetlist; + ptlist = cavetetvertlist; + points->traversalinit(); + ploop = pointtraverse(); + vpointcount = 0; + while (ploop != (point) NULL) { + if ((pointtype(ploop) != UNUSEDVERTEX) && + (pointtype(ploop) != DUPLICATEDVERTEX) && + (pointtype(ploop) != NREGULARVERTEX)) { + getvertexstar(1, ploop, tetlist, ptlist, NULL); + // Mark all vertices. Check if it is a hull vertex. + ishullvert = 0; + for (i = 0; i < ptlist->objects; i++) { + neipt = * (point *) fastlookup(ptlist, i); + if (neipt != dummypoint) { + pinfect(neipt); + } else { + ishullvert = 1; + } } - } - - // Calculate the largest and smallest volume. - if (tetvol < smallestvolume) { - smallestvolume = tetvol; - } - if (tetvol > biggestvolume) { - biggestvolume = tetvol; - } - - // Calculate the largest and smallest dihedral angles. - for (i = 0; i < 6; i++) { - if (i == 0) { - smalldiangle = bigdiangle = alldihed[i]; + tcount = (int) ptlist->objects; + if (out == (tetgenio *) NULL) { + fprintf(outfile, "%4d %-2d ", vpointcount + shift, tcount); } else { - smalldiangle = alldihed[i] < smalldiangle ? alldihed[i] : smalldiangle; - bigdiangle = alldihed[i] > bigdiangle ? alldihed[i] : bigdiangle; + arraysize = tcount; + vertarray = new int[arraysize + 1]; + out->vcelllist[vpointcount] = vertarray; + vertarray[0] = tcount; + index = 1; } - if (alldihed[i] < smallestdiangle) { - smallestdiangle = alldihed[i]; - } - if (alldihed[i] > biggestdiangle) { - biggestdiangle = alldihed[i]; + // List Voronoi facets bounding this cell. + for (i = 0; i < tetlist->objects; i++) { + worktet = * (triface *) fastlookup(tetlist, i); + // Let 'worktet' be [a,b,c,d] where d = ploop. + for (j = 0; j < 3; j++) { + neipt = org(worktet); // neipt is a, or b, or c + // Skip the dummypoint. + if (neipt != dummypoint) { + if (pinfected(neipt)) { + // It's not processed yet. + puninfect(neipt); + // Go to the DT edge [a,d], or [b,d], or [c,d]. + esym(worktet, spintet); + enextself(spintet); + // Get the V-face dual to this edge. + eidxs = (int *) spintet.tet[11]; + vfacecount = eidxs[4 + ver2edge[spintet.ver]]; + if (out == (tetgenio *) NULL) { + fprintf(outfile, " %d", vfacecount + shift); + } else { + vertarray[index++] = vfacecount + shift; + } + } + } + enextself(worktet); + } // j + } // i + if (ishullvert) { + // Add a hull facet (-1) to the facet list. + if (out == (tetgenio *) NULL) { + fprintf(outfile, " -1"); + } else { + vertarray[index++] = -1; + } } - } - // Accumulate the corresponding number in the dihedral angle histogram. - if (smalldiangle < 5.0) { - tendegree = 0; - } else if (smalldiangle >= 5.0 && smalldiangle < 10.0) { - tendegree = 1; - } else if (smalldiangle >= 80.0 && smalldiangle < 110.0) { - tendegree = 9; // Angles between 80 to 110 degree are in one entry. - } else { - tendegree = (int) (smalldiangle / 10.); - if (smalldiangle < 80.0) { - tendegree++; // In the left column. - } else { - tendegree--; // In the right column. + if (out == (tetgenio *) NULL) { + fprintf(outfile, "\n"); } + tetlist->restart(); + ptlist->restart(); + vpointcount++; } - dihedangletable[tendegree]++; - if (bigdiangle >= 80.0 && bigdiangle < 110.0) { - tendegree = 9; // Angles between 80 to 110 degree are in one entry. - } else if (bigdiangle >= 170.0 && bigdiangle < 175.0) { - tendegree = 16; - } else if (bigdiangle >= 175.0) { - tendegree = 17; - } else { - tendegree = (int) (bigdiangle / 10.); - if (bigdiangle < 80.0) { - tendegree++; // In the left column. + ploop = pointtraverse(); + } + + // Delete the space for face/edge indices. + delete [] indexarray; + + if (out == (tetgenio *) NULL) { + fprintf(outfile, "# Generated by %s\n", b->commandline); + fclose(outfile); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// outsmesh() Write surface mesh to a .smesh file, which can be read and // +// tetrahedralized by TetGen. // +// // +// You can specify a filename (without suffix) in 'smfilename'. If you don't // +// supply a filename (let smfilename be NULL), the default name stored in // +// 'tetgenbehavior' will be used. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::outsmesh(char* smfilename) +{ + FILE *outfile; + char nodfilename[FILENAMESIZE]; + char smefilename[FILENAMESIZE]; + face faceloop; + point p1, p2, p3; + int firstindex, shift; + int bmark; + int faceid, marker; + int i; + + if (smfilename != (char *) NULL && smfilename[0] != '\0') { + strcpy(smefilename, smfilename); + } else if (b->outfilename[0] != '\0') { + strcpy(smefilename, b->outfilename); + } else { + strcpy(smefilename, "unnamed"); + } + strcpy(nodfilename, smefilename); + strcat(smefilename, ".smesh"); + strcat(nodfilename, ".node"); + + if (!b->quiet) { + printf("Writing %s.\n", smefilename); + } + outfile = fopen(smefilename, "w"); + if (outfile == (FILE *) NULL) { + printf("File I/O Error: Cannot create file %s.\n", smefilename); + return; + } + + // 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. + } + + fprintf(outfile, "# %s. TetGen's input file.\n", smefilename); + fprintf(outfile, "\n# part 1: node list.\n"); + fprintf(outfile, "0 3 0 0 # nodes are found in %s.\n", nodfilename); + + marker = 0; // avoid compile warning. + bmark = !b->nobound && in->facetmarkerlist; + + fprintf(outfile, "\n# part 2: facet list.\n"); + // Number of facets, boundary marker. + fprintf(outfile, "%ld %d\n", subfaces->items, bmark); + + subfaces->traversalinit(); + faceloop.sh = shellfacetraverse(subfaces); + while (faceloop.sh != (shellface *) NULL) { + p1 = sorg(faceloop); + p2 = sdest(faceloop); + p3 = sapex(faceloop); + if (bmark) { + faceid = shellmark(faceloop) - 1; + if (faceid >= 0) { + marker = in->facetmarkerlist[faceid]; } else { - tendegree--; // In the right column. + marker = 0; // This subface must be added manually later. } } - dihedangletable[tendegree]++; - - // Calulate the largest and smallest face angles. - tetloop.ver = 0; - for (tetloop.loc = 0; tetloop.loc < 4; tetloop.loc++) { - sym(tetloop, neightet); - // Only do the calulation once for a face. - if ((neightet.tet == dummytet) || (tetloop.tet < neightet.tet)) { - p[0] = org(tetloop); - p[1] = dest(tetloop); - p[2] = apex(tetloop); - faceangle[0] = interiorangle(p[0], p[1], p[2], NULL); - faceangle[1] = interiorangle(p[1], p[2], p[0], NULL); - faceangle[2] = PI - (faceangle[0] + faceangle[1]); - // Translate angles into degrees. - for (i = 0; i < 3; i++) { - faceangle[i] = (faceangle[i] * 180.0) / PI; - } - // Calculate the largest and smallest face angles. - for (i = 0; i < 3; i++) { - if (i == 0) { - smallfaangle = bigfaangle = faceangle[i]; - } else { - smallfaangle = faceangle[i] < smallfaangle ? - faceangle[i] : smallfaangle; - bigfaangle = faceangle[i] > bigfaangle ? faceangle[i] : bigfaangle; - } - if (faceangle[i] < smallestfaangle) { - smallestfaangle = faceangle[i]; - } - if (faceangle[i] > biggestfaangle) { - biggestfaangle = faceangle[i]; - } - } - tendegree = (int) (smallfaangle / 10.); - faceangletable[tendegree]++; - tendegree = (int) (bigfaangle / 10.); - faceangletable[tendegree]++; - } + fprintf(outfile, "3 %4d %4d %4d", pointmark(p1) - shift, + pointmark(p2) - shift, pointmark(p3) - shift); + if (bmark) { + fprintf(outfile, " %d", marker); } + fprintf(outfile, "\n"); + faceloop.sh = shellfacetraverse(subfaces); + } - // Calculate aspect ratio and radius-edge ratio for this element. - 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++; - } - aspecttable[aspectindex]++; - radiusindex = 0; - while ((tetradius > radiusratiotable[radiusindex]) && (radiusindex < 11)) { - radiusindex++; - } - radiustable[radiusindex]++; + // Copy input holelist. + fprintf(outfile, "\n# part 3: hole list.\n"); + fprintf(outfile, "%d\n", in->numberofholes); + for (i = 0; i < in->numberofholes; i++) { + fprintf(outfile, "%d %g %g %g\n", i + in->firstnumber, + in->holelist[i * 3], in->holelist[i * 3 + 1], + in->holelist[i * 3 + 2]); + } - tetloop.tet = tetrahedrontraverse(); + // Copy input regionlist. + fprintf(outfile, "\n# part 4: region list.\n"); + fprintf(outfile, "%d\n", in->numberofregions); + for (i = 0; i < in->numberofregions; i++) { + fprintf(outfile, "%d %g %g %g %d %g\n", i + in->firstnumber, + in->regionlist[i * 5], in->regionlist[i * 5 + 1], + in->regionlist[i * 5 + 2], (int) in->regionlist[i * 5 + 3], + in->regionlist[i * 5 + 4]); } - shortest = sqrt(shortest); - longest = sqrt(longest); - minaltitude = sqrt(minaltitude); + fprintf(outfile, "# Generated by %s\n", b->commandline); + fclose(outfile); +} - printf(" Smallest volume: %16.5g | Largest volume: %16.5g\n", - 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'; +/////////////////////////////////////////////////////////////////////////////// +// // +// outmesh2medit() Write mesh to a .mesh file, which can be read and // +// rendered by Medit (a free mesh viewer from INRIA). // +// // +// You can specify a filename (without suffix) in 'mfilename'. If you don't // +// supply a filename (let mfilename be NULL), the default name stored in // +// 'tetgenbehavior' will be used. The output file will have the suffix .mesh.// +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::outmesh2medit(char* mfilename) +{ + FILE *outfile; + char mefilename[FILENAMESIZE]; + tetrahedron* tetptr; + triface tface, tsymface; + face segloop, checkmark; + point ptloop, p1, p2, p3, p4; + long ntets, faces; + int pointnumber; + int faceid, marker; + int i; + + if (mfilename != (char *) NULL && mfilename[0] != '\0') { + strcpy(mefilename, mfilename); + } else if (b->outfilename[0] != '\0') { + strcpy(mefilename, b->outfilename); + } else { + strcpy(mefilename, "unnamed"); } - printf(" Smallest facangle: %14.5g | Largest facangle: %s\n", - smallestfaangle, sbuf); - sprintf(sbuf, "%.17g", biggestdiangle); - if (strlen(sbuf) > 8) { - sbuf[8] = '\0'; + strcat(mefilename, ".mesh"); + + if (!b->quiet) { + printf("Writing %s.\n", mefilename); + } + outfile = fopen(mefilename, "w"); + if (outfile == (FILE *) NULL) { + printf("File I/O Error: Cannot create file %s.\n", mefilename); + return; } - printf(" Smallest dihedral: %14.5g | Largest dihedral: %s\n\n", - smallestdiangle, sbuf); - /* - printf(" Radius-edge ratio histogram:\n"); - printf(" < %-6.6g : %8d | %6.6g - %-6.6g : %8d\n", - radiusratiotable[0], radiustable[0], radiusratiotable[5], - radiusratiotable[6], radiustable[6]); - for (i = 1; i < 5; i++) { - printf(" %6.6g - %-6.6g : %8d | %6.6g - %-6.6g : %8d\n", - radiusratiotable[i - 1], radiusratiotable[i], radiustable[i], - radiusratiotable[i + 5], radiusratiotable[i + 6], - radiustable[i + 6]); - } - printf(" %6.6g - %-6.6g : %8d | %6.6g - : %8d\n", - radiusratiotable[4], radiusratiotable[5], radiustable[5], - radiusratiotable[10], radiustable[11]); - printf(" (A tetrahedron's radius-edge ratio is its radius of "); - printf("circumsphere divided\n"); - printf(" by its shortest edge length)\n\n"); - */ + fprintf(outfile, "MeshVersionFormatted 1\n"); + fprintf(outfile, "\n"); + fprintf(outfile, "Dimension\n"); + fprintf(outfile, "3\n"); + fprintf(outfile, "\n"); - printf(" Aspect ratio histogram:\n"); - printf(" < %-6.6g : %8d | %6.6g - %-6.6g : %8d\n", - aspectratiotable[0], aspecttable[0], aspectratiotable[5], - aspectratiotable[6], aspecttable[6]); - for (i = 1; i < 5; i++) { - printf(" %6.6g - %-6.6g : %8d | %6.6g - %-6.6g : %8d\n", - aspectratiotable[i - 1], aspectratiotable[i], aspecttable[i], - aspectratiotable[i + 5], aspectratiotable[i + 6], - aspecttable[i + 6]); + fprintf(outfile, "\n# Set of mesh vertices\n"); + fprintf(outfile, "Vertices\n"); + fprintf(outfile, "%ld\n", points->items); + + points->traversalinit(); + ptloop = pointtraverse(); + pointnumber = 1; // Medit need start number form 1. + while (ptloop != (point) NULL) { + // Point coordinates. + fprintf(outfile, "%.17g %.17g %.17g", ptloop[0], ptloop[1], ptloop[2]); + if (in->numberofpointattributes > 0) { + // Write an attribute, ignore others if more than one. + fprintf(outfile, " %.17g\n", ptloop[3]); + } else { + fprintf(outfile, " 0\n"); + } + setpointmark(ptloop, pointnumber); + ptloop = pointtraverse(); + pointnumber++; } - printf(" %6.6g - %-6.6g : %8d | %6.6g - : %8d\n", - aspectratiotable[4], aspectratiotable[5], aspecttable[5], - aspectratiotable[10], aspecttable[11]); - printf(" (A tetrahedron's aspect ratio is its longest edge length"); - printf(" divided by its\n"); - printf(" smallest side height)\n\n"); - printf(" Face angle histogram:\n"); - for (i = 0; i < 9; i++) { - printf(" %3d - %3d degrees: %8d | %3d - %3d degrees: %8d\n", - i * 10, i * 10 + 10, faceangletable[i], - i * 10 + 90, i * 10 + 100, faceangletable[i + 9]); + // Compute the number of faces. + ntets = tetrahedrons->items - hullsize; + faces = (ntets * 4l + hullsize) / 2l; + + fprintf(outfile, "\n# Set of Triangles\n"); + fprintf(outfile, "Triangles\n"); + fprintf(outfile, "%ld\n", faces); + + tetrahedrons->traversalinit(); + tface.tet = tetrahedrontraverse(); + while (tface.tet != (tetrahedron *) NULL) { + for (tface.ver = 0; tface.ver < 4; tface.ver ++) { + fsym(tface, tsymface); + if (ishulltet(tsymface) || + (elemindex(tface.tet) < elemindex(tsymface.tet))) { + p1 = org (tface); + p2 = dest(tface); + p3 = apex(tface); + fprintf(outfile, "%5d %5d %5d", + pointmark(p1), pointmark(p2), pointmark(p3)); + // Check if it is a subface. + tspivot(tface, checkmark); + if (checkmark.sh == NULL) { + marker = 0; // It is an inner face. It's marker is 0. + } else { + if (in->facetmarkerlist) { + // The facet marker is given, get it. + faceid = shellmark(checkmark) - 1; + marker = in->facetmarkerlist[faceid]; + } else { + marker = 1; // The default marker for subface is 1. + } + } + fprintf(outfile, " %d\n", marker); + } + } + tface.tet = tetrahedrontraverse(); } - if (minfaceang != PI) { - printf(" Minimum input face angle is %g (degree).\n", - minfaceang / PI * 180.0); + + fprintf(outfile, "\n# Set of Tetrahedra\n"); + fprintf(outfile, "Tetrahedra\n"); + fprintf(outfile, "%ld\n", ntets); + + tetrahedrons->traversalinit(); + tetptr = tetrahedrontraverse(); + while (tetptr != (tetrahedron *) NULL) { + if (!b->reversetetori) { + p1 = (point) tetptr[4]; + p2 = (point) tetptr[5]; + } else { + p1 = (point) tetptr[5]; + p2 = (point) tetptr[4]; + } + p3 = (point) tetptr[6]; + p4 = (point) tetptr[7]; + fprintf(outfile, "%5d %5d %5d %5d", + pointmark(p1), pointmark(p2), pointmark(p3), pointmark(p4)); + if (numelemattrib > 0) { + fprintf(outfile, " %.17g", elemattribute(tetptr, 0)); + } else { + fprintf(outfile, " 0"); + } + fprintf(outfile, "\n"); + tetptr = tetrahedrontraverse(); } - printf("\n"); - printf(" Dihedral angle histogram:\n"); - // Print the three two rows: - printf(" %3d - %2d degrees: %8d | %3d - %3d degrees: %8d\n", - 0, 5, dihedangletable[0], 80, 110, dihedangletable[9]); - printf(" %3d - %2d degrees: %8d | %3d - %3d degrees: %8d\n", - 5, 10, dihedangletable[1], 110, 120, dihedangletable[10]); - // Print the third to seventh rows. - for (i = 2; i < 7; i++) { - printf(" %3d - %2d degrees: %8d | %3d - %3d degrees: %8d\n", - (i - 1) * 10, (i - 1) * 10 + 10, dihedangletable[i], - (i - 1) * 10 + 110, (i - 1) * 10 + 120, dihedangletable[i + 9]); + fprintf(outfile, "\nCorners\n"); + fprintf(outfile, "%d\n", in->numberofpoints); + + for (i = 0; i < in->numberofpoints; i++) { + fprintf(outfile, "%4d\n", i + 1); } - // Print the last two rows. - printf(" %3d - %2d degrees: %8d | %3d - %3d degrees: %8d\n", - 60, 70, dihedangletable[7], 170, 175, dihedangletable[16]); - printf(" %3d - %2d degrees: %8d | %3d - %3d degrees: %8d\n", - 70, 80, dihedangletable[8], 175, 180, dihedangletable[17]); - if (minfacetdihed != PI) { - printf(" Minimum input facet dihedral angle is %g (degree).\n", - minfacetdihed / PI * 180.0); + + if (b->plc || b->refine) { + fprintf(outfile, "\nEdges\n"); + fprintf(outfile, "%ld\n", subsegs->items); + + subsegs->traversalinit(); + segloop.sh = shellfacetraverse(subsegs); + while (segloop.sh != (shellface *) NULL) { + p1 = sorg(segloop); + p2 = sdest(segloop); + fprintf(outfile, "%5d %5d", pointmark(p1), pointmark(p2)); + marker = shellmark(segloop); + fprintf(outfile, " %d\n", marker); + segloop.sh = shellfacetraverse(subsegs); + } } - printf("\n"); + + fprintf(outfile, "\nEnd\n"); + fclose(outfile); } + + /////////////////////////////////////////////////////////////////////////////// // // -// statistics() Print all sorts of cool facts. // +// outmesh2vtk() Save mesh to file in VTK Legacy format. // +// // +// This function was contributed by Bryn Llyod from ETH, 2007. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::statistics() +void tetgenmesh::outmesh2vtk(char* ofilename) { - printf("\nStatistics:\n\n"); - printf(" Input points: %d\n", in->numberofpoints + jettisoninverts); - if (b->refine) { - printf(" Input tetrahedra: %d\n", in->numberoftetrahedra); - } - if (b->plc) { - printf(" Input facets: %d\n", in->numberoffacets); - printf(" Input segments: %ld\n", insegments); - printf(" Input holes: %d\n", in->numberofholes); - printf(" Input regions: %d\n", in->numberofregions); + FILE *outfile; + char vtkfilename[FILENAMESIZE]; + point pointloop, p1, p2, p3, p4; + tetrahedron* tptr; + double x, y, z; + int n1, n2, n3, n4; + int nnodes = 4; + int celltype = 10; + + if (b->order == 2) { + printf(" Write VTK not implemented for order 2 elements \n"); + return; } - 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); + int NEL = tetrahedrons->items - hullsize; + int NN = points->items; - if (b->plc || b->refine) { - printf(" Mesh boundary faces: %ld\n", subfaces->items); - printf(" Mesh boundary edges: %ld\n\n", subsegs->items); + if (ofilename != (char *) NULL && ofilename[0] != '\0') { + strcpy(vtkfilename, ofilename); + } else if (b->outfilename[0] != '\0') { + strcpy(vtkfilename, b->outfilename); } else { - printf(" Convex hull faces: %ld\n\n", hullsize); + strcpy(vtkfilename, "unnamed"); } + strcat(vtkfilename, ".vtk"); - if (b->verbose > 0) { - if (b->plc || b->refine) { - qualitystatistics(); + 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; + while (tptr != (tetrahedron *) NULL) { + if (!b->reversetetori) { + p1 = (point) tptr[4]; + p2 = (point) tptr[5]; + } else { + p1 = (point) tptr[5]; + p2 = (point) tptr[4]; + } + p3 = (point) tptr[6]; + p4 = (point) tptr[7]; + n1 = pointmark(p1) - in->firstnumber; + n2 = pointmark(p2) - in->firstnumber; + n3 = pointmark(p3) - in->firstnumber; + n4 = pointmark(p4) - in->firstnumber; + 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; tid 0) { + // Output tetrahedra region attributes. + fprintf(outfile, "CELL_DATA %d\n", NEL); + fprintf(outfile, "SCALARS cell_scalars int 1\n"); + fprintf(outfile, "LOOKUP_TABLE default\n"); + tetrahedrons->traversalinit(); + tptr = tetrahedrontraverse(); + while (tptr != (tetrahedron *) NULL) { + fprintf(outfile, "%d\n", (int) elemattribute(tptr, numelemattrib - 1)); + tptr = tetrahedrontraverse(); } - // algorithmicstatistics(); + fprintf(outfile, "\n"); } + + fclose(outfile); } //// //// //// //// -//// report_cxx /////////////////////////////////////////////////////////////// +//// output_cxx /////////////////////////////////////////////////////////////// //// main_cxx ///////////////////////////////////////////////////////////////// //// //// @@ -34426,217 +30853,226 @@ void tetgenmesh::statistics() // - Read the vertices from a file and either // // - tetrahedralize them (no -r), or // // - read an old mesh from files and reconstruct it (-r). // -// - Insert the PLC segments and facets (-p). // +// - Insert the boundary segments and facets (-p or -Y). // // - Read the holes (-p), regional attributes (-pA), and regional volume // // constraints (-pa). Carve the holes and concavities, and spread the // // regional attributes and volume constraints. // // - Enforce the constraints on minimum quality bound (-q) and maximum // -// volume (-a). Also enforce the conforming Delaunay property (-q and -a). // -// - Promote the mesh's linear tetrahedra to higher order elements (-o). // +// volume (-a), and a mesh size function (-m). // +// - Optimize the mesh wrt. specified quality measures (-O and -o). // // - Write the output files and print the statistics. // -// - Check the consistency and Delaunay property of the mesh (-C). // +// - Check the consistency of the mesh (-C). // // // /////////////////////////////////////////////////////////////////////////////// void tetrahedralize(tetgenbehavior *b, tetgenio *in, tetgenio *out, - tetgenio *addin, tetgenio *bgmin) + tetgenio *addin, tetgenio *bgmin) { tetgenmesh m; - // Variables for timing the performance of TetGen (defined in time.h). - clock_t tv[17]; + clock_t tv[12], ts[5]; // Timing informations (defined in time.h) + REAL cps = (REAL) CLOCKS_PER_SEC; tv[0] = clock(); m.b = b; m.in = in; - m.macheps = exactinit(); - m.steinerleft = b->steiner; - if (b->metric) { - m.bgm = new tetgenmesh(); + m.addin = addin; + + if (b->metric && bgmin && (bgmin->numberofpoints > 0)) { + m.bgm = new tetgenmesh(); // Create an empty background mesh. m.bgm->b = b; m.bgm->in = bgmin; - m.bgm->macheps = exactinit(); } + m.initializepools(); m.transfernodes(); + exactinit(b->verbose, b->noexact, b->nostaticfilter, + m.xmax - m.xmin, m.ymax - m.ymin, m.zmax - m.zmin); + tv[1] = clock(); - if (b->refine) { + if (b->refine) { // -r m.reconstructmesh(); - } else { - m.delaunizevertices(); - if (m.hullsize == 0l) { - printf("The input point set does not span a 3D subspace.\n"); - return; - } + } else { // -p + m.incrementaldelaunay(ts[0]); } tv[2] = clock(); if (!b->quiet) { if (b->refine) { - printf("Mesh reconstruction seconds:"); + printf("Mesh reconstruction seconds: %g\n", ((REAL)(tv[2]-tv[1])) / cps); } else { - printf("Delaunay seconds:"); + printf("Delaunay seconds: %g\n", ((REAL)(tv[2]-tv[1])) / cps); + if (b->verbose) { + printf(" Point sorting seconds: %g\n", ((REAL)(ts[0]-tv[1])) / cps); + } } - printf(" %g\n", (tv[2] - tv[1]) / (REAL) CLOCKS_PER_SEC); } - if (b->metric) { - if (bgmin != (tetgenio *) NULL) { - m.bgm->initializepools(); - m.bgm->transfernodes(); - m.bgm->reconstructmesh(); - } else { - m.bgm->in = in; - m.bgm->initializepools(); - m.duplicatebgmesh(); + if (b->plc && !b->refine) { // -p + m.meshsurface(); + + ts[0] = clock(); + + if (!b->quiet) { + printf("Surface mesh seconds: %g\n", ((REAL)(ts[0]-tv[2])) / cps); + } + + if (b->diagnose) { // -d + m.detectinterfaces(); + + ts[1] = clock(); + + if (!b->quiet) { + printf("Self-intersection seconds: %g\n", ((REAL)(ts[1]-ts[0])) / cps); + } + + // Only output when self-intersecting faces exist. + if (m.subfaces->items > 0l) { + m.outnodes(out); + m.outsubfaces(out); + } + + return; } } tv[3] = clock(); - if (!b->quiet) { - if (b->metric) { + if ((b->metric) && (m.bgm != NULL)) { // -m + m.bgm->initializepools(); + m.bgm->transfernodes(); + m.bgm->reconstructmesh(); + + ts[0] = clock(); + + if (!b->quiet) { printf("Background mesh reconstruct seconds: %g\n", - (tv[3] - tv[2]) / (REAL) CLOCKS_PER_SEC); + ((REAL)(ts[0] - tv[3])) / cps); } - } - if (b->useshelles && !b->refine) { - m.meshsurface(); - tv[14] = clock(); - if (b->diagnose != 1) { - m.markacutevertices(60.0); - m.formskeleton(tv[15]); - } else { - m.detectinterfaces(); + if (b->metric) { // -m + m.interpolatemeshsize(); + + ts[1] = clock(); + + if (!b->quiet) { + printf("Size interpolating seconds: %g\n",((REAL)(ts[1]-ts[0])) / cps); + } } } tv[4] = clock(); - if (!b->quiet) { - if (b->useshelles && !b->refine) { - if (b->diagnose != 1) { + if (b->plc && !b->refine) { // -p + if (b->nobisect) { // -Y + m.recoverboundary(ts[0]); + } else { + m.constraineddelaunay(ts[0]); + } + + ts[1] = clock(); + + if (!b->quiet) { + if (b->nobisect) { printf("Boundary recovery "); } else { - printf("Intersection "); + printf("Constrained Delaunay "); + } + printf("seconds: %g\n", ((REAL)(ts[1] - tv[4])) / cps); + if (b->verbose) { + printf(" Segment recovery seconds: %g\n",((REAL)(ts[0]-tv[4]))/ cps); + printf(" Facet recovery seconds: %g\n", ((REAL)(ts[1]-ts[0])) / cps); } - printf("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); - }*/ } - } - if (b->plc && !(b->diagnose == 1)) { m.carveholes(); - } - tv[5] = clock(); + ts[2] = clock(); - if (!b->quiet) { - if (b->plc && !(b->diagnose == 1)) { - printf("Hole seconds: %g\n", (tv[5] - tv[4]) / (REAL) CLOCKS_PER_SEC); + if (!b->quiet) { + printf("Exterior tets removal seconds: %g\n",((REAL)(ts[2]-ts[1]))/cps); } - } - if ((b->plc || b->refine) && !(b->diagnose == 1)) { - m.optimizemesh2(false); - } + if (b->nobisect) { // -Y + if (m.subvertstack->objects > 0l) { + m.suppresssteinerpoints(); - tv[6] = clock(); + ts[3] = clock(); - if (!b->quiet) { - if ((b->plc || b->refine) && !(b->diagnose == 1)) { - printf("Repair seconds: %g\n", (tv[6] - tv[5]) / (REAL) CLOCKS_PER_SEC); + if (!b->quiet) { + printf("Steiner suppression seconds: %g\n", + ((REAL)(ts[3]-ts[2]))/cps); + } + } } } - if ((b->plc && b->nobisect) && !(b->diagnose == 1)) { - m.removesteiners2(); + tv[5] = clock(); + + if (b->coarsen) { // -R + m.meshcoarsening(); } - tv[7] = clock(); + tv[6] = clock(); if (!b->quiet) { - if ((b->plc && b->nobisect) && !(b->diagnose == 1)) { - printf("Steiner removal seconds: %g\n", - (tv[7] - tv[6]) / (REAL) CLOCKS_PER_SEC); + if (b->coarsen) { + printf("Mesh coarsening seconds: %g\n", ((REAL)(tv[6] - tv[5])) / cps); } } - if (b->insertaddpoints && (addin != (tetgenio *) NULL)) { - if (addin->numberofpoints > 0) { - m.insertconstrainedpoints(addin); - } + if ((b->plc && b->nobisect) || b->coarsen) { + m.recoverdelaunay(); } - tv[8] = clock(); + tv[7] = clock(); if (!b->quiet) { - if ((b->plc || b->refine) && (b->insertaddpoints)) { - printf("Constrained points seconds: %g\n", - (tv[8] - tv[7]) / (REAL) CLOCKS_PER_SEC); + if ((b->plc && b->nobisect) || b->coarsen) { + printf("Delaunay recovery seconds: %g\n", ((REAL)(tv[7] - tv[6]))/cps); } } - if (b->metric) { - m.interpolatesizemap(); - } - - tv[9] = clock(); - - if (!b->quiet) { - if (b->metric) { - printf("Size interpolating seconds: %g\n", - (tv[9] - tv[8]) / (REAL) CLOCKS_PER_SEC); + if ((b->plc || b->refine) && b->insertaddpoints) { // -i + if ((addin != NULL) && (addin->numberofpoints > 0)) { + m.insertconstrainedpoints(addin); } } - //if (b->coarse) { - // m.removesteiners2(true); - //} - - tv[10] = clock(); + tv[8] = clock(); if (!b->quiet) { - if (b->coarse) { - printf("Mesh coarsening seconds: %g\n", - (tv[10] - tv[9]) / (REAL) CLOCKS_PER_SEC); + if ((b->plc || b->refine) && b->insertaddpoints) { // -i + if ((addin != NULL) && (addin->numberofpoints > 0)) { + printf("Constrained points seconds: %g\n", ((REAL)(tv[8]-tv[7]))/cps); + } } } if (b->quality) { - m.enforcequality(); + m.delaunayrefinement(); } - tv[11] = clock(); + tv[9] = clock(); if (!b->quiet) { if (b->quality) { - printf("Quality seconds: %g\n", - (tv[11] - tv[10]) / (REAL) CLOCKS_PER_SEC); + printf("Refinement seconds: %g\n", ((REAL)(tv[9] - tv[8])) / cps); } } - if (b->quality && (b->optlevel > 0)) { - m.optimizemesh2(true); + if ((b->plc || b->refine) && (b->optlevel > 0)) { + m.optimizemesh(); } - tv[12] = clock(); + tv[10] = clock(); if (!b->quiet) { - if (b->quality && (b->optlevel > 0)) { - printf("Optimize seconds: %g\n", - (tv[12] - tv[11]) / (REAL) CLOCKS_PER_SEC); + if ((b->plc || b->refine) && (b->optlevel > 0)) { + printf("Optimization seconds: %g\n", ((REAL)(tv[10] - tv[9])) / cps); } } @@ -34645,7 +31081,7 @@ void tetrahedralize(tetgenbehavior *b, tetgenio *in, tetgenio *out, m.jettisonnodes(); } - if (b->order > 1) { + if ((b->order == 2) && !b->convex) { m.highorder(); } @@ -34663,28 +31099,16 @@ void tetrahedralize(tetgenbehavior *b, tetgenio *in, tetgenio *out, printf("NOT writing a .node file.\n"); } } else { - if (b->diagnose == 1) { - if (m.subfaces->items > 0l) { - m.outnodes(out); // Only output when self-intersecting faces exist. - } - } else { - m.outnodes(out); - if (b->quality && b->metric) { - m.outmetrics(out); - } - } + m.outnodes(out); } - if (b->noelewritten == 1) { + if (b->noelewritten) { if (!b->quiet) { printf("NOT writing an .ele file.\n"); } - m.numberedges(); } else { - if (!(b->diagnose == 1)) { - if (m.tetrahedrons->items > 0l) { - m.outelements(out); - } + if (m.tetrahedrons->items > 0l) { + m.outelements(out); } } @@ -34698,11 +31122,7 @@ void tetrahedralize(tetgenbehavior *b, tetgenio *in, tetgenio *out, m.outfaces(out); // Output all faces. } } else { - if (b->diagnose == 1) { - if (m.subfaces->items > 0l) { - m.outsubfaces(out); // Only output self-intersecting faces. - } - } else if (b->plc || b->refine) { + if (b->plc || b->refine) { if (m.subfaces->items > 0l) { m.outsubfaces(out); // Output boundary faces. } @@ -34714,18 +31134,25 @@ void tetrahedralize(tetgenbehavior *b, tetgenio *in, tetgenio *out, } } - //if (m.checkpbcs) { - // m.outpbcnodes(out); - //} - if (b->edgesout) { - if (b->edgesout > 1) { - m.outedges(out); // -ee, output all mesh edges. + if (b->nofacewritten) { + if (!b->quiet) { + printf("NOT writing an .edge file.\n"); + } + } else { + if (b->edgesout) { // -e + m.outedges(out); // output all mesh edges. } else { - m.outsubsegments(out); // -e, only output subsegments. + if (b->plc || b->refine) { + m.outsubsegments(out); // output subsegments. + } } } + if ((b->plc || b->refine) && b->metric) { // -m + m.outmetrics(out); + } + if (!out && b->plc && ((b->object == tetgenbehavior::OFF) || (b->object == tetgenbehavior::PLY) || @@ -34737,13 +31164,6 @@ void tetrahedralize(tetgenbehavior *b, tetgenio *in, tetgenio *out, m.outmesh2medit(b->outfilename); } - if (!out && b->gidview) { - m.outmesh2gid(b->outfilename); - } - - if (!out && b->geomview) { - m.outmesh2off(b->outfilename); - } if (!out && b->vtkview) { m.outmesh2vtk(b->outfilename); @@ -34753,43 +31173,32 @@ void tetrahedralize(tetgenbehavior *b, tetgenio *in, tetgenio *out, m.outneighbors(out); } - if (b->voroout) { + if ((!(b->plc || b->refine)) && b->voroout) { m.outvoronoi(out); } - tv[13] = clock(); + + tv[11] = clock(); if (!b->quiet) { - printf("\nOutput seconds: %g\n", - (tv[13] - tv[12]) / (REAL) CLOCKS_PER_SEC); - printf("Total running seconds: %g\n", - (tv[13] - tv[0]) / (REAL) CLOCKS_PER_SEC); + printf("\nOutput seconds: %g\n", ((REAL)(tv[11] - tv[10])) / cps); + printf("Total running seconds: %g\n", ((REAL)(tv[11] - tv[0])) / cps); } if (b->docheck) { - m.checkmesh(); - if (m.checksubfaces) { + m.checkmesh(0); + if (b->plc || b->refine) { m.checkshells(); + m.checksegments(); } if (b->docheck > 1) { - if (m.checkdelaunay(0.0, NULL) > 0) { - assert(0); - } - if (b->docheck > 2) { - if (b->quality || b->refine) { - m.checkconforming(); - } - } + m.checkdelaunay(); } } if (!b->quiet) { m.statistics(); } - - if (b->metric) { - delete m.bgm; - } exactdeinit(); } @@ -34797,7 +31206,7 @@ void tetrahedralize(tetgenbehavior *b, tetgenio *in, tetgenio *out, /////////////////////////////////////////////////////////////////////////////// // // -// main() The entrance for running TetGen from command line. // +// main() The command line interface of TetGen. // // // /////////////////////////////////////////////////////////////////////////////// @@ -34807,12 +31216,12 @@ int main(int argc, char *argv[]) /////////////////////////////////////////////////////////////////////////////// // // -// tetrahedralize() The entrance for calling TetGen from another program. // +// tetrahedralize() The library interface of TetGen. // // // /////////////////////////////////////////////////////////////////////////////// -void tetrahedralize(char *switches, tetgenio *in, tetgenio *out, - tetgenio *addin, tetgenio *bgmin) +void tetrahedralize(char *switches, tetgenio *in, tetgenio *out, + tetgenio *addin, tetgenio *bgmin) #endif // not TETLIBRARY @@ -34824,40 +31233,36 @@ void tetrahedralize(char *switches, tetgenio *in, tetgenio *out, tetgenio in, addin, bgmin; if (!b.parse_commandline(argc, argv)) { - terminatetetgen(0); + terminatetetgen(NULL, 10); } - if (b.refine) { - if (!in.load_tetmesh(b.infilename)) { - terminatetetgen(3); + + // Read input files. + if (b.refine) { // -r + if (!in.load_tetmesh(b.infilename, (int) b.object)) { + terminatetetgen(NULL, 10); } - } else { + } else { // -p if (!in.load_plc(b.infilename, (int) b.object)) { - terminatetetgen(3); + terminatetetgen(NULL, 10); } } - if (b.insertaddpoints) { - if (!addin.load_node(b.addinfilename)) { - addin.numberofpoints = 0l; - } + if (b.insertaddpoints) { // -i + // Try to read a .a.node file. + addin.load_node(b.addinfilename); } - if (b.metric) { - if (!bgmin.load_tetmesh(b.bgmeshfilename)) { - bgmin.numberoftetrahedra = 0l; - } + if (b.metric) { // -m + // Try to read a background mesh in files .b.node, .b.ele. + bgmin.load_tetmesh(b.bgmeshfilename, (int) b.object); } - if (bgmin.numberoftetrahedra > 0l) { - tetrahedralize(&b, &in, NULL, &addin, &bgmin); - } else { - tetrahedralize(&b, &in, NULL, &addin, NULL); - } + tetrahedralize(&b, &in, NULL, &addin, &bgmin); return 0; #else // with TETLIBRARY if (!b.parse_commandline(switches)) { - terminatetetgen(1); + terminatetetgen(NULL, 10); } tetrahedralize(&b, in, out, addin, bgmin); diff --git a/src/cpp/tetgen.h b/src/cpp/tetgen.h index d451614..e434dba 100644 --- a/src/cpp/tetgen.h +++ b/src/cpp/tetgen.h @@ -2,85 +2,48 @@ // // // TetGen // // // -// A Quality Tetrahedral Mesh Generator and 3D Delaunay Triangulator // +// A Quality Tetrahedral Mesh Generator and A 3D Delaunay Triangulator // // // -// Version 1.4 // -// September 6, December 13, 2009 // +// Version 1.5 // +// November 4, 2013 // // // -// Copyright (C) 2002--2009 // -// Hang Si // -// Research Group: Numerical Mathematics and Scientific Computing // -// Weierstrass Institute for Applied Analysis and Stochastics (WIAS) // -// Mohrenstr. 39, 10117 Berlin, Germany // -// si@wias-berlin.de // -// // -// TetGen is freely available through the website: http://tetgen.berlios.de. // +// TetGen is freely available through the website: http://www.tetgen.org. // // It may be copied, modified, and redistributed for non-commercial use. // // Please consult the file LICENSE for the detailed copyright notices. // // // /////////////////////////////////////////////////////////////////////////////// -/////////////////////////////////////////////////////////////////////////////// -// // -// 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 incorporates a suit of geometrical and mesh generation algorithms. // -// A brief description of algorithms used in TetGen is found in the first // -// section of the user's manual. References are given for users who are // -// interesting in these approaches. The main references are given below: // -// // -// The efficient Delaunay tetrahedralization algorithm is: H. Edelsbrunner // -// and N. R. Shah, "Incremental Topological Flipping Works for Regular // -// Triangulations". Algorithmica 15: 223--241, 1996. // -// // -// The constrained Delaunay tetrahedralization algorithm is described in: // -// H. Si and K. Gaertner, "Meshing Piecewise Linear Complexes by Constr- // -// ained Delaunay Tetrahedralizations". In Proceeding of the 14th Inter- // -// 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. // -// // -// The mesh data structure of TetGen is a combination of two types of mesh // -// data structures. The tetrahedron-based mesh data structure introduced // -// by Shewchuk is eligible for tetrahedralization algorithms. The triangle // -// -edge data structure developed by Muecke is adopted for representing // -// boundary elements: subfaces and subsegments. // -// // -// J. R. Shewchuk, "Delaunay Refinement Mesh Generation". PhD thesis, // -// Carnegie Mellon University, Pittsburgh, PA, 1997. // -// // -// E. P. Muecke, "Shapes and Implementations in Three-Dimensional // -// Geometry". PhD thesis, Univ. of Illinois, Urbana, Illinois, 1993. // -// // -// The research of mesh generation is definitly on the move. Many State-of- // -// the-art algorithms need implementing and evaluating. I heartily welcome // -// any new algorithm especially for generating quality conforming Delaunay // -// meshes and anisotropic conforming Delaunay meshes. // -// // -// TetGen is supported by the "pdelib" project of Weierstrass Institute for // -// Applied Analysis and Stochastics (WIAS) in Berlin. It is a collection // -// of software components for solving non-linear partial differential // -// equations including 2D and 3D mesh generators, sparse matrix solvers, // -// and scientific visualization tools, etc. For more information please // -// visit: http://www.wias-berlin.de/software/pdelib. // -// // -/////////////////////////////////////////////////////////////////////////////// - -/////////////////////////////////////////////////////////////////////////////// -// // -// tetgen.h // -// // -// Header file of the TetGen library. Also is the user-level header file. // -// // -/////////////////////////////////////////////////////////////////////////////// #ifndef tetgenH #define tetgenH +// To compile TetGen as a library instead of an executable program, define +// the TETLIBRARY symbol. + +// #define TETLIBRARY + +// Uncomment the following line to disable assert macros. These macros were +// inserted in the code where I hoped to catch bugs. They may slow down the +// speed of TetGen. + +// #define NDEBUG + +// TetGen default uses the double precision (64 bit) for a real number. +// Alternatively, one can use the single precision (32 bit) 'float' if the +// memory is limited. + +#define REAL double // #define REAL float + +// Maximum number of characters in a file name (including the null). + +#define FILENAMESIZE 1024 + +// Maximum number of chars in a line read from a file (including the null). + +#define INPUTLINESIZE 2048 + +// TetGen only uses the C standard library. + #include #include #include @@ -91,18 +54,15 @@ // 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++. +// They are defined in by the C99 Standard. However, Microsoft +// Visual C++ 2003 -- 2008 (Visual C++ 7.1 - 9) doesn't ship with this header +// file. In such case, we can define them by ourself. +// Update (learned from Stack Overflow): Visual Studio 2010 and Visual C++ 2010 +// Express both have stdint.h -// #define _MSC_VER - -// Define the _WIN64 symbol if you are running TetGen on Win64. - -// #define _WIN64 +// The following piece of code was provided by Steven Johnson (MIT). Define the +// symbol _MSC_VER if you are using Microsoft Visual C++. Moreover, define +// the _WIN64 symbol if you are running TetGen on Win64 systems. #ifdef _MSC_VER // Microsoft Visual C++ # ifdef _WIN64 @@ -116,105 +76,52 @@ # include #endif -// To compile TetGen as a library instead of an executable program, define -// the TETLIBRARY symbol. - -// #define TETLIBRARY - -// Uncomment the following line to disable assert macros. These macros are -// inserted in places where I hope to catch bugs. - -// #define NDEBUG - -// To insert lots of self-checks for internal errors, define the SELF_CHECK -// symbol. This will slow down the program a bit. - -// #define SELF_CHECK - -// For single precision ( which will save some memory and reduce paging ), -// define the symbol SINGLE by using the -DSINGLE compiler switch or by -// writing "#define SINGLE" below. -// -// For double precision ( which will allow you to refine meshes to a smaller -// edge length), leave SINGLE undefined. - -// #define SINGLE - -#ifdef SINGLE - #define REAL float -#else - #define REAL double -#endif // not defined SINGLE - /////////////////////////////////////////////////////////////////////////////// // // -// TetGen Library Overview // +// tetgenio // // // -// 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. // -// // -/////////////////////////////////////////////////////////////////////////////// - -/////////////////////////////////////////////////////////////////////////////// +// A structure for transferring data into and out of TetGen's mesh structure,// +// 'tetgenmesh' (declared below). // // // -// Class tetgenio // +// The input of TetGen is either a 3D point set, or a 3D piecewise linear // +// complex (PLC), or a tetrahedral mesh. Depending on the input object and // +// the specified options, the output of TetGen is either a Delaunay (or wei- // +// ghted Delaunay) tetrahedralization, or a constrained (Delaunay) tetrahed- // +// ralization, or a quality tetrahedral mesh. // // // -// The interface for passing data into and out of the library of TetGen. // +// A piecewise linear complex (PLC) represents a 3D polyhedral domain with // +// possibly internal boundaries(subdomains). It is introduced in [Miller et // +// al, 1996]. Basically it is a set of "cells", i.e., vertices, edges, poly- // +// gons, and polyhedra, and the intersection of any two of its cells is the // +// union of other cells of it. // // // -// The tetgenio data structure is actually a collection of arrays of points, // -// facets, tetrahedra, and so forth. The library will read and write these // -// arrays according to the options specified in tetgenbehavior structure. // +// TetGen uses a set of files to describe the inputs and outputs. Each file // +// is identified from its file extension (.node, .ele, .face, .edge, etc). // // // -// If you want to program with the library of TetGen, it's necessary for you // -// to understand this data type,while the other two structures can be hidden // -// through calling the global function "tetrahedralize()". Each array corre- // -// sponds to a list of data in the file formats of TetGen. It is necessary // -// to understand TetGen's input/output file formats (see user's manual). // +// The 'tetgenio' structure is a collection of arrays of data, i.e., points, // +// facets, tetrahedra, and so forth. It contains functions to read and write // +// (input and output) files of TetGen as well as other supported mesh files. // // // // Once an object of tetgenio is declared, no array is created. One has to // -// allocate enough memory for them, e.g., use the "new" operator in C++. On // -// deletion of the object, the memory occupied by these arrays needs to be // -// freed. Routine deinitialize() will be automatically called. It will de- // -// allocate the memory for an array if it is not a NULL. However, it assumes // -// that the memory is allocated by the C++ "new" operator. If you use malloc // -// (), 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. // +// allocate enough memory for them. On deletion of this object, the memory // +// occupied by these arrays needs to be freed. The routine deinitialize() // +// will be automatically called. It frees the memory for an array if it is // +// not a NULL. Note that it assumes that the memory is allocated by the C++ // +// "new" operator. Otherwise, the user is responsible to free them and all // +// pointers must be NULL before the call of the destructor. // // // /////////////////////////////////////////////////////////////////////////////// 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}; +public: - // 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 { + // A "polygon" describes a simple polygon (no holes). It is not necessarily + // convex. Each polygon contains a number of corners (points) and the same + // number of sides (edges). The points of the polygon must be given in + // either counterclockwise or clockwise order and they form a ring, so + // every two consecutive points forms an edge of the polygon. + struct polygon { int *vertexlist; int numberofvertices; @@ -222,14 +129,10 @@ class tetgenio { ~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 { + // A "facet" describes a polygonal region possibly with holes, edges, and + // points floating in it. Each facet consists of a list of polygons and + // a list of hole points (which lie strictly inside holes). + struct facet{ polygon *polygonlist; int numberofpolygons; REAL *holelist; @@ -239,14 +142,7 @@ class tetgenio { ~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 + // 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 @@ -258,7 +154,7 @@ class tetgenio { REAL vnormal[3]; } voroedge; - // A 'vorofacet' is an facet of the Voronoi diagram. It corresponds to a + // 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 @@ -270,22 +166,21 @@ class tetgenio { 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(); - }; + + // Additional parameters associated with an input (or mesh) vertex. + // These informations are provided by CAD libraries. + typedef struct { + REAL uv[2]; + int tag; + int type; // 0, 1, or 2. + } pointparam; + + // Callback functions for meshing PSCs. + typedef REAL (* GetVertexParamOnEdge)(void*, int, int); + typedef void (* GetSteinerOnEdge)(void*, int, REAL, REAL*); + typedef void (* GetVertexParamOnFace)(void*, int, int, REAL*); + typedef void (* GetEdgeSteinerParamOnFace)(void*, int, REAL, int, REAL*); + typedef void (* GetSteinerOnFace)(void*, int, REAL*, REAL*); // A callback function for mesh refinement. typedef bool (* TetSizeFunc)(REAL*, REAL*, REAL*, REAL*, REAL*, REAL); @@ -296,8 +191,8 @@ class tetgenio { // 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; + // Does the lines in .node file contain index or not, default is 1. + int 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 @@ -307,94 +202,108 @@ class tetgenio { // 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. + // 'pointmarkerlist': An array of point markers; one integer per point. REAL *pointlist; REAL *pointattributelist; REAL *pointmtrlist; - int *pointmarkerlist; + int *pointmarkerlist; + pointparam *pointparamlist; int numberofpoints; int numberofpointattributes; int numberofpointmtrs; - // `elementlist': An array of element (triangle or tetrahedron) corners. - // The first element's first corner is at index [0], followed by its - // other corners in counterclockwise order, followed by any other - // nodes if the element represents a nonlinear element. Each element - // occupies `numberofcorners' ints. - // `elementattributelist': An array of element attributes. Each - // element's attributes occupy `numberofelementattributes' REALs. - // `elementconstraintlist': An array of constraints, i.e. triangle's - // area or tetrahedron's volume; one REAL per element. Input only. - // `neighborlist': An array of element neighbors; 3 or 4 ints per - // element. Output only. - int *tetrahedronlist; + // 'tetrahedronlist': An array of tetrahedron corners. The first + // tetrahedron's first corner is at index [0], followed by its other + // corners, followed by six nodes on the edges of the tetrahedron if the + // second order option (-o2) is applied. Each tetrahedron occupies + // 'numberofcorners' ints. The second order nodes are ouput only. + // 'tetrahedronattributelist': An array of tetrahedron attributes. Each + // tetrahedron's attributes occupy 'numberoftetrahedronattributes' REALs. + // 'tetrahedronvolumelist': An array of constraints, i.e. tetrahedron's + // volume; one REAL per element. Input only. + // 'neighborlist': An array of tetrahedron neighbors; 4 ints per element. + // Output only. + int *tetrahedronlist; REAL *tetrahedronattributelist; REAL *tetrahedronvolumelist; - int *neighborlist; + int *neighborlist; int numberoftetrahedra; int numberofcorners; int numberoftetrahedronattributes; - // `facetlist': An array of facets. Each entry is a structure of facet. - // `facetmarkerlist': An array of facet markers; one int per facet. + // 'facetlist': An array of facets. Each entry is a structure of facet. + // 'facetmarkerlist': An array of facet markers; one int per facet. facet *facetlist; int *facetmarkerlist; int numberoffacets; - // `holelist': An array of holes. The first hole's x, y and z - // coordinates are at indices [0], [1] and [2], followed by the - // remaining holes. Three REALs per hole. + // 'holelist': An array of holes (in volume). Each hole is given by a + // seed (point) which lies strictly inside it. The first seed's x, y and z + // coordinates are at indices [0], [1] and [2], followed by the + // remaining seeds. Three REALs per hole. REAL *holelist; int numberofholes; - // `regionlist': An array of regional attributes and volume constraints. - // The first constraint's x, y and z coordinates are at indices [0], - // [1] and [2], followed by the regional attribute at index [3], foll- - // owed by the maximum volume at index [4]. Five REALs per constraint. - // Note that each regional attribute is used only if you select the `A' + // 'regionlist': An array of regions (subdomains). Each region is given by + // a seed (point) which lies strictly inside it. The first seed's x, y and + // z coordinates are at indices [0], [1] and [2], followed by the regional + // attribute at index [3], followed by the maximum volume at index [4]. + // Five REALs per region. + // Note that each regional attribute is used only if you select the 'A' // switch, and each volume constraint is used only if you select the - // `a' switch (with no number following). + // 'a' switch (with no number following). REAL *regionlist; int numberofregions; - // `facetconstraintlist': An array of facet maximal area constraints. - // Two REALs per constraint. The first (at index [0]) is the facet - // marker (cast it to int), the second (at index [1]) is its maximum - // area bound. + // 'facetconstraintlist': An array of facet constraints. Each constraint + // specifies a maximum area bound on the subfaces of that facet. The + // first facet constraint is given by a facet marker at index [0] and its + // maximum area bound at index [1], followed by the remaining facet con- + // straints. Two REALs per facet constraint. Note: the facet marker is + // actually an integer. REAL *facetconstraintlist; int numberoffacetconstraints; - // `segmentconstraintlist': An array of segment max. length constraints. - // Three REALs per constraint. The first two (at indcies [0] and [1]) - // are the indices of the endpoints of the segment, the third (at index - // [2]) is its maximum length bound. + // 'segmentconstraintlist': An array of segment constraints. Each constraint + // specifies a maximum length bound on the subsegments of that segment. + // The first constraint is given by the two endpoints of the segment at + // index [0] and [1], and the maximum length bound at index [2], followed + // by the remaining segment constraints. Three REALs per constraint. + // Note the segment endpoints are actually integers. REAL *segmentconstraintlist; int numberofsegmentconstraints; - // 'pbcgrouplist': An array of periodic boundary condition groups. - pbcgroup *pbcgrouplist; - int numberofpbcgroups; - - // `trifacelist': An array of triangular face endpoints. The first - // face's endpoints are at indices [0], [1] and [2], followed by the - // remaining faces. Three ints per face. - // `adjtetlist': An array of adjacent tetrahedra to the faces of - // trifacelist. Each face has at most two adjacent tets, the first - // face's adjacent tets are at [0], [1]. Two ints per face. A '-1' - // indicates outside (no adj. tet). This list is output when '-nn' - // switch is used. - // `trifacemarkerlist': An array of face markers; one int per face. + + // 'trifacelist': An array of face (triangle) corners. The first face's + // three corners are at indices [0], [1] and [2], followed by the remaining + // faces. Three ints per face. + // 'trifacemarkerlist': An array of face markers; one int per face. + // 'o2facelist': An array of second order nodes (on the edges) of the face. + // It is output only if the second order option (-o2) is applied. The + // first face's three second order nodes are at [0], [1], and [2], + // followed by the remaining faces. Three ints per face. + // 'adjtetlist': An array of adjacent tetrahedra to the faces. The first + // face's two adjacent tetrahedra are at indices [0] and [1], followed by + // the remaining faces. A '-1' indicates outside (no adj. tet). This list + // is output when '-nn' switch is used. Output only. int *trifacelist; - int *adjtetlist; int *trifacemarkerlist; + int *o2facelist; + int *adjtetlist; int numberoftrifaces; - // `edgelist': An array of edge endpoints. The first edge's endpoints - // are at indices [0] and [1], followed by the remaining edges. Two - // ints per edge. - // `edgemarkerlist': An array of edge markers; one int per edge. + // 'edgelist': An array of edge endpoints. The first edge's endpoints + // are at indices [0] and [1], followed by the remaining edges. + // Two ints per edge. + // 'edgemarkerlist': An array of edge markers; one int per edge. + // 'o2edgelist': An array of midpoints of edges. It is output only if the + // second order option (-o2) is applied. One int per edge. + // 'edgeadjtetlist': An array of adjacent tetrahedra to the edges. One + // tetrahedron (an integer) per edge. int *edgelist; int *edgemarkerlist; + int *o2edgelist; + int *edgeadjtetlist; int numberofedges; // 'vpointlist': An array of Voronoi vertex coordinates (like pointlist). @@ -412,29 +321,42 @@ class tetgenio { int numberofvfacets; int numberofvcells; + // Variable (and callback functions) for meshing PSCs. + void *geomhandle; + GetVertexParamOnEdge getvertexparamonedge; + GetSteinerOnEdge getsteineronedge; + GetVertexParamOnFace getvertexparamonface; + GetEdgeSteinerParamOnFace getedgesteinerparamonface; + GetSteinerOnFace getsteineronface; + // 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_node_call(FILE* infile, int markers, int uvflag, char*); + bool load_node(char*); + bool load_edge(char*); + bool load_face(char*); + bool load_tet(char*); + bool load_vol(char*); bool load_var(char*); bool load_mtr(char*); - bool load_poly(char*); bool load_pbc(char*); + bool load_poly(char*); bool load_off(char*); bool load_ply(char*); bool load_stl(char*); - bool load_medit(char*); bool load_vtk(char*); + bool load_medit(char*, int); bool load_plc(char*, int); - bool load_tetmesh(char*); + bool load_tetmesh(char*, int); void save_nodes(char*); void save_elements(char*); void save_faces(char*); void save_edges(char*); void save_neighbors(char*); void save_poly(char*); + void save_faces2smesh(char*); // Read line and parse string functions. char *readline(char* string, FILE* infile, int *linenumber); @@ -442,17 +364,30 @@ class tetgenio { char *readnumberline(char* string, FILE* infile, char* infilename); char *findnextnumber(char* string); + static void init(polygon* p) { + p->vertexlist = (int *) NULL; + p->numberofvertices = 0; + } + + static void init(facet* f) { + f->polygonlist = (polygon *) NULL; + f->numberofpolygons = 0; + f->holelist = (REAL *) NULL; + f->numberofholes = 0; + } + // Initialize routine. void initialize() { - firstnumber = 0; // Default item index is numbered from Zero. - mesh_dim = 3; // Default mesh dimension is 3. - useindex = true; + firstnumber = 0; + mesh_dim = 3; + useindex = 1; pointlist = (REAL *) NULL; pointattributelist = (REAL *) NULL; pointmtrlist = (REAL *) NULL; pointmarkerlist = (int *) NULL; + pointparamlist = (pointparam *) NULL; numberofpoints = 0; numberofpointattributes = 0; numberofpointmtrs = 0; @@ -462,22 +397,25 @@ class tetgenio { tetrahedronvolumelist = (REAL *) NULL; neighborlist = (int *) NULL; numberoftetrahedra = 0; - numberofcorners = 4; // Default is 4 nodes per element. + numberofcorners = 4; numberoftetrahedronattributes = 0; trifacelist = (int *) NULL; - adjtetlist = (int *) NULL; trifacemarkerlist = (int *) NULL; + o2facelist = (int *) NULL; + adjtetlist = (int *) NULL; numberoftrifaces = 0; - facetlist = (facet *) NULL; - facetmarkerlist = (int *) NULL; - numberoffacets = 0; - edgelist = (int *) NULL; edgemarkerlist = (int *) NULL; + o2edgelist = (int *) NULL; + edgeadjtetlist = (int *) NULL; numberofedges = 0; + facetlist = (facet *) NULL; + facetmarkerlist = (int *) NULL; + numberoffacets = 0; + holelist = (REAL *) NULL; numberofholes = 0; @@ -489,8 +427,6 @@ class tetgenio { segmentconstraintlist = (REAL *) NULL; numberofsegmentconstraints = 0; - pbcgrouplist = (pbcgroup *) NULL; - numberofpbcgroups = 0; vpointlist = (REAL *) NULL; vedgelist = (voroedge *) NULL; @@ -502,19 +438,21 @@ class tetgenio { numberofvcells = 0; tetunsuitable = NULL; + + geomhandle = NULL; + getvertexparamonedge = NULL; + getsteineronedge = NULL; + getvertexparamonface = NULL; + getedgesteinerparamonface = NULL; + getsteineronface = NULL; } - // Free the memory allocated in 'tetgenio'. + // Free the memory allocated in 'tetgenio'. Note that it assumes that the + // memory was allocated by the "new" operator (C++). 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; } @@ -527,6 +465,9 @@ class tetgenio { if (pointmarkerlist != (int *) NULL) { delete [] pointmarkerlist; } + if (pointparamlist != (pointparam *) NULL) { + delete [] pointparamlist; + } if (tetrahedronlist != (int *) NULL) { delete [] tetrahedronlist; @@ -544,12 +485,15 @@ class tetgenio { if (trifacelist != (int *) NULL) { delete [] trifacelist; } - if (adjtetlist != (int *) NULL) { - delete [] adjtetlist; - } if (trifacemarkerlist != (int *) NULL) { delete [] trifacemarkerlist; } + if (o2facelist != (int *) NULL) { + delete [] o2facelist; + } + if (adjtetlist != (int *) NULL) { + delete [] adjtetlist; + } if (edgelist != (int *) NULL) { delete [] edgelist; @@ -557,6 +501,12 @@ class tetgenio { if (edgemarkerlist != (int *) NULL) { delete [] edgemarkerlist; } + if (o2edgelist != (int *) NULL) { + delete [] o2edgelist; + } + if (edgeadjtetlist != (int *) NULL) { + delete [] edgeadjtetlist; + } if (facetlist != (facet *) NULL) { delete [] facetlist; @@ -577,9 +527,6 @@ class tetgenio { if (segmentconstraintlist != (REAL *) NULL) { delete [] segmentconstraintlist; } - if (pbcgrouplist != (pbcgroup *) NULL) { - delete [] pbcgrouplist; - } if (vpointlist != (REAL *) NULL) { delete [] vpointlist; } @@ -603,105 +550,125 @@ class tetgenio { // Constructor & destructor. tetgenio() {initialize();} ~tetgenio() {deinitialize();} -}; + +}; // class tetgenio /////////////////////////////////////////////////////////////////////////////// // // -// Class tetgenbehavior // +// tetgenbehavior // +// // +// A structure for maintaining the switches and parameters used by TetGen's // +// mesh data structure and algorithms. // // // -// The object holding a collection of options controlling TetGen's behavior. // -// See "command line switches" in User's manual. // +// All switches and parameters are initialized with default values. They can // +// be set by the command line arguments (a list of strings) of TetGen. // // // -// 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. // +// NOTE: Some of the switches are incompatible. While some may depend on // +// other switches. The routine parse_commandline() sets the switches from // +// the command line (a list of strings) and checks the consistency of the // +// applied switches. // // // /////////////////////////////////////////////////////////////////////////////// class tetgenbehavior { - public: +public: + + // Switches of TetGen. + int plc; // '-p', 0. + int psc; // '-s', 0. + int refine; // '-r', 0. + int quality; // '-q', 0. + int nobisect; // '-Y', 0. + int coarsen; // '-R', 0. + int weighted; // '-w', 0. + int brio_hilbert; // '-b', 1. + int incrflip; // '-l', 0. + int flipinsert; // '-L', 0. + int metric; // '-m', 0. + int varvolume; // '-a', 0. + int fixedvolume; // '-a', 0. + int regionattrib; // '-A', 0. + int conforming; // '-D', 0. + int insertaddpoints; // '-i', 0. + int diagnose; // '-d', 0. + int convex; // '-c', 0. + int nomergefacet; // '-M', 0. + int nomergevertex; // '-M', 0. + int noexact; // '-X', 0. + int nostaticfilter; // '-X', 0. + int zeroindex; // '-z', 0. + int facesout; // '-f', 0. + int edgesout; // '-e', 0. + int neighout; // '-n', 0. + int voroout; // '-v', 0. + int meditview; // '-g', 0. + int vtkview; // '-k', 0. + int nobound; // '-B', 0. + int nonodewritten; // '-N', 0. + int noelewritten; // '-E', 0. + int nofacewritten; // '-F', 0. + int noiterationnum; // '-I', 0. + int nojettison; // '-J', 0. + int reversetetori; // '-R', 0. + int docheck; // '-C', 0. + int quiet; // '-Q', 0. + int verbose; // '-V', 0. + + // Parameters of TetGen. + int vertexperblock; // '-x', 4092. + int tetrahedraperblock; // '-x', 8188. + int shellfaceperblock; // '-x', 2044. + int nobisect_param; // '-Y', 2. + int addsteiner_algo; // '-Y/', 1. + int coarsen_param; // '-R', 0. + int weighted_param; // '-w', 0. + int fliplinklevel; // -1. + int flipstarsize; // -1. + int fliplinklevelinc; // 1. + int reflevel; // '-D', 3. + int optlevel; // '-O', 2. + int optscheme; // '-O', 7. + int delmaxfliplevel; // 1. + int order; // '-o', 1. + int steinerleft; // '-S', 0. + int no_sort; // 0. + int hilbert_order; // '-b///', 52. + int hilbert_limit; // '-b//' 8. + int brio_threshold; // '-b' 64. + REAL brio_ratio; // '-b/' 0.125. + REAL facet_ang_tol; // '-p', 179.9. + REAL maxvolume; // '-a', -1.0. + REAL minratio; // '-q', 0.0. + REAL mindihedral; // '-q', 5.0. + REAL optmaxdihedral; // 165.0. + REAL optminsmtdihed; // 179.0. + REAL optminslidihed; // 179.0. + REAL epsilon; // '-T', 1.0e-8. + REAL minedgelength; // 0.0. + REAL coarsen_percent; // -R1/#, 1.0. + + // Strings of command line arguments and input/output file names. + char commandline[1024]; + char infilename[1024]; + char outfilename[1024]; + char addinfilename[1024]; + char bgmeshfilename[1024]; - // Labels define the objects which are acceptable by TetGen. They are - // recognized by the file extensions. + // The input object of TetGen. They are recognized by either the input + // file extensions or by the specified options. + // Currently the following objects are supported: // - NODES, a list of nodes (.node); // - POLY, a piecewise linear complex (.poly or .smesh); // - OFF, a polyhedron (.off, Geomview's file format); - // - PLY, a polyhedron (.ply, file format from gatech); + // - PLY, a polyhedron (.ply, file format from gatech, only ASCII); // - STL, a surface mesh (.stl, stereolithography format); // - MEDIT, a surface mesh (.mesh, Medit's file format); // - MESH, a tetrahedral mesh (.ele). - // If no extension is available, the imposed commandline switch + // If no extension is available, the imposed command line switch // (-p or -r) implies the object. + enum objecttype {NODES, POLY, OFF, PLY, STL, MEDIT, VTK, MESH} 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(); @@ -716,547 +683,729 @@ class tetgenbehavior { tetgenbehavior() { plc = 0; - quality = 0; + psc = 0; refine = 0; - coarse = 0; + quality = 0; + nobisect = 0; + coarsen = 0; metric = 0; - minratio = 2.0; - goodratio = 0.0; - minangle = 20.0; - goodangle = 0.0; - maxdihedral = 165.0; - mindihedral = 5.0; + weighted = 0; + brio_hilbert = 1; + incrflip = 0; + flipinsert = 0; varvolume = 0; fixedvolume = 0; - maxvolume = -1.0; - regionattrib = 0; + noexact = 0; + nostaticfilter = 0; insertaddpoints = 0; + regionattrib = 0; + conforming = 0; diagnose = 0; - offcenter = 0; - conformdel = 0; - alpha1 = sqrt(2.0); - alpha2 = 1.0; - alpha3 = 0.6; + convex = 0; 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; + nomergefacet = 0; + nomergevertex = 0; + nojettison = 0; + reversetetori = 0; docheck = 0; quiet = 0; verbose = 0; - useshelles = 0; - maxflipedgelinksize = 10; + + vertexperblock = 4092; + tetrahedraperblock = 8188; + shellfaceperblock = 4092; + nobisect_param = 2; + addsteiner_algo = 1; + coarsen_param = 0; + weighted_param = 0; + fliplinklevel = -1; // No limit on linklevel. + flipstarsize = -1; // No limit on flip star size. + fliplinklevelinc = 1; + reflevel = 3; + optscheme = 7; // 1 & 2 & 4, // min_max_dihedral. + optlevel = 2; + delmaxfliplevel = 1; + order = 1; + steinerleft = -1; + no_sort = 0; + hilbert_order = 52; //-1; + hilbert_limit = 8; + brio_threshold = 64; + brio_ratio = 0.125; + facet_ang_tol = 179.9; + maxvolume = -1.0; + minratio = 2.0; + mindihedral = 0.0; // 5.0; + optmaxdihedral = 165.00; // without -q, default is 179.0 + optminsmtdihed = 179.00; // without -q, default is 179.999 + optminslidihed = 179.00; // without -q, default is 179.999 epsilon = 1.0e-8; - epsilon2 = 1.0e-5; - object = NONE; + minedgelength = 0.0; + coarsen_percent = 1.0; + object = NODES; commandline[0] = '\0'; infilename[0] = '\0'; outfilename[0] = '\0'; addinfilename[0] = '\0'; bgmeshfilename[0] = '\0'; + } - - ~tetgenbehavior() - { - } -}; + +}; // class tetgenbehavior /////////////////////////////////////////////////////////////////////////////// // // -// Class tetgenmesh // +// Robust Geometric predicates // // // -// The object to store, generate, and refine a tetrahedral mesh. // +// Geometric predicates are simple tests of spatial relations of a set of d- // +// dimensional points, such as the orientation test and the point-in-sphere // +// test. Each of these tests is performed by evaluating the sign of a deter- // +// minant of a matrix whose entries are the coordinates of these points. If // +// the computation is performed by using the floating-point numbers, e.g., // +// the single or double precision numbers in C/C++, roundoff error may cause // +// an incorrect result. This may either lead to a wrong result or eventually // +// lead to a failure of the program. Computing the predicates exactly will // +// avoid the error and make the program robust. // // // -// It implements the mesh data structures and functions to create and update // -// a tetrahedral mesh according to the specified options. // +// The following routines are the robust geometric predicates for 3D orient- // +// ation test and point-in-sphere test. They were implemented by Shewchuk. // +// The source code are generously provided by him in the public domain, // +// http://www.cs.cmu.edu/~quake/robust.html. predicates.cxx is a C++ version // +// of the original C code. // +// // +// The original predicates of Shewchuk only use "dynamic filters", i.e., it // +// computes the error at run time step by step. TetGen first adds a "static // +// filter" in each predicate. It estimates the maximal possible error in all // +// cases. So it can safely and quickly answer many easy cases. // // // /////////////////////////////////////////////////////////////////////////////// -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}; +void exactinit(int, int, int, REAL, REAL, REAL); +REAL orient3d(REAL *pa, REAL *pb, REAL *pc, REAL *pd); +REAL insphere(REAL *pa, REAL *pb, REAL *pc, REAL *pd, REAL *pe); +REAL orient4d(REAL *pa, REAL *pb, REAL *pc, REAL *pd, REAL *pe, + REAL ah, REAL bh, REAL ch, REAL dh, REAL eh); - // Labels that signify whether a record consists primarily of pointers - // or of floating-point words. Used for data alignment. - enum wordtype {POINTER, FLOATINGPOINT}; +/////////////////////////////////////////////////////////////////////////////// +// // +// tetgenmesh // +// // +// A structure for creating and updating tetrahedral meshes. // +// // +/////////////////////////////////////////////////////////////////////////////// - // Labels that signify the type of a vertex. - enum verttype {UNUSEDVERTEX, DUPLICATEDVERTEX, NACUTEVERTEX, ACUTEVERTEX, - FREESEGVERTEX, FREESUBVERTEX, FREEVOLVERTEX, DEADVERTEX = -32768}; - - // Labels that signify the type of a subface/subsegment. - enum shestype {NSHARP, SHARP}; +class tetgenmesh { - // Labels that signify the type of flips can be applied on a face. - enum fliptype {T23, T32, T22, T44, N32, N40, FORBIDDENFACE, FORBIDDENEDGE}; +public: - // 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}; +/////////////////////////////////////////////////////////////////////////////// +// // +// Mesh data structure // +// // +// A tetrahedral mesh T of a 3D piecewise linear complex (PLC) X is a 3D // +// simplicial complex whose underlying space is equal to the space of X. T // +// contains a 2D subcomplex S which is a triangular mesh of the boundary of // +// X. S contains a 1D subcomplex L which is a linear mesh of the boundary of // +// S. Faces and edges in S and L are respectively called subfaces and segme- // +// nts to distinguish them from others in T. // +// // +// TetGen stores the tetrahedra and vertices of T. The basic structure of a // +// tetrahedron contains pointers to its vertices and adjacent tetrahedra. A // +// vertex stores its x-, y-, and z-coordinates, and a pointer to a tetrahed- // +// ron containing it. Both tetrahedra and vertices may contain user data. // +// // +// Each face of T belongs to either two tetrahedra or one tetrahedron. In // +// the latter case, the face is an exterior boundary face of T. TetGen adds // +// fictitious tetrahedra (one-to-one) at such faces, and connects them to an // +// "infinite vertex" (which has no geometric coordinates). One can imagine // +// such a vertex lies in 4D space and is visible by all exterior boundary // +// faces. The extended set of tetrahedra (including the infinite vertex) is // +// a tetrahedralization of a 3-pseudomanifold without boundary. It has the // +// property that every face is shared by exactly two tetrahedra. // +// // +// The current version of TetGen stores explicitly the subfaces and segments // +// (which are in surface mesh S and the linear mesh L), respectively. Extra // +// pointers are allocated in tetrahedra and subfaces to point each others. // +// // +/////////////////////////////////////////////////////////////////////////////// - // Labels that signify the result of point location. - enum locateresult {INTETRAHEDRON, ONFACE, ONEDGE, ONVERTEX, OUTSIDE, - ENCSEGMENT}; - - // Labels that signify the result of vertex insertion. - enum insertsiteresult {SUCCESSINTET, SUCCESSONFACE, SUCCESSONEDGE, - DUPLICATEPOINT, OUTSIDEPOINT}; - - // Labels that signify the result of direction finding. - enum finddirectionresult {ACROSSEDGE, ACROSSFACE, LEFTCOLLINEAR, - RIGHTCOLLINEAR, TOPCOLLINEAR, BELOWHULL}; - -/////////////////////////////////////////////////////////////////////////////// -// // -// Mesh elements // -// // -// There are four types of mesh elements: tetrahedra, subfaces, subsegments, // -// and points, where subfaces and subsegments are triangles and edges which // -// appear on boundaries. A tetrahedralization of a 3D point set comprises // -// tetrahedra and points; a surface mesh of a 3D domain comprises subfaces // -// subsegments and points. The elements of all the four types consist of a // -// tetrahedral mesh of a 3D domain. However, TetGen uses three data types: // -// 'tetrahedron', 'shellface', and 'point'. A 'tetrahedron' is a tetrahedron;// -// while a 'shellface' can be either a subface or a subsegment; and a 'point'// -// is a point. These three data types, linked by pointers comprise a mesh. // -// // -// A tetrahedron primarily consists of a list of 4 pointers to its corners, // -// a list of 4 pointers to its adjoining tetrahedra, a list of 4 pointers to // -// its adjoining subfaces (when subfaces are needed). Optinoally, (depending // -// 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. // -// // -// 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 // -// to the order of their storage in t. v3 always has a negative orientation // -// with respect to v0, v1, v2 (ie,, v3 lies above the oriented plane passes // -// through v0, v1, v2). Let the 4 faces of t be f0, f1, f2, and f3. Vertices // -// of each face are stipulated as follows: f0 (v0, v1, v2), f1 (v0, v3, v1), // -// f2 (v1, v3, v2), f3 (v2, v3, v0). // -// // -// A subface has 3 pointers to vertices, 3 pointers to adjoining subfaces, 3 // -// pointers to adjoining subsegments, 2 pointers to adjoining tetrahedra, a // -// boundary marker(an integer). Like a tetrahedron, the pointers to vertices,// -// subfaces, and subsegments are ordered in a way that indicates their geom- // -// etric relation. Let s be a subface, v0, v1 and v2 be the 3 nodes corres- // -// ponding to the order of their storage in s, e0, e1 and e2 be the 3 edges,// -// then we have: e0 (v0, v1), e1 (v1, v2), e2 (v2, v0). // -// // -// A subsegment has exactly the same data fields as a subface has, but only // -// uses some of them. It has 2 pointers to its endpoints, 2 pointers to its // -// adjoining (and collinear) subsegments, a pointer to a subface containing // -// it (there may exist any number of subfaces having it, choose one of them // -// arbitrarily). The geometric relation between its endpoints and adjoining // -// subsegments is kept with respect to the storing order of its endpoints. // -// // -// The data structure of point is relatively simple. A point is a list of // -// floating-point numbers, starting with the x, y, and z coords, followed by // -// an arbitrary number of optional user-defined floating-point attributes, // -// an integer boundary marker, an integer for the point type, and a pointer // -// to a tetrahedron (used for speeding up point location). // -// // -// For a tetrahedron on a boundary (or a hull) of the mesh, some or all of // -// the adjoining tetrahedra may not be present. For an interior tetrahedron, // -// often no neighboring subfaces are present, Such absent tetrahedra and // -// subfaces are never represented by the NULL pointers; they are represented // -// by two special records: `dummytet', the tetrahedron fills "outer space", // -// and `dummysh', the vacuous subfaces which are omnipresent. // -// // -// Tetrahedra and adjoining subfaces are glued together through the pointers // -// saved in each data fields of them. Subfaces and adjoining subsegments are // -// connected in the same fashion. However, there are no pointers directly // -// gluing tetrahedra and adjoining subsegments. For the purpose of saving // -// space, the connections between tetrahedra and subsegments are entirely // -// mediated through subfaces. The following part explains how subfaces are // -// connected in TetGen. // -// // -/////////////////////////////////////////////////////////////////////////////// - -/////////////////////////////////////////////////////////////////////////////// -// // -// 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 // -// which is cyclic, e.g., one can start from any subface in it and traverse // -// back. When the edge is not a subsegment, the ring only has two coplanar // -// subfaces which are pointing to each other. Otherwise, the face ring may // -// have any number of subfaces (and are not all coplanar). // -// // -// How is the face ring formed? Let s be a subsegment, f is one of subfaces // -// containing s as an edge. The direction of s is stipulated from its first // -// endpoint to its second (according to their storage in s). Once the dir of // -// s is determined, the other two edges of f are oriented to follow this dir.// -// The "directional normal" N_f is a vector formed from any point in f and a // -// points orthogonally above f. // -// // -// The face ring of s is a cyclic ordered set of subfaces containing s, i.e.,// -// F(s) = {f1, f2, ..., fn}, n >= 1. Where the order is defined as follows: // -// let fi, fj be two faces in F(s), the "normal-angle", NAngle(i,j) (range // -// from 0 to 360 degree) is the angle between the N_fi and N_fj; then fi is // -// in front of fj (or symbolically, fi < fj) if there exists another fk in // -// F(s), and NAangle(k, i) < NAngle(k, j). The face ring of s is: f1 < f2 < // -// ... < fn < f1. // -// // -// The easiest way to imagine how a face ring is formed is to use the right- // -// hand rule. Make a fist using your right hand with the thumb pointing to // -// the direction of the subsegment. The face ring is connected following the // -// direction of your fingers. // -// // -// The subface and subsegment are also connected through pointers stored in // -// their own data fields. Every subface has a pointer to its adjoining sub- // -// segment. However, a subsegment only has one pointer to a subface which is // -// containing it. Such subface can be chosen arbitrarily, other subfaces are // -// found through the face ring. // -// // -/////////////////////////////////////////////////////////////////////////////// - - // The tetrahedron data structure. Fields of a tetrahedron contains: + // The tetrahedron data structure. It includes the following fields: // - a list of four adjoining tetrahedra; // - a list of four vertices; - // - a list of four subfaces (optional, used for -p switch); + // - a pointer to a list of four subfaces (optional, for -p switch); + // - a pointer to a list of six segments (optional, for -p switch); // - a list of user-defined floating-point attributes (optional); - // - a volume constraint (optional, 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); + // - a volume constraint (optional, for -a switch); + // - an integer of element marker (and flags); + // The structure of a tetrahedron is an array of pointers. Its actual size + // (the length of the array) is determined at runtime. typedef REAL **tetrahedron; - // The shellface data structure. Fields of a shellface contains: + // The subface data structure. It includes the following fields: // - 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); + // - a list of three adjoining segments; + // - two adjoining tetrahedra; + // - an area constraint (optional, for -q switch); // - an integer for boundary marker; - // - an integer for type: SHARPSEGMENT, NONSHARPSEGMENT, ...; - // - an integer for pbc group (optional, if in->pbcgrouplist exists); + // - an integer for type, flags, etc. typedef REAL **shellface; - // The point data structure. It is actually an array of REALs: + // The point data structure. It includes the following fields: // - 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, ...; + // - u, v coordinates (optional, for -s switch); + // - a metric tensor (optional, for -q or -m switch); + // - a pointer to an adjacent tetrahedron; + // - a pointer to a parent (or a duplicate) point; + // - a pointer to an adjacent subface or segment (optional, -p switch); + // - a pointer to a tet in background mesh (optional, for -m switch); + // - an integer for boundary marker (point index); + // - an integer for point type (and flags). + // - an integer for geometry tag (optional, for -s switch). + // The structure of a point is an array of REALs. Its acutal size is + // determined at the runtime. typedef REAL *point; /////////////////////////////////////////////////////////////////////////////// // // -// Mesh handles // +// Handles // +// // +// Navigation and manipulation in a tetrahedralization are accomplished by // +// operating on structures referred as ``handles". A handle is a pair (t,v), // +// where t is a pointer to a tetrahedron, and v is a 4-bit integer, in the // +// range from 0 to 11. v is called the ``version'' of a tetrahedron, it rep- // +// resents a directed edge of a specific face of the tetrahedron. // // // -// Two special data types, 'triface' and 'face' are defined for maintaining // -// and updating meshes. They are like pointers (or handles), which allow you // -// to hold one particular part of the mesh, i.e., a tetrahedron, a triangle, // -// an edge and a vertex. However, these data types do not themselves store // -// any part of the mesh. The mesh is made of the data types defined above. // +// There are 12 even permutations of the four vertices, each of them corres- // +// ponds to a directed edge (a version) of the tetrahedron. The 12 versions // +// can be grouped into 4 distinct ``edge rings'' in 4 ``oriented faces'' of // +// this tetrahedron. One can encode each version (a directed edge) into a // +// 4-bit integer such that the two upper bits encode the index (from 0 to 2) // +// of this edge in the edge ring, and the two lower bits encode the index ( // +// from 0 to 3) of the oriented face which contains this edge. // // // -// Muecke's "triangle-edge" data structure is the prototype for these data // -// types. It allows a universal representation for every tetrahedron, // -// triangle, edge and vertex. For understanding the following descriptions // -// of these handle data structures, readers are required to read both the // -// introduction and implementation detail of "triangle-edge" data structure // -// in Muecke's thesis. // +// The four vertices of a tetrahedron are indexed from 0 to 3 (according to // +// their storage in the data structure). Give each face the same index as // +// the node opposite it in the tetrahedron. Denote the edge connecting face // +// i to face j as i/j. We number the twelve versions as follows: // // // -// A 'triface' represents a face of a tetrahedron and an oriented edge of // -// the face simultaneously. It has a pointer 'tet' to a tetrahedron, an // -// integer 'loc' (range from 0 to 3) as the face index, and an integer 'ver' // -// (range from 0 to 5) as the edge version. A face of the tetrahedron can be // -// uniquly determined by the pair (tet, loc), and an oriented edge of this // -// face can be uniquly determined by the triple (tet, loc, ver). Therefore, // -// different usages of one triface are possible. If we only use the pair // -// (tet, loc), it refers to a face, and if we add the 'ver' additionally to // -// the pair, it is an oriented edge of this face. // +// | edge 0 edge 1 edge 2 // +// --------|-------------------------------- // +// face 0 | 0 (0/1) 4 (0/3) 8 (0/2) // +// face 1 | 1 (1/2) 5 (1/3) 9 (1/0) // +// face 2 | 2 (2/3) 6 (2/1) 10 (2/0) // +// face 3 | 3 (3/0) 7 (3/1) 11 (3/2) // // // -// A 'face' represents a subface and an oriented edge of it simultaneously. // -// It has a pointer 'sh' to a subface, an integer 'shver'(range from 0 to 5) // -// as the edge version. The pair (sh, shver) determines a unique oriented // -// edge of this subface. A 'face' is also used to represent a subsegment, // -// in this case, 'sh' points to the subsegment, and 'shver' indicates the // -// one of two orientations of this subsegment, hence, it only can be 0 or 1. // +// Similarly, navigation and manipulation in a (boundary) triangulation are // +// done by using handles of triangles. Each handle is a pair (s, v), where s // +// is a pointer to a triangle, and v is a version in the range from 0 to 5. // +// Each version corresponds to a directed edge of this triangle. // // // -// Mesh navigation and updating are accomplished through a set of mesh // -// manipulation primitives which operate on trifaces and faces. They are // -// introduced below. // +// Number the three vertices of a triangle from 0 to 2 (according to their // +// storage in the data structure). Give each edge the same index as the node // +// opposite it in the triangle. The six versions of a triangle are: // +// // +// | edge 0 edge 1 edge 2 // +// ---------------|-------------------------- // +// ccw orieation | 0 2 4 // +// cw orieation | 1 3 5 // +// // +// In the following, a 'triface' is a handle of tetrahedron, and a 'face' is // +// a handle of a triangle. // // // /////////////////////////////////////////////////////////////////////////////// class triface { - - public: - - tetrahedron* tet; - int loc, ver; - - // Constructors; - triface() : tet(0), loc(0), ver(0) {} - // Operators; + public: + tetrahedron *tet; + int ver; // Range from 0 to 11. + triface() : tet(0), ver(0) {} triface& operator=(const triface& t) { - tet = t.tet; loc = t.loc; ver = t.ver; + tet = t.tet; 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 { - - public: - + public: shellface *sh; - int shver; - - // Constructors; + int shver; // Range from 0 to 5. 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);} }; /////////////////////////////////////////////////////////////////////////////// // // -// The badface structure // -// // -// A multiple usages structure. Despite of its name, a 'badface' can be used // -// to represent the following objects: // -// - a face of a tetrahedron which is (possibly) non-Delaunay; // -// - an encroached subsegment or subface; // -// - a bad-quality tetrahedron, i.e, has too large radius-edge ratio; // -// - a sliver, i.e., has good radius-edge ratio but nearly zero volume; // -// - a degenerate tetrahedron (see routine checkdegetet()). // -// - a recently flipped face (saved for undoing the flip later). // -// // -// It has the following fields: 'tt' holds a tetrahedron; 'ss' holds a sub- // -// segment or subface; 'cent' is the circumcent of 'tt' or 'ss', 'key' is a // -// special value depending on the use, it can be either the square of the // -// radius-edge ratio of 'tt' or the flipped type of 'tt'; 'forg', 'fdest', // -// 'fapex', and 'foppo' are vertices saved for checking the object in 'tt' // -// or 'ss' is still the same when it was stored; 'noppo' is the fifth vertex // -// of a degenerate point set. 'previtem' and 'nextitem' implement a double // -// link for managing many basfaces. // +// Arraypool // // // -/////////////////////////////////////////////////////////////////////////////// - - struct badface { - triface tt; - face ss; - REAL key; - REAL cent[3]; - point forg, fdest, fapex, foppo; - point noppo; - struct badface *previtem, *nextitem; - }; - -/////////////////////////////////////////////////////////////////////////////// +// A dynamic linear array. (It is written by J. Shewchuk) // // // -// Elementary flip data structure // +// 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 // +// addresses 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. // // // -// A data structure to record three types of elementary flips, which are // -// 2-to-3, 3-to-2, and 2-to-2 flips. // +// '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 total memory in bytes. // // // /////////////////////////////////////////////////////////////////////////////// - class elemflip { + class arraypool { - public: + public: - enum fliptype ft; // ft \in {T23, T32, T22}. - point pset1[3]; - point pset2[3]; + int objectbytes; + int objectsperblock; + int log2objectsperblock; + int objectsperblockmark; + int toparraylen; + char **toparray; + long objects; + unsigned long totalmemory; - elemflip() { - ft = T23; // Default. - pset1[0] = pset1[1] = pset1[2] = (point) NULL; - pset2[0] = pset2[1] = pset2[2] = (point) NULL; - } + 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(); }; -/////////////////////////////////////////////////////////////////////////////// -// // -// The pbcdata structure // -// // -// A pbcdata stores data of a periodic boundary condition defined on a pair // -// of facets or segments. Let f1 and f2 define a pbcgroup. 'fmark' saves the // -// facet markers of f1 and f2; 'ss' contains two subfaces belong to f1 and // -// f2, respectively. Let s1 and s2 define a segment pbcgroup. 'segid' are // -// the segment ids of s1 and s2; 'ss' contains two segments belong to s1 and // -// s2, respectively. 'transmat' are two transformation matrices. transmat[0] // -// transforms a point of f1 (or s1) into a point of f2 (or s2), transmat[1] // -// does the inverse. // -// // -/////////////////////////////////////////////////////////////////////////////// +// 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(). - struct pbcdata { - int fmark[2]; - int segid[2]; - face ss[2]; - REAL transmat[2][4][4]; - }; +#define fastlookup(pool, index) \ + (void *) ((pool)->toparray[(index) >> (pool)->log2objectsperblock] + \ + ((index) & (pool)->objectsperblockmark) * (pool)->objectbytes) /////////////////////////////////////////////////////////////////////////////// // // -// Fast lookup tables for mesh manipulation primitives. // +// Memorypool // +// // +// A structure for memory allocation. (It is written by J. Shewchuk) // // // -// 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. // +// firstblock is the first block of items. nowblock is the block from which // +// items are currently being allocated. nextitem points to the next slab // +// of free memory for an item. deaditemstack is the head of a linked list // +// (stack) of deallocated items that can be recycled. unallocateditems is // +// the number of items that remain to be allocated from nowblock. // // // -// 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. // +// Traversal is the process of walking through the entire list of items, and // +// is separate from allocation. Note that a traversal will visit items on // +// the "deaditemstack" stack as well as live items. pathblock points to // +// the block currently being traversed. pathitem points to the next item // +// to be traversed. pathitemsleft is the number of items that remain to // +// be traversed in pathblock. // // // /////////////////////////////////////////////////////////////////////////////// - // 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]; + class memorypool { - // For oppo() primitives, uses 'loc' as the index. - static int loc2oppo[4]; + public: - // 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]; + void **firstblock, **nowblock; + void *nextitem; + void *deaditemstack; + void **pathblock; + void *pathitem; + int alignbytes; + int itembytes, itemwords; + int itemsperblock; + long items, maxitems; + int unallocateditems; + int pathitemsleft; - // The edge number (from 0 to 5) of a tet is defined as follows: - static int locver2edge[4][6]; - static int edge2locver[6][2]; + memorypool(); + memorypool(int, int, int, int); + ~memorypool(); + + void poolinit(int, int, int, int); + void restart(); + void *alloc(); + void dealloc(void*); + void traversalinit(); + void *traverse(); + }; - // 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]; +/////////////////////////////////////////////////////////////////////////////// +// // +// badface // +// // +// Despite of its name, a 'badface' can be used to represent one of the // +// following objects: // +// - a face of a tetrahedron which is (possibly) non-Delaunay; // +// - an encroached subsegment or subface; // +// - a bad-quality tetrahedron, i.e, has too large radius-edge ratio; // +// - a sliver, i.e., has good radius-edge ratio but nearly zero volume; // +// - a recently flipped face (saved for undoing the flip later). // +// // +/////////////////////////////////////////////////////////////////////////////// - // For enumerating three edges of a triangle. - static int plus1mod3[3]; - static int minus1mod3[3]; + class badface { + public: + triface tt; + face ss; + REAL key, cent[6]; // circumcenter or cos(dihedral angles) at 6 edges. + point forg, fdest, fapex, foppo, noppo; + badface *nextitem; + badface() : key(0), forg(0), fdest(0), fapex(0), foppo(0), noppo(0), + nextitem(0) {} + }; /////////////////////////////////////////////////////////////////////////////// // // -// Mesh manipulation primitives // +// insertvertexflags // +// // +// A collection of flags that pass to the routine insertvertex(). // +// // +/////////////////////////////////////////////////////////////////////////////// + + class insertvertexflags { + + public: + + int iloc; // input/output. + int bowywat, lawson; + int splitbdflag, validflag, respectbdflag; + int rejflag, chkencflag, cdtflag; + int assignmeshsize; + int sloc, sbowywat; + + // Used by Delaunay refinement. + int refineflag; // 0, 1, 2, 3 + triface refinetet; + face refinesh; + int smlenflag; // for useinsertradius. + REAL smlen; // for useinsertradius. + point parentpt; + + insertvertexflags() { + iloc = bowywat = lawson = 0; + splitbdflag = validflag = respectbdflag = 0; + rejflag = chkencflag = cdtflag = 0; + assignmeshsize = 0; + sloc = sbowywat = 0; + + refineflag = 0; + refinetet.tet = NULL; + refinesh.sh = NULL; + smlenflag = 0; + smlen = 0.0; + } + }; + +/////////////////////////////////////////////////////////////////////////////// +// // +// flipconstraints // +// // +// A structure of a collection of data (options and parameters) which pass // +// to the edge flip function flipnm(). // +// // +/////////////////////////////////////////////////////////////////////////////// + + class flipconstraints { + + public: + + // Elementary flip flags. + int enqflag; // (= flipflag) + int chkencflag; + + // Control flags + int unflip; // Undo the performed flips. + int collectnewtets; // Collect the new tets created by flips. + int collectencsegflag; + + // Optimization flags. + int remove_ndelaunay_edge; // Remove a non-Delaunay edge. + REAL bak_tetprism_vol; // The value to be minimized. + REAL tetprism_vol_sum; + int remove_large_angle; // Remove a large dihedral angle at edge. + REAL cosdihed_in; // The input cosine of the dihedral angle (> 0). + REAL cosdihed_out; // The improved cosine of the dihedral angle. + + // Boundary recovery flags. + int checkflipeligibility; + point seg[2]; // A constraining edge to be recovered. + point fac[3]; // A constraining face to be recovered. + point remvert; // A vertex to be removed. + + + flipconstraints() { + enqflag = 0; + chkencflag = 0; + + unflip = 0; + collectnewtets = 0; + collectencsegflag = 0; + + remove_ndelaunay_edge = 0; + bak_tetprism_vol = 0.0; + tetprism_vol_sum = 0.0; + remove_large_angle = 0; + cosdihed_in = 0.0; + cosdihed_out = 0.0; + + checkflipeligibility = 0; + seg[0] = NULL; + fac[0] = NULL; + remvert = NULL; + } + }; + +/////////////////////////////////////////////////////////////////////////////// // // -// 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. // +// optparameters // +// // +// Optimization options and parameters. // // // /////////////////////////////////////////////////////////////////////////////// + class optparameters { + + public: + + // The one of goals of optimization. + int max_min_volume; // Maximize the minimum volume. + int max_min_aspectratio; // Maximize the minimum aspect ratio. + int min_max_dihedangle; // Minimize the maximum dihedral angle. + + // The initial and improved value. + REAL initval, imprval; + + int numofsearchdirs; + REAL searchstep; + int maxiter; // Maximum smoothing iterations (disabled by -1). + int smthiter; // Performed iterations. + + + optparameters() { + max_min_volume = 0; + max_min_aspectratio = 0; + min_max_dihedangle = 0; + + initval = imprval = 0.0; + + numofsearchdirs = 10; + searchstep = 0.01; + maxiter = -1; // Unlimited smoothing iterations. + smthiter = 0; + + } + }; + + +/////////////////////////////////////////////////////////////////////////////// +// // +// Labels (enumeration declarations) used by TetGen. // +// // +/////////////////////////////////////////////////////////////////////////////// + + // Labels that signify the type of a vertex. + enum verttype {UNUSEDVERTEX, DUPLICATEDVERTEX, RIDGEVERTEX, ACUTEVERTEX, + FACETVERTEX, VOLVERTEX, FREESEGVERTEX, FREEFACETVERTEX, + FREEVOLVERTEX, NREGULARVERTEX, DEADVERTEX}; + + // Labels that signify the result of triangle-triangle intersection test. + enum interresult {DISJOINT, INTERSECT, SHAREVERT, SHAREEDGE, SHAREFACE, + TOUCHEDGE, TOUCHFACE, ACROSSVERT, ACROSSEDGE, ACROSSFACE, + COLLISIONFACE, ACROSSSEG, ACROSSSUB}; + + // Labels that signify the result of point location. + enum locateresult {UNKNOWN, OUTSIDE, INTETRAHEDRON, ONFACE, ONEDGE, ONVERTEX, + ENCVERTEX, ENCSEGMENT, ENCSUBFACE, NEARVERTEX, NONREGULAR, + INSTAR, BADELEMENT}; + +/////////////////////////////////////////////////////////////////////////////// +// // +// Variables of TetGen // +// // +/////////////////////////////////////////////////////////////////////////////// + + // Pointer to the input data (a set of nodes, a PLC, or a mesh). + tetgenio *in, *addin; + + // Pointer to the switches and parameters. + tetgenbehavior *b; + + // Pointer to a background mesh (contains size specification map). + tetgenmesh *bgm; + + // Memorypools to store mesh elements (points, tetrahedra, subfaces, and + // segments) and extra pointers between tetrahedra, subfaces, and segments. + memorypool *tetrahedrons, *subfaces, *subsegs, *points; + memorypool *tet2subpool, *tet2segpool; + + // Memorypools to store bad-quality (or encroached) elements. + memorypool *badtetrahedrons, *badsubfacs, *badsubsegs; + + // A memorypool to store faces to be flipped. + memorypool *flippool; + arraypool *unflipqueue; + badface *flipstack; + + // Arrays used for point insertion (the Bowyer-Watson algorithm). + arraypool *cavetetlist, *cavebdrylist, *caveoldtetlist; + arraypool *cavetetshlist, *cavetetseglist, *cavetetvertlist; + arraypool *caveencshlist, *caveencseglist; + arraypool *caveshlist, *caveshbdlist, *cavesegshlist; + + // Stacks used for CDT construction and boundary recovery. + arraypool *subsegstack, *subfacstack, *subvertstack; + + // Arrays of encroached segments and subfaces (for mesh refinement). + arraypool *encseglist, *encshlist; + + // The map between facets to their vertices (for mesh refinement). + int *idx2facetlist; + point *facetverticeslist; + + // The map between segments to their endpoints (for mesh refinement). + point *segmentendpointslist; + + // The infinite vertex. + point dummypoint; + // The recently visited tetrahedron, subface. + triface recenttet; + face recentsh; + + // PI is the ratio of a circle's circumference to its diameter. + static REAL PI; + + // Array (size = numberoftetrahedra * 6) for storing high-order nodes of + // tetrahedra (only used when -o2 switch is selected). + point *highordertable; + + // Various variables. + int numpointattrib; // Number of point attributes. + int numelemattrib; // Number of tetrahedron attributes. + int sizeoftensor; // Number of REALs per metric tensor. + int pointmtrindex; // Index to find the metric tensor of a point. + int pointparamindex; // Index to find the u,v coordinates of a point. + int point2simindex; // Index to find a simplex adjacent to a point. + int pointmarkindex; // Index to find boundary marker of a point. + int 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 checksubsegflag; // Are there segments in the tetrahedralization yet? + int checksubfaceflag; // Are there subfaces in the tetrahedralization yet? + int checkconstraints; // Are there variant (node, seg, facet) constraints? + int nonconvex; // Is current mesh non-convex? + int autofliplinklevel; // The increase of link levels, default is 1. + int useinsertradius; // Save the insertion radius for Steiner points. + long samples; // Number of random samples for point location. + unsigned long randomseed; // Current random number seed. + REAL cosmaxdihed, cosmindihed; // The cosine values of max/min dihedral. + REAL cossmtdihed; // The cosine value of a bad dihedral to be smoothed. + REAL cosslidihed; // The cosine value of the max dihedral of a sliver. + REAL minfaceang, minfacetdihed; // The minimum input (dihedral) angles. + REAL tetprism_vol_sum; // The total volume of tetrahedral-prisms (in 4D). + REAL longest; // The longest possible edge length. + REAL xmax, xmin, ymax, ymin, zmax, zmin; // Bounding box of points. + + // Counters. + long insegments; // Number of input segments. + long hullsize; // Number of exterior boundary faces. + long meshedges; // Number of mesh edges. + long meshhulledges; // Number of boundary mesh edges. + long steinerleft; // Number of Steiner points not yet used. + long dupverts; // Are there duplicated vertices? + long unuverts; // Are there unused vertices? + long nonregularcount; // Are there non-regular vertices? + long st_segref_count, st_facref_count, st_volref_count; // Steiner points. + long fillregioncount, cavitycount, cavityexpcount; + long flip14count, flip26count, flipn2ncount; + long flip23count, flip32count, flip44count, flip41count; + long flip31count, flip22count; + unsigned long totalworkmemory; // Total memory used by working arrays. + + +/////////////////////////////////////////////////////////////////////////////// +// // +// Mesh manipulation primitives // +// // +/////////////////////////////////////////////////////////////////////////////// + + // Fast lookup tables for mesh manipulation primitives. + static int bondtbl[12][12], fsymtbl[12][12]; + static int esymtbl[12], enexttbl[12], eprevtbl[12]; + static int enextesymtbl[12], eprevesymtbl[12]; + static int eorgoppotbl[12], edestoppotbl[12]; + static int facepivot1[12], facepivot2[12][12]; + static int orgpivot[12], destpivot[12], apexpivot[12], oppopivot[12]; + static int tsbondtbl[12][6], stbondtbl[12][6]; + static int tspivottbl[12][6], stpivottbl[12][6]; + static int ver2edge[12], edge2ver[6], epivot[12]; + static int sorgpivot [6], sdestpivot[6], sapexpivot[6]; + static int snextpivot[6]; + + void inittables(); + // 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 tetrahedron encode2(tetrahedron* ptr, int ver); + inline void decode(tetrahedron ptr, triface& t); inline void bond(triface& t1, triface& t2); inline void dissolve(triface& t); - inline 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 void eprev(triface& t1, triface& t2); + inline void eprevself(triface& t); + inline void enextesym(triface& t1, triface& t2); + inline void enextesymself(triface& t); + inline void eprevesym(triface& t1, triface& t2); + inline void eprevesymself(triface& t); + inline void eorgoppo(triface& t1, triface& t2); + inline void eorgoppoself(triface& t); + inline void edestoppo(triface& t1, triface& t2); + inline void edestoppoself(triface& t); + inline void fsym(triface& t1, triface& t2); + inline void fsymself(triface& t); + inline void fnext(triface& t1, triface& t2); + inline void fnextself(triface& t); + inline 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 p); + inline void setdest(triface& t, point p); + inline void setapex(triface& t, point p); + inline void setoppo(triface& t, point p); 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 int elemindex(tetrahedron* ptr); + inline void setelemindex(tetrahedron* ptr, int value); + inline int elemmarker(tetrahedron* ptr); inline void setelemmarker(tetrahedron* ptr, int value); inline void infect(triface& t); inline void uninfect(triface& t); @@ -1270,10 +1419,20 @@ class tetgenmesh { inline void markedge(triface& t); inline void unmarkedge(triface& t); inline bool edgemarked(triface& t); + inline void marktest2(triface& t); + inline void unmarktest2(triface& t); + inline bool marktest2ed(triface& t); + inline int elemcounter(triface& t); + inline void setelemcounter(triface& t, int value); + inline void increaseelemcounter(triface& t); + inline void decreaseelemcounter(triface& t); + inline bool ishulltet(triface& t); + inline bool isdeadtet(triface& t); // Primitives for subfaces and subsegments. inline void sdecode(shellface sptr, face& s); inline shellface sencode(face& s); + inline shellface sencode2(shellface *sh, int shver); inline void spivot(face& s1, face& s2); inline void spivotself(face& s); inline void sbond(face& s1, face& s2); @@ -1291,365 +1450,144 @@ class tetgenmesh { 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); + inline void smarktest(face& s); + inline void sunmarktest(face& s); + inline bool smarktested(face& s); + inline void smarktest2(face& s); + inline void sunmarktest2(face& s); + inline bool smarktest2ed(face& s); + inline void smarktest3(face& s); + inline void sunmarktest3(face& s); + inline bool smarktest3ed(face& s); + inline void setfacetindex(face& f, int value); + inline int getfacetindex(face& f); // 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); + inline void tspivot(triface& t, face& s); + inline void stpivot(face& s, triface& t); - // 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); + // Primitives for interacting tetrahedra and segments. inline void tssbond1(triface& t, face& seg); + inline void sstbond1(face& s, triface& t); inline void tssdissolve1(triface& t); + inline void sstdissolve1(face& s); + inline void tsspivot1(triface& t, face& s); + inline void sstpivot1(face& s, triface& t); + + // Primitives for interacting subfaces and segments. + inline void ssbond(face& s, face& edge); + inline void ssbond1(face& s, face& edge); + inline void ssdissolve(face& s); + inline void sspivot(face& s, face& edge); // 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 int pointgeomtag(point pt); + inline void setpointgeomtag(point pt, int value); + inline REAL pointgeomuv(point pt, int i); + inline void setpointgeomuv(point pt, int i, REAL value); inline void pinfect(point pt); inline void puninfect(point pt); inline bool pinfected(point pt); + inline void pmarktest(point pt); + inline void punmarktest(point pt); + inline bool pmarktested(point pt); + inline void pmarktest2(point pt); + inline void punmarktest2(point pt); + inline bool pmarktest2ed(point pt); + inline void pmarktest3(point pt); + inline void punmarktest3(point pt); + inline bool pmarktest3ed(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); + inline void setpointinsradius(point pt, REAL value); + inline REAL getpointinsradius(point pt); // 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*); - -/////////////////////////////////////////////////////////////////////////////// -// // -// Arraypool // -// // -// Each arraypool contains an array of pointers to a number of blocks. Each // -// block contains the same fixed number of objects. Each index of the array // -// addesses a particular object in the pool. The most significant bits add- // -// ress the index of the block containing the object. The less significant // -// bits address this object within the block. // -// // -// 'objectbytes' is the size of one object in blocks; 'log2objectsperblock' // -// is the base-2 logarithm of 'objectsperblock'; 'objects' counts the number // -// of allocated objects; 'totalmemory' is the totoal memorypool in bytes. // -// // -/////////////////////////////////////////////////////////////////////////////// - - class arraypool { - - public: - - int objectbytes; - int objectsperblock; - int log2objectsperblock; - int toparraylen; - char **toparray; - long objects; - unsigned long totalmemory; - - void restart(); - void poolinit(int sizeofobject, int log2objperblk); - char* getblock(int objectindex); - void* lookup(int objectindex); - int newindex(void **newptr); - - arraypool(int sizeofobject, int log2objperblk); - ~arraypool(); - }; - -// fastlookup() -- A fast, unsafe operation. Return the pointer to the object -// with a given index. Note: The object's block must have been allocated, -// i.e., by the function newindex(). - -#define fastlookup(pool, index) \ - (void *) ((pool)->toparray[(index) >> (pool)->log2objectsperblock] + \ - ((index) & ((pool)->objectsperblock - 1)) * (pool)->objectbytes) - - -// 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 *); - -/////////////////////////////////////////////////////////////////////////////// -// // -// List // -// // -// An array of items with automatically reallocation of memory. // -// // -// 'base' is the starting address of the array. 'itembytes' is the size of // -// each item in byte. // -// // -// '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. // -// // -// 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]. // -// // -/////////////////////////////////////////////////////////////////////////////// - - class list { - - public: - - 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); } - - 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); - }; - -/////////////////////////////////////////////////////////////////////////////// -// // -// Memorypool // -// // -// A type used to allocate memory. // -// // -// firstblock is the first block of items. nowblock is the block from which // -// items are currently being allocated. nextitem points to the next slab // -// of free memory for an item. deaditemstack is the head of a linked list // -// (stack) of deallocated items that can be recycled. unallocateditems is // -// the number of items that remain to be allocated from nowblock. // -// // -// Traversal is the process of walking through the entire list of items, and // -// is separate from allocation. Note that a traversal will visit items on // -// the "deaditemstack" stack as well as live items. pathblock points to // -// the block currently being traversed. pathitem points to the next item // -// to be traversed. pathitemsleft is the number of items that remain to // -// be traversed in pathblock. // -// // -// itemwordtype is set to POINTER or FLOATINGPOINT, and is used to suggest // -// what sort of word the record is primarily made up of. alignbytes // -// determines how new records should be aligned in memory. itembytes and // -// itemwords are the length of a record in bytes (after rounding up) and // -// words. itemsperblock is the number of items allocated at once in a // -// single block. items is the number of currently allocated items. // -// maxitems is the maximum number of items that have been allocated at // -// once; it is the current number of items plus the number of records kept // -// on deaditemstack. // -// // -/////////////////////////////////////////////////////////////////////////////// - - 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(); - - void poolinit(int, int, enum wordtype, int); - void restart(); - void *alloc(); - void dealloc(void*); - void traversalinit(); - void *traverse(); - }; - -/////////////////////////////////////////////////////////////////////////////// -// // -// Queue // -// // -// A 'queue' is a FIFO data structure. // -// // -/////////////////////////////////////////////////////////////////////////////// - - 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; - } - } - }; + inline void point2tetorg(point pt, triface& t); + inline void point2shorg(point pa, face& s); + inline point farsorg(face& seg); + inline point farsdest(face& seg); /////////////////////////////////////////////////////////////////////////////// // // -// Memory managment routines // +// Memory managment // // // /////////////////////////////////////////////////////////////////////////////// - void dummyinit(int, int); - void initializepools(); void tetrahedrondealloc(tetrahedron*); tetrahedron *tetrahedrontraverse(); + tetrahedron *alltetrahedrontraverse(); void shellfacedealloc(memorypool*, shellface*); shellface *shellfacetraverse(memorypool*); - void badfacedealloc(memorypool*, badface*); - badface *badfacetraverse(memorypool*); void pointdealloc(point); point pointtraverse(); + + void makeindex2pointmap(point*&); + void makepoint2submap(memorypool*, int*&, face*&); void maketetrahedron(triface*); void makeshellface(memorypool*, face*); - void makepoint(point*); + void makepoint(point*, enum verttype); - void makepoint2tetmap(); - void makepoint2segmap(); - void makeindex2pointmap(point*&); - void makesegmentmap(int*&, shellface**&); - void makesubfacemap(int*&, shellface**&); - void maketetrahedronmap(int*&, tetrahedron**&); + void initializepools(); /////////////////////////////////////////////////////////////////////////////// // // -// Geometric functions // +// Advanced geometric predicates and calculations // +// // +// TetGen uses a simplified symbolic perturbation scheme from Edelsbrunner, // +// et al [*]. Hence the point-in-sphere test never returns a zero. The idea // +// is to perturb the weights of vertices in the fourth dimension. TetGen // +// uses the indices of the vertices decide the amount of perturbation. It is // +// implemented in the routine insphere_s(). +// // +// The routine tri_edge_test() determines whether or not a triangle and an // +// edge intersect in 3D. If they intersect, their intersection type is also // +// reported. This test is a combination of n 3D orientation tests (n is bet- // +// ween 3 and 9). It uses the robust orient3d() test to make the branch dec- // +// isions. The routine tri_tri_test() determines whether or not two triang- // +// les intersect in 3D. It also uses the robust orient3d() test. // +// // +// There are a number of routines to calculate geometrical quantities, e.g., // +// circumcenters, angles, dihedral angles, face normals, face areas, etc. // +// They are so far done by the default floating-point arithmetics which are // +// non-robust. They should be improved in the future. // // // /////////////////////////////////////////////////////////////////////////////// - // PI is the ratio of a circle's circumference to its diameter. - static REAL PI; + // Symbolic perturbations (robust) + REAL insphere_s(REAL*, REAL*, REAL*, REAL*, REAL*); + REAL orient4d_s(REAL*, REAL*, REAL*, REAL*, REAL*, + REAL, REAL, REAL, REAL, REAL); - // Triangle-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*); + // Triangle-edge intersection test (robust) int tri_edge_2d(point, point, point, point, point, point, int, int*, int*); + int tri_edge_tail(point, point, point, point, point, point, REAL, REAL, int, + int*, 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); + // Triangle-triangle intersection test (robust) + int tri_edge_inter_tail(point, point, point, point, point, REAL, REAL); + int tri_tri_inter(point, point, point, point, point, point); // Linear algebra functions inline REAL dot(REAL* v1, REAL* v2); @@ -1657,289 +1595,450 @@ class tetgenmesh { 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 + // An embedded 2-dimensional geometric predicate (non-robust) + REAL incircle3d(point pa, point pb, point pc, point pd); + + // Geometric calculations (non-robust) + REAL orient3dfast(REAL *pa, REAL *pb, REAL *pc, REAL *pd); + inline REAL norm2(REAL x, REAL y, REAL z); inline REAL distance(REAL* p1, REAL* p2); + void facenormal(point pa, point pb, point pc, REAL *n, int pivot, REAL *lav); REAL shortdistance(REAL* p, REAL* e1, REAL* e2); - REAL shortdistance(REAL* p, REAL* e1, REAL* e2, REAL* e3); + REAL triarea(REAL* pa, REAL* pb, REAL* pc); 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*); + bool 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); + bool orthosphere(REAL*,REAL*,REAL*,REAL*,REAL,REAL,REAL,REAL,REAL*,REAL*); void planelineint(REAL*, REAL*, REAL*, REAL*, REAL*, REAL*, REAL*); + int linelineint(REAL*, REAL*, REAL*, REAL*, REAL*, REAL*, REAL*, REAL*); + REAL tetprismvol(REAL* pa, REAL* pb, REAL* pc, REAL* pd); + bool calculateabovepoint(arraypool*, point*, point*, point*); + void calculateabovepoint4(point, point, point, point); + +/////////////////////////////////////////////////////////////////////////////// +// // +// Local mesh transformations // +// // +// A local transformation replaces a small set of tetrahedra with another // +// set of tetrahedra which fills the same space and the same boundaries. // +// In 3D, the most simplest local transformations are the elementary flips // +// performed within the convex hull of five vertices: 2-to-3, 3-to-2, 1-to-4,// +// and 4-to-1 flips, where the numbers indicate the number of tetrahedra // +// before and after each flip. The 1-to-4 and 4-to-1 flip involve inserting // +// or deleting a vertex, respectively. // +// There are complex local transformations which can be decomposed as a // +// combination of elementary flips. For example,a 4-to-4 flip which replaces // +// two coplanar edges can be regarded by a 2-to-3 flip and a 3-to-2 flip. // +// Note that the first 2-to-3 flip will temporarily create a degenerate tet- // +// rahedron which is removed immediately by the followed 3-to-2 flip. More // +// generally, a n-to-m flip, where n > 3, m = (n - 2) * 2, which removes an // +// edge can be done by first performing a sequence of (n - 3) 2-to-3 flips // +// followed by a 3-to-2 flip. // +// // +// The routines flip23(), flip32(), and flip41() perform the three element- // +// ray flips. The flip14() is available inside the routine insertpoint(). // +// // +// The routines flipnm() and flipnm_post() implement a generalized edge flip // +// algorithm which uses a combination of elementary flips. // +// // +// The routine insertpoint() implements a variant of Bowyer-Watson's cavity // +// algorithm to insert a vertex. It works for arbitrary tetrahedralization, // +// either Delaunay, or constrained Delaunay, or non-Delaunay. // +// // +/////////////////////////////////////////////////////////////////////////////// + + // The elementary flips. + void flip23(triface*, int, flipconstraints* fc); + void flip32(triface*, int, flipconstraints* fc); + void flip41(triface*, int, flipconstraints* fc); + + // A generalized edge flip. + int flipnm(triface*, int n, int level, int, flipconstraints* fc); + int flipnm_post(triface*, int n, int nn, int, flipconstraints* fc); + + // Point insertion. + int insertpoint(point, triface*, face*, face*, insertvertexflags*); + void insertpoint_abort(face*, insertvertexflags*); + +/////////////////////////////////////////////////////////////////////////////// +// // +// Delaunay tetrahedralization // +// // +// The routine incrementaldelaunay() implemented two incremental algorithms // +// for constructing Delaunay tetrahedralizations (DTs): the Bowyer-Watson // +// (B-W) algorithm and the incremental flip algorithm of Edelsbrunner and // +// Shah, "Incremental topological flipping works for regular triangulation," // +// Algorithmica, 15:233-241, 1996. // +// // +// The routine incrementalflip() implements the flip algorithm of [Edelsbru- // +// nner and Shah, 1996]. It flips a queue of locally non-Delaunay faces (in // +// an arbitrary order). The success is guaranteed when the Delaunay tetrah- // +// edralization is constructed incrementally by adding one vertex at a time. // +// // +// The routine locate() finds a tetrahedron contains a new point in current // +// DT. It uses a simple stochastic walk algorithm: starting from an arbitr- // +// ary tetrahedron in DT, it finds the destination by visit one tetrahedron // +// at a time, randomly chooses a tetrahedron if there are more than one // +// choices. This algorithm terminates due to Edelsbrunner's acyclic theorem. // +// Choose a good starting tetrahedron is crucial to the speed of the walk. // +// TetGen originally uses the "jump-and-walk" algorithm of Muecke, E.P., // +// Saias, I., and Zhu, B. "Fast Randomized Point Location Without Preproces- // +// sing." In Proceedings of the 12th ACM Symposium on Computational Geometry,// +// 274-283, 1996. It first randomly samples several tetrahedra in the DT // +// and then choosing the closet one to start walking. // +// The above algorithm slows download dramatically as the number of points // +// grows -- reported in Amenta, N., Choi, S. and Rote, G., "Incremental // +// construction con {BRIO}," In Proceedings of 19th ACM Symposium on // +// Computational Geometry, 211-219, 2003. On the other hand, Liu and // +// Snoeyink showed that the point location can be made in constant time if // +// the points are pre-sorted so that the nearby points in space have nearby // +// indices, then adding the points in this order. They sorted the points // +// along the 3D Hilbert curve. // +// // +// The routine hilbert_sort3() sorts a set of 3D points along the 3D Hilbert // +// curve. It recursively splits a point set according to the Hilbert indices // +// mapped to the subboxes of the bounding box of the point set. // +// The Hilbert indices is calculated by Butz's algorithm in 1971. A nice // +// exposition of this algorithm can be found in the paper of Hamilton, C., // +// "Compact Hilbert Indices", Technical Report CS-2006-07, Computer Science, // +// Dalhousie University, 2006 (the Section 2). My implementation also refer- // +// enced Steven Witham's implementation of "Hilbert walk" (hopefully, it is // +// still available at: http://www.tiac.net/~sw/2008/10/Hilbert/). // +// // +// TetGen sorts the points using the method in the paper of Boissonnat,J.-D.,// +// Devillers, O. and Hornus, S. "Incremental Construction of the Delaunay // +// Triangulation and the Delaunay Graph in Medium Dimension," In Proceedings // +// of the 25th ACM Symposium on Computational Geometry, 2009. // +// It first randomly sorts the points into subgroups using the Biased Rand-// +// omized Insertion Ordering (BRIO) of Amenta et al 2003, then sorts the // +// points in each subgroup along the 3D Hilbert curve. Inserting points in // +// this order ensures a randomized "sprinkling" of the points over the // +// domain, while sorting of each subset ensures locality. // +// // +/////////////////////////////////////////////////////////////////////////////// + + void transfernodes(); - // Point location routines. + // Point sorting. + int transgc[8][3][8], tsb1mod3[8]; + void hilbert_init(int n); + int hilbert_split(point* vertexarray, int arraysize, int gc0, int gc1, + REAL, REAL, REAL, REAL, REAL, REAL); + void hilbert_sort3(point* vertexarray, int arraysize, int e, int d, + REAL, REAL, REAL, REAL, REAL, REAL, int depth); + void brio_multiscale_sort(point*,int,int threshold,REAL ratio,int* depth); + + // Point location. 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); - -/////////////////////////////////////////////////////////////////////////////// -// // -// Mesh update functions // -// // -/////////////////////////////////////////////////////////////////////////////// - - 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); - -/////////////////////////////////////////////////////////////////////////////// -// // -// Delaunay tetrahedralization functions // -// // -/////////////////////////////////////////////////////////////////////////////// - - // 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); - - 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(); - -/////////////////////////////////////////////////////////////////////////////// -// // -// Surface triangulation functions // -// // -/////////////////////////////////////////////////////////////////////////////// - - 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); + enum locateresult locate(point searchpt, triface *searchtet); + + // Incremental flips. + void flippush(badface*&, triface*); + int incrementalflip(point newpt, int, flipconstraints *fc); + + // Incremental Delaunay construction. + void initialdelaunay(point pa, point pb, point pc, point pd); + void incrementaldelaunay(clock_t&); + +/////////////////////////////////////////////////////////////////////////////// +// // +// Surface triangulation // +// // +/////////////////////////////////////////////////////////////////////////////// + + void flipshpush(face*); + void flip22(face*, int, int); + void flip31(face*, int); + long lawsonflip(); + int sinsertvertex(point newpt, face*, face*, int iloc, int bowywat, int); + int sremovevertex(point delpt, face*, face*, int lawson); + + enum locateresult slocate(point, face*, int, int, int); + enum interresult sscoutsegment(face*, point); + void scarveholes(int, REAL*); + void triangulate(int, arraypool*, arraypool*, int, REAL*); + + void unifysubfaces(face*, face*); void unifysegments(); - void assignsegmentmarkers(); - void mergefacets(queue* flipqueue); - long meshsurface(); + void mergefacets(); + void identifypscedges(point*); + void meshsurface(); + + void interecursive(shellface** subfacearray, int arraysize, int axis, + REAL, REAL, REAL, REAL, REAL, REAL, int* internum); + void detectinterfaces(); + +/////////////////////////////////////////////////////////////////////////////// +// // +// Constrained Delaunay tetrahedralization // +// // +// A constrained Delaunay tetrahedralization (CDT) is a variation of a Dela- // +// unay tetrahedralization (DT) that is constrained to respect the boundary // +// of a 3D PLC (domain). In a CDT of a 3D PLC, every vertex or edge of the // +// PLC is also a vertex or an edge of the CDT, every polygon of the PLC is a // +// union of triangles of the CDT. A crucial difference between a CDT and a // +// DT is that triangles in the PLC's polygons are not required to be locally // +// Delaunay, which frees the CDT to better respect the PLC's polygons. CDTs // +// have optimal properties similar to those of DTs. // +// // +// Steiner Points and Steiner CDTs. It is known that even a simple 3D polyh- // +// edron may not have a tetrahedralization which only uses its own vertices. // +// Some extra points, so-called "Steiner points" are needed in order to form // +// a tetrahedralization of such polyhedron. It is true for tetrahedralizing // +// a 3D PLC as well. A Steiner CDT of a 3D PLC is a CDT containing Steiner // +// points. The CDT algorithms of TetGen in general create Steiner CDTs. // +// Almost all of the Steiner points are added in the edges of the PLC. They // +// guarantee the existence of a CDT of the modified PLC. // +// // +// The routine constraineddelaunay() starts from a DT of the vertices of a // +// PLC and creates a (Steiner) CDT of the PLC (including Steiner points). It // +// is constructed by two steps, (1) segment recovery and (2) facet (polygon) // +// recovery. Each step is accomplished by its own algorithm. // +// // +// The routine delaunizesegments() implements the segment recovery algorithm // +// of Si, H. and Gaertner, K. "Meshing Piecewise Linear Complexes by Constr- // +// ained Delaunay Tetrahedralizations," In Proceedings of the 14th Internat- // +// ional Meshing Roundtable, 147--163, 2005. It adds Steiner points into // +// non-Delaunay segments until all subsegments appear together in a DT. The // +// running time of this algorithm is proportional to the number of added // +// Steiner points. // +// // +// There are two incremental facet recovery algorithms: the cavity re-trian- // +// gulation algorithm of Si, H. and Gaertner, K. "3D Boundary Recovery by // +// Constrained Delaunay Tetrahedralization," International Journal for Numer-// +// ical Methods in Engineering, 85:1341-1364, 2011, and the flip algorithm // +// of Shewchuk, J. "Updating and Constructing Constrained Delaunay and // +// Constrained Regular Triangulations by Flips." In Proceedings of the 19th // +// ACM Symposium on Computational Geometry, 86-95, 2003. // +// // +// It is guaranteed in theory, no Steiner point is needed in both algorithms // +// However, a facet with non-coplanar vertices might cause the additions of // +// Steiner points. It is discussed in the paper of Si, H., and Shewchuk, J.,// +// "Incrementally Constructing and Updating Constrained Delaunay // +// Tetrahedralizations with Finite Precision Coordinates." In Proceedings of // +// the 21th International Meshing Roundtable, 2012. // +// // +// Our implementation of the facet recovery algorithms recover a "missing // +// region" at a time. Each missing region is a subset of connected interiors // +// of a polygon. The routine formcavity() creates the cavity of crossing // +// tetrahedra of the missing region. // +// // +// The cavity re-triangulation algorithm is implemented by three subroutines,// +// delaunizecavity(), fillcavity(), and carvecavity(). Since it may fail due // +// to non-coplanar vertices, the subroutine restorecavity() is used to rest- // +// ore the original cavity. // +// // +// The routine flipinsertfacet() implements the flip algorithm. The subrout- // +// ine flipcertify() is used to maintain the priority queue of flips. // +// // +// The routine refineregion() is called when the facet recovery algorithm // +// fail to recover a missing region. It inserts Steiner points to refine the // +// missing region. In order to avoid inserting Steiner points very close to // +// existing segments. The classical encroachment rules of the Delaunay // +// refinement algorithm are used to choose the Steiner points. // +// // +// The routine constrainedfacets() does the facet recovery by using either // +// the cavity re-triangulation algorithm (default) or the flip algorithm. It // +// results a CDT of the (modified) PLC (including Steiner points). // +// // +/////////////////////////////////////////////////////////////////////////////// + + void makesegmentendpointsmap(); + + enum interresult finddirection(triface* searchtet, point endpt); + enum interresult scoutsegment(point, point, triface*, point*, arraypool*); + int getsteinerptonsegment(face* seg, point refpt, point steinpt); + void delaunizesegments(); + + enum interresult scoutsubface(face* searchsh, triface* searchtet); + void formregion(face*, arraypool*, arraypool*, arraypool*); + int scoutcrossedge(triface& crosstet, arraypool*, arraypool*); + bool formcavity(triface*, arraypool*, arraypool*, arraypool*, arraypool*, + arraypool*, arraypool*); + + // Facet recovery by cavity re-triangulation [Si and Gaertner 2011]. + void delaunizecavity(arraypool*, arraypool*, arraypool*, arraypool*, + arraypool*, arraypool*); + bool fillcavity(arraypool*, arraypool*, arraypool*, arraypool*, + arraypool*, arraypool*, triface* crossedge); + void carvecavity(arraypool*, arraypool*, arraypool*); + void restorecavity(arraypool*, arraypool*, arraypool*, arraypool*); + + // Facet recovery by flips [Shewchuk 2003]. + void flipcertify(triface *chkface, badface **pqueue, point, point, point); + void flipinsertfacet(arraypool*, arraypool*, arraypool*, arraypool*); + + bool fillregion(arraypool* missingshs, arraypool*, arraypool* newshs); + + int insertpoint_cdt(point, triface*, face*, face*, insertvertexflags*, + arraypool*, arraypool*, arraypool*, arraypool*, + arraypool*, arraypool*); + void refineregion(face&, arraypool*, arraypool*, arraypool*, arraypool*, + arraypool*, arraypool*); - // 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(); + void constrainedfacets(); + + void constraineddelaunay(clock_t&); /////////////////////////////////////////////////////////////////////////////// // // -// Constrained Delaunay tetrahedralization functions // +// Constrained tetrahedralizations. // // // /////////////////////////////////////////////////////////////////////////////// - // 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(); + int checkflipeligibility(int fliptype, point, point, point, point, point, + int level, int edgepivot, flipconstraints* fc); - // 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(); + int removeedgebyflips(triface*, flipconstraints*); + int removefacebyflips(triface*, flipconstraints*); + + int recoveredgebyflips(point, point, triface*, int fullsearch); + int add_steinerpt_in_schoenhardtpoly(triface*, int, int chkencflag); + int add_steinerpt_in_segment(face*, int searchlevel); + int addsteiner4recoversegment(face*, int); + int recoversegments(arraypool*, int fullsearch, int steinerflag); + + int recoverfacebyflips(point, point, point, face*, triface*); + int recoversubfaces(arraypool*, int steinerflag); + + int getvertexstar(int, point searchpt, arraypool*, arraypool*, arraypool*); + int getedge(point, point, triface*); + int reduceedgesatvertex(point startpt, arraypool* endptlist); + int removevertexbyflips(point steinerpt); + + int suppressbdrysteinerpoint(point steinerpt); + int suppresssteinerpoints(); + + void recoverboundary(clock_t&); /////////////////////////////////////////////////////////////////////////////// // // -// 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 // +// Mesh reconstruction // // // /////////////////////////////////////////////////////////////////////////////// - void transfernodes(); - long reconstructmesh(); + void carveholes(); + + void reconstructmesh(); + + int scoutpoint(point, triface*, int randflag); + REAL getpointmeshsize(point, triface*, int iloc); + void interpolatemeshsize(); + + void insertconstrainedpoints(point *insertarray, int arylen, int rejflag); void insertconstrainedpoints(tetgenio *addio); - bool p1interpolatebgm(point pt, triface* bgmtet, long *scount); - void interpolatesizemap(); - void duplicatebgmesh(); + + void collectremovepoints(arraypool *remptlist); + void meshcoarsening(); /////////////////////////////////////////////////////////////////////////////// // // -// Mesh refinement functions // +// Mesh refinement // +// // +// The purpose of mesh refinement is to obtain a tetrahedral mesh with well- // +// -shaped tetrahedra and appropriate mesh size. It is necessary to insert // +// new Steiner points to achieve this property. The questions are (1) how to // +// choose the Steiner points? and (2) how to insert them? // +// // +// Delaunay refinement is a technique first developed by Chew [1989] and // +// Ruppert [1993, 1995] to generate quality triangular meshes in the plane. // +// It provides guarantee on the smallest angle of the triangles. Rupper's // +// algorithm guarantees that the mesh is size-optimal (to within a constant // +// factor) among all meshes with the same quality. // +// Shewchuk generalized Ruppert's algorithm into 3D in his PhD thesis // +// [Shewchuk 1997]. A short version of his algorithm appears in "Tetrahedral // +// Mesh Generation by Delaunay Refinement," In Proceedings of the 14th ACM // +// Symposium on Computational Geometry, 86-95, 1998. It guarantees that all // +// tetrahedra of the output mesh have a "radius-edge ratio" (equivalent to // +// the minimal face angle) bounded. However, it does not remove slivers, a // +// type of very flat tetrahedra which can have no small face angles but have // +// very small (and large) dihedral angles. Moreover, it may not terminate if // +// the input PLC contains "sharp features", e.g., two edges (or two facets) // +// meet at an acute angle (or dihedral angle). // +// // +// TetGen uses the basic Delaunay refinement scheme to insert Steiner points.// +// While it always maintains a constrained Delaunay mesh. The algorithm is // +// described in Si, H., "Adaptive Constrained Delaunay Mesh Generation," // +// International Journal for Numerical Methods in Engineering, 75:856-880. // +// This algorithm always terminates and sharp features are easily preserved. // +// The mesh has good quality (same as Shewchuk's Delaunay refinement algori- // +// thm) in the bulk of the mesh domain. Moreover, it supports the generation // +// of adaptive mesh according to a (isotropic) mesh sizing function. // +// // +/////////////////////////////////////////////////////////////////////////////// + + void makefacetverticesmap(); + int segsegadjacent(face *, face *); + int segfacetadjacent(face *checkseg, face *checksh); + int facetfacetadjacent(face *, face *); + + int checkseg4encroach(point pa, point pb, point checkpt); + int checkseg4split(face *chkseg, point&, int&); + int splitsegment(face *splitseg, point encpt, REAL, point, point, int, int); + void repairencsegs(int chkencflag); + + void enqueuesubface(memorypool*, face*); + int checkfac4encroach(point, point, point, point checkpt, REAL*, REAL*); + int checkfac4split(face *chkfac, point& encpt, int& qflag, REAL *ccent); + int splitsubface(face *splitfac, point, point, int qflag, REAL *ccent, int); + void repairencfacs(int chkencflag); + + void enqueuetetrahedron(triface*); + int checktet4split(triface *chktet, int& qflag, REAL *ccent); + int splittetrahedron(triface* splittet,int qflag,REAL *ccent, int); + void repairbadtets(int chkencflag); + + void delaunayrefinement(); + +/////////////////////////////////////////////////////////////////////////////// +// // +// Mesh optimization // // // /////////////////////////////////////////////////////////////////////////////// - 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(); + long lawsonflip3d(flipconstraints *fc); + void recoverdelaunay(); + + int gettetrahedron(point, point, point, point, triface *); + long improvequalitybyflips(); + + int smoothpoint(point smtpt, arraypool*, int ccw, optparameters *opm); + long improvequalitybysmoothing(optparameters *opm); + + int splitsliver(triface *, REAL, int); + long removeslivers(int); + + void optimizemesh(); /////////////////////////////////////////////////////////////////////////////// // // -// Mesh optimization routines // +// Mesh check and statistics // // // /////////////////////////////////////////////////////////////////////////////// - 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 validations. + int checkmesh(int topoflag); + int checkshells(); + int checksegments(); + int checkdelaunay(); + int checkregular(int); + int checkconforming(int); + + // Mesh statistics. + void printfcomma(unsigned long n); + void qualitystatistics(); + void memorystatistics(); + void statistics(); /////////////////////////////////////////////////////////////////////////////// // // -// Mesh output functions // +// Mesh output // // // /////////////////////////////////////////////////////////////////////////////// @@ -1958,339 +2057,154 @@ class tetgenmesh { 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 // +// Constructor & destructor // // // /////////////////////////////////////////////////////////////////////////////// - // 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; + tetgenmesh() + { + in = addin = NULL; + b = NULL; + bgm = NULL; - 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; + tetrahedrons = subfaces = subsegs = points = NULL; + badtetrahedrons = badsubfacs = badsubsegs = NULL; + tet2segpool = tet2subpool = NULL; + flippool = NULL; - // 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; + dummypoint = NULL; + flipstack = NULL; + unflipqueue = NULL; - 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. + cavetetlist = cavebdrylist = caveoldtetlist = NULL; + cavetetshlist = cavetetseglist = cavetetvertlist = NULL; + caveencshlist = caveencseglist = NULL; + caveshlist = caveshbdlist = cavesegshlist = NULL; -/////////////////////////////////////////////////////////////////////////////// -// // -// Class constructor & destructor // -// // -/////////////////////////////////////////////////////////////////////////////// + subsegstack = subfacstack = subvertstack = NULL; + encseglist = encshlist = NULL; + idx2facetlist = NULL; + facetverticeslist = NULL; + segmentendpointslist = NULL; - 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; + highordertable = NULL; - xmax = xmin = ymax = ymin = zmax = zmin = 0.0; - longest = 0.0; - hullsize = 0l; - insegments = 0l; - meshedges = 0l; + numpointattrib = numelemattrib = 0; + sizeoftensor = 0; pointmtrindex = 0; + pointparamindex = 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; + checksubsegflag = 0; + checksubfaceflag = 0; + checkconstraints = 0; nonconvex = 0; - dupverts = 0; - unuverts = 0; - relverts = 0; - suprelverts = 0; - collapverts = 0; - unsupverts = 0; - jettisoninverts = 0; + autofliplinklevel = 1; + useinsertradius = 0; samples = 0l; randomseed = 1l; - macheps = 0.0; minfaceang = minfacetdihed = PI; - b_steinerflag = false; + tetprism_vol_sum = 0.0; + longest = 0.0; + xmax = xmin = ymax = ymin = zmax = zmin = 0.0; - ptloc_count = ptloc_max_count = 0l; - orient3dcount = 0l; - inspherecount = insphere_sos_count = 0l; + insegments = 0l; + hullsize = 0l; + meshedges = meshhulledges = 0l; + steinerleft = -1; + dupverts = 0l; + unuverts = 0l; + nonregularcount = 0l; + st_segref_count = st_facref_count = st_volref_count = 0l; + fillregioncount = cavitycount = cavityexpcount = 0l; flip14count = flip26count = flipn2ncount = 0l; - 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; + flip23count = flip32count = flip44count = flip41count = 0l; + flip22count = flip31count = 0l; + totalworkmemory = 0l; + + } // tetgenmesh() - - ~tetgenmesh() + + void freememory() { - bgm = (tetgenmesh *) NULL; - in = (tetgenio *) NULL; - b = (tetgenbehavior *) NULL; + if (bgm != NULL) { + delete bgm; + } + + if (points != (memorypool *) NULL) { + delete points; + delete [] dummypoint; + } if (tetrahedrons != (memorypool *) NULL) { delete tetrahedrons; } + if (subfaces != (memorypool *) NULL) { delete subfaces; - } - if (subsegs != (memorypool *) NULL) { delete subsegs; } - if (points != (memorypool *) NULL) { - delete points; - } + if (tet2segpool != NULL) { delete tet2segpool; - } - if (tet2subpool != NULL) { delete tet2subpool; } - if (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 (flippool != NULL) { + delete flippool; + delete unflipqueue; } if (cavetetlist != NULL) { delete cavetetlist; delete cavebdrylist; delete caveoldtetlist; + delete cavetetvertlist; + } + + if (caveshlist != NULL) { + delete caveshlist; + delete caveshbdlist; + delete cavesegshlist; + delete cavetetshlist; + delete cavetetseglist; + delete caveencshlist; + delete caveencseglist; } + if (subsegstack != NULL) { delete subsegstack; - } - if (subfacstack != NULL) { delete subfacstack; + delete subvertstack; + } + + if (idx2facetlist != NULL) { + delete [] idx2facetlist; + delete [] facetverticeslist; + } + + if (segmentendpointslist != NULL) { + delete [] segmentendpointslist; + } + + if (highordertable != NULL) { + delete [] highordertable; } + } + + ~tetgenmesh() + { + freememory(); } // ~tetgenmesh() }; // End of class tetgenmesh. @@ -2306,7 +2220,7 @@ class tetgenmesh { // must not be a NULL. 'out' is another object of 'tetgenio' for storing the // // generated tetrahedral mesh. It can be a NULL. If so, the output will be // // saved to file(s). If 'bgmin' != NULL, it contains a background mesh which // -// defines a mesh size distruction function. // +// defines a mesh size function. // // // /////////////////////////////////////////////////////////////////////////////// @@ -2324,8 +2238,12 @@ void tetrahedralize(char *switches, tetgenio *in, tetgenio *out, // // /////////////////////////////////////////////////////////////////////////////// -inline void terminatetetgen(int x) +inline void terminatetetgen(tetgenmesh *m, int x) { + // Release the allocated memory. + if (m) { + m->freememory(); + } #ifdef TETLIBRARY throw x; #else @@ -2334,12 +2252,25 @@ inline void terminatetetgen(int x) 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"); + printf("Please report this bug to Hang.Si@wias-berlin.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; + case 3: + printf("A self-intersection was detected. Program stopped.\n"); + printf("Hint: use -d option to detect all self-intersections.\n"); + break; + case 4: + printf("A very small input feature size was detected. Program stopped.\n"); + printf("Hint: use -T option to set a smaller tolerance.\n"); + break; + case 5: + printf("Two very close input facets were detected. Program stopped.\n"); + printf("Hint: use -Y option to avoid adding Steiner points in boundary.\n"); + break; + case 10: + printf("An input error was detected. Program stopped.\n"); break; - default: - printf("Program stopped.\n"); } // switch (x) exit(x); #endif // #ifdef TETLIBRARY @@ -2347,369 +2278,204 @@ inline void terminatetetgen(int x) /////////////////////////////////////////////////////////////////////////////// // // -// 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 // +// Primitives for tetrahedra // // // /////////////////////////////////////////////////////////////////////////////// -// 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); -} +// encode() compress a handle into a single pointer. It relies on the +// assumption that all addresses of tetrahedra are aligned to sixteen- +// byte boundaries, so that the last four significant bits are zero. inline tetgenmesh::tetrahedron tetgenmesh::encode(triface& t) { - return (tetrahedron) ((uintptr_t) t.tet | (uintptr_t) t.loc); + return (tetrahedron) ((uintptr_t) (t).tet | (uintptr_t) (t).ver); } -// 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 tetgenmesh::tetrahedron tetgenmesh::encode2(tetrahedron* ptr, int ver) { + return (tetrahedron) ((uintptr_t) (ptr) | (uintptr_t) (ver)); } -inline void tetgenmesh::symself(triface& t) { - tetrahedron ptr = t.tet[t.loc]; - decode(ptr, t); +// decode() converts a pointer to a handle. The version is extracted from +// the four least significant bits of the pointer. + +inline void tetgenmesh::decode(tetrahedron ptr, triface& t) { + (t).ver = (int) ((uintptr_t) (ptr) & (uintptr_t) 15); + (t).tet = (tetrahedron *) ((uintptr_t) (ptr) ^ (uintptr_t) (t).ver); } -// Bond two tetrahedra together at their faces. +// bond() connects two tetrahedra together. (t1,v1) and (t2,v2) must +// refer to the same face and the same edge. inline void tetgenmesh::bond(triface& t1, triface& t2) { - t1.tet[t1.loc] = encode(t2); - t2.tet[t2.loc] = encode(t1); + t1.tet[t1.ver & 3] = encode2(t2.tet, bondtbl[t1.ver][t2.ver]); + t2.tet[t2.ver & 3] = encode2(t1.tet, bondtbl[t2.ver][t1.ver]); } -// 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. + +// dissolve() a bond (from one side). inline void tetgenmesh::dissolve(triface& t) { - t.tet[t.loc] = (tetrahedron) dummytet; + t.tet[t.ver & 3] = NULL; } -// These primitives determine or set the origin, destination, apex or -// opposition of a tetrahedron with respect to 'loc' and 'ver'. +// enext() finds the next edge (counterclockwise) in the same face. -inline tetgenmesh::point tetgenmesh::org(triface& t) { - return (point) t.tet[locver2org[t.loc][t.ver] + 4]; +inline void tetgenmesh::enext(triface& t1, triface& t2) { + t2.tet = t1.tet; + t2.ver = enexttbl[t1.ver]; } -inline tetgenmesh::point tetgenmesh::dest(triface& t) { - return (point) t.tet[locver2dest[t.loc][t.ver] + 4]; +inline void tetgenmesh::enextself(triface& t) { + t.ver = enexttbl[t.ver]; } -inline tetgenmesh::point tetgenmesh::apex(triface& t) { - return (point) t.tet[locver2apex[t.loc][t.ver] + 4]; -} +// eprev() finds the next edge (clockwise) in the same face. -inline tetgenmesh::point tetgenmesh::oppo(triface& t) { - return (point) t.tet[loc2oppo[t.loc] + 4]; +inline void tetgenmesh::eprev(triface& t1, triface& t2) { + t2.tet = t1.tet; + t2.ver = eprevtbl[t1.ver]; } -inline void tetgenmesh::setorg(triface& t, point pointptr) { - t.tet[locver2org[t.loc][t.ver] + 4] = (tetrahedron) pointptr; +inline void tetgenmesh::eprevself(triface& t) { + t.ver = eprevtbl[t.ver]; } -inline void tetgenmesh::setdest(triface& t, point pointptr) { - t.tet[locver2dest[t.loc][t.ver] + 4] = (tetrahedron) pointptr; -} +// esym() finds the reversed edge. It is in the other face of the +// same tetrahedron. -inline void tetgenmesh::setapex(triface& t, point pointptr) { - t.tet[locver2apex[t.loc][t.ver] + 4] = (tetrahedron) pointptr; +inline void tetgenmesh::esym(triface& t1, triface& t2) { + (t2).tet = (t1).tet; + (t2).ver = esymtbl[(t1).ver]; } -inline void tetgenmesh::setoppo(triface& t, point pointptr) { - t.tet[loc2oppo[t.loc] + 4] = (tetrahedron) pointptr; +inline void tetgenmesh::esymself(triface& t) { + (t).ver = esymtbl[(t).ver]; } -// 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. +// enextesym() finds the reversed edge of the next edge. It is in the other +// face of the same tetrahedron. It is the combination esym() * enext(). -inline void tetgenmesh::esym(triface& t1, triface& t2) { +inline void tetgenmesh::enextesym(triface& t1, triface& t2) { t2.tet = t1.tet; - t2.loc = t1.loc; - t2.ver = t1.ver + (EdgeRing(t1.ver) ? -1 : 1); + t2.ver = enextesymtbl[t1.ver]; } -inline void tetgenmesh::esymself(triface& t) { - t.ver += (EdgeRing(t.ver) ? -1 : 1); +inline void tetgenmesh::enextesymself(triface& t) { + t.ver = enextesymtbl[t.ver]; } -// If e0 and e1 are both in the same edge ring of a face, e1 = e0.enext(). +// eprevesym() finds the reversed edge of the previous edge. -inline void tetgenmesh::enext(triface& t1, triface& t2) { +inline void tetgenmesh::eprevesym(triface& t1, triface& t2) { t2.tet = t1.tet; - t2.loc = t1.loc; - t2.ver = ve[t1.ver]; + t2.ver = eprevesymtbl[t1.ver]; } -inline void tetgenmesh::enextself(triface& t) { - t.ver = ve[t.ver]; +inline void tetgenmesh::eprevesymself(triface& t) { + t.ver = eprevesymtbl[t.ver]; } -// enext2() is equal to e2 = e0.enext().enext() +// eorgoppo() Finds the opposite face of the origin of the current edge. +// Return the opposite edge of the current edge. -inline void tetgenmesh::enext2(triface& t1, triface& t2) { +inline void tetgenmesh::eorgoppo(triface& t1, triface& t2) { t2.tet = t1.tet; - t2.loc = t1.loc; - t2.ver = ve[ve[t1.ver]]; + t2.ver = eorgoppotbl[t1.ver]; } -inline void tetgenmesh::enext2self(triface& t) { - t.ver = ve[ve[t.ver]]; +inline void tetgenmesh::eorgoppoself(triface& t) { + t.ver = eorgoppotbl[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. +// edestoppo() Finds the opposite face of the destination of the current +// edge. Return the opposite edge of the current edge. -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 void tetgenmesh::edestoppo(triface& t1, triface& t2) { + t2.tet = t1.tet; + t2.ver = edestoppotbl[t1.ver]; } -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; +inline void tetgenmesh::edestoppoself(triface& t) { + t.ver = edestoppotbl[t.ver]; } -// 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. +// fsym() finds the adjacent tetrahedron at the same face and the same edge. -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::fsym(triface& t1, triface& t2) { + decode((t1).tet[(t1).ver & 3], t2); + t2.ver = fsymtbl[t1.ver][t2.ver]; } -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; - } - } + +#define fsymself(t) \ + t1ver = (t).ver; \ + decode((t).tet[(t).ver & 3], (t));\ + (t).ver = fsymtbl[t1ver][(t).ver] + +// fnext() finds the next face while rotating about an edge according to +// a right-hand rule. The face is in the adjacent tetrahedron. It is +// the combination: fsym() * esym(). + +inline void tetgenmesh::fnext(triface& t1, triface& t2) { + decode(t1.tet[facepivot1[t1.ver]], t2); + t2.ver = facepivot2[t1.ver][t2.ver]; } -// 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]; - } - } +#define fnextself(t) \ + t1ver = (t).ver; \ + decode((t).tet[facepivot1[(t).ver]], (t)); \ + (t).ver = facepivot2[t1ver][(t).ver] + + +// The following primtives get or set the origin, destination, face apex, +// or face opposite of an ordered tetrahedron. + +inline tetgenmesh::point tetgenmesh::org(triface& t) { + return (point) (t).tet[orgpivot[(t).ver]]; } -inline void tetgenmesh::tfnextself(triface& t) -{ - int *iptr; +inline tetgenmesh::point tetgenmesh:: dest(triface& t) { + return (point) (t).tet[destpivot[(t).ver]]; +} - 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]; - } - } +inline tetgenmesh::point tetgenmesh:: apex(triface& t) { + return (point) (t).tet[apexpivot[(t).ver]]; } -// enextfnext() and enext2fnext() are combination primitives of enext(), -// enext2() and fnext(). +inline tetgenmesh::point tetgenmesh:: oppo(triface& t) { + return (point) (t).tet[oppopivot[(t).ver]]; +} -inline void tetgenmesh::enextfnext(triface& t1, triface& t2) { - enext(t1, t2); - fnextself(t2); +inline void tetgenmesh:: setorg(triface& t, point p) { + (t).tet[orgpivot[(t).ver]] = (tetrahedron) (p); } -inline void tetgenmesh::enextfnextself(triface& t) { - enextself(t); - fnextself(t); +inline void tetgenmesh:: setdest(triface& t, point p) { + (t).tet[destpivot[(t).ver]] = (tetrahedron) (p); } -inline void tetgenmesh::enext2fnext(triface& t1, triface& t2) { - enext2(t1, t2); - fnextself(t2); +inline void tetgenmesh:: setapex(triface& t, point p) { + (t).tet[apexpivot[(t).ver]] = (tetrahedron) (p); } -inline void tetgenmesh::enext2fnextself(triface& t) { - enext2self(t); - fnextself(t); +inline void tetgenmesh:: setoppo(triface& t, point p) { + (t).tet[oppopivot[(t).ver]] = (tetrahedron) (p); } +#define setvertices(t, torg, tdest, tapex, toppo) \ + (t).tet[orgpivot[(t).ver]] = (tetrahedron) (torg);\ + (t).tet[destpivot[(t).ver]] = (tetrahedron) (tdest); \ + (t).tet[apexpivot[(t).ver]] = (tetrahedron) (tapex); \ + (t).tet[oppopivot[(t).ver]] = (tetrahedron) (toppo) + // 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){ +inline void tetgenmesh::setelemattribute(tetrahedron* ptr, int attnum, + REAL value) { ((REAL *) (ptr))[elemattribindex + attnum] = value; } @@ -2723,9 +2489,23 @@ inline void tetgenmesh::setvolumebound(tetrahedron* ptr, REAL value) { ((REAL *) (ptr))[volumeboundindex] = value; } -// Check or set a tetrahedron's marker. +// Get or set a tetrahedron's index (only used for output). +// These two routines use the reserved slot ptr[10]. + +inline int tetgenmesh::elemindex(tetrahedron* ptr) { + int *iptr = (int *) &(ptr[10]); + return iptr[0]; +} + +inline void tetgenmesh::setelemindex(tetrahedron* ptr, int value) { + int *iptr = (int *) &(ptr[10]); + iptr[0] = value; +} + +// Get or set a tetrahedron's marker. +// Set 'value = 0' cleans all the face/edge flags. -inline int tetgenmesh::getelemmarker(tetrahedron* ptr) { +inline int tetgenmesh::elemmarker(tetrahedron* ptr) { return ((int *) (ptr))[elemmarkerindex]; } @@ -2738,82 +2518,124 @@ inline void tetgenmesh::setelemmarker(tetrahedron* ptr, int value) { // or unflagged (0). inline void tetgenmesh::infect(triface& t) { - ((int *) (t.tet))[elemmarkerindex] |= (int) 1; + ((int *) (t.tet))[elemmarkerindex] |= 1; } inline void tetgenmesh::uninfect(triface& t) { - ((int *) (t.tet))[elemmarkerindex] &= ~(int) 1; + ((int *) (t.tet))[elemmarkerindex] &= ~1; } -// Test a tetrahedron for viral infection. - inline bool tetgenmesh::infected(triface& t) { - return (((int *) (t.tet))[elemmarkerindex] & (int) 1) != 0; + return (((int *) (t.tet))[elemmarkerindex] & 1) != 0; } // marktest(), marktested(), unmarktest() -- primitives to flag or unflag a -// tetrahedron. The last second bit of the element marker is marked (1) -// or unmarked (0). -// One needs them in forming Bowyer-Watson cavity, to mark a tetrahedron if -// it has been checked (for Delaunay case) so later check can be avoided. +// tetrahedron. Use the second lowerest bit of the element marker. inline void tetgenmesh::marktest(triface& t) { - ((int *) (t.tet))[elemmarkerindex] |= (int) 2; + ((int *) (t.tet))[elemmarkerindex] |= 2; } inline void tetgenmesh::unmarktest(triface& t) { - ((int *) (t.tet))[elemmarkerindex] &= ~(int) 2; + ((int *) (t.tet))[elemmarkerindex] &= ~2; } inline bool tetgenmesh::marktested(triface& t) { - return (((int *) (t.tet))[elemmarkerindex] & (int) 2) != 0; + return (((int *) (t.tet))[elemmarkerindex] & 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. + +inline void tetgenmesh::markface(triface& t) { + ((int *) (t.tet))[elemmarkerindex] |= (4 << (t.ver & 3)); +} + +inline void tetgenmesh::unmarkface(triface& t) { + ((int *) (t.tet))[elemmarkerindex] &= ~(4 << (t.ver & 3)); +} + +inline bool tetgenmesh::facemarked(triface& t) { + return (((int *) (t.tet))[elemmarkerindex] & (4 << (t.ver & 3))) != 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 << ver2edge[(t).ver]); +} + +inline void tetgenmesh::unmarkedge(triface& t) { + ((int *) (t.tet))[elemmarkerindex] &= ~(int) (64 << ver2edge[(t).ver]); +} + +inline bool tetgenmesh::edgemarked(triface& t) { + return (((int *) (t.tet))[elemmarkerindex] & + (int) (64 << ver2edge[(t).ver])) != 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. +// marktest2(), unmarktest2(), marktest2ed() -- primitives to flag and unflag +// a tetrahedron. The 13th bit (2^12 = 4096) is used for this flag. -inline void tetgenmesh::markface(triface& t) { - ((int *) (t.tet))[elemmarkerindex] |= (int) (4<<(t).loc); +inline void tetgenmesh::marktest2(triface& t) { + ((int *) (t.tet))[elemmarkerindex] |= (int) (4096); } -inline void tetgenmesh::unmarkface(triface& t) { - ((int *) (t.tet))[elemmarkerindex] &= ~(int) (4<<(t).loc); +inline void tetgenmesh::unmarktest2(triface& t) { + ((int *) (t.tet))[elemmarkerindex] &= ~(int) (4096); } -inline bool tetgenmesh::facemarked(triface& t) { - return (((int *) (t.tet))[elemmarkerindex] & (int) (4<<(t).loc)) != 0; +inline bool tetgenmesh::marktest2ed(triface& t) { + return (((int *) (t.tet))[elemmarkerindex] & (int) (4096)) != 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. +// elemcounter(), setelemcounter() -- primitives to read or ser a (small) +// integer counter in this tet. It is saved from the 16th bit. On 32 bit +// system, the range of the counter is [0, 2^15 = 32768]. -inline void tetgenmesh::markedge(triface& t) { - ((int *) (t.tet))[elemmarkerindex] |= - (int) (64<> 16; } -inline void tetgenmesh::unmarkedge(triface& t) { - ((int *) (t.tet))[elemmarkerindex] &= - ~(int) (64<> 1] = sencode(s2); + s2.sh[s2.shver >> 1] = sencode(s1); } -// sbond1() only bonds s2 to s1, i.e., after bonding, s1 is pointing to s2, -// but s2 is not pointing to s1. +// sbond1() bonds s1 <== s2, i.e., after bonding, s1 is pointing to s2, +// but s2 is not pointing to s1. s1 and s2 must refer to the same edge. +// No requirement is needed on their orientations. -inline void tetgenmesh::sbond1(face& s1, face& s2) { - s1.sh[Orient(s1.shver)] = sencode(s2); +inline void tetgenmesh::sbond1(face& s1, face& s2) +{ + s1.sh[s1.shver >> 1] = 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; +inline void tetgenmesh::sdissolve(face& s) +{ + s.sh[s.shver >> 1] = NULL; } -// These primitives determine or set the origin, destination, or apex -// of a subface with respect to the edge version. +// spivot() finds the adjacent subface (s2) for a given subface (s1). +// s1 and s2 share at the same edge. -inline tetgenmesh::point tetgenmesh::sorg(face& s) { - return (point) s.sh[3 + vo[s.shver]]; +inline void tetgenmesh::spivot(face& s1, face& s2) +{ + shellface sptr = s1.sh[s1.shver >> 1]; + sdecode(sptr, s2); } -inline tetgenmesh::point tetgenmesh::sdest(face& s) { - return (point) s.sh[3 + vd[s.shver]]; +inline void tetgenmesh::spivotself(face& s) +{ + shellface sptr = s.sh[s.shver >> 1]; + sdecode(sptr, s); } -inline tetgenmesh::point tetgenmesh::sapex(face& s) { - return (point) s.sh[3 + va[s.shver]]; -} +// These primitives determine or set the origin, destination, or apex +// of a subface with respect to the edge version. -inline void tetgenmesh::setsorg(face& s, point pointptr) { - s.sh[3 + vo[s.shver]] = (shellface) pointptr; +inline tetgenmesh::point tetgenmesh::sorg(face& s) +{ + return (point) s.sh[sorgpivot[s.shver]]; } -inline void tetgenmesh::setsdest(face& s, point pointptr) { - s.sh[3 + vd[s.shver]] = (shellface) pointptr; +inline tetgenmesh::point tetgenmesh::sdest(face& s) +{ + return (point) s.sh[sdestpivot[s.shver]]; } -inline void tetgenmesh::setsapex(face& s, point pointptr) { - s.sh[3 + va[s.shver]] = (shellface) pointptr; +inline tetgenmesh::point tetgenmesh::sapex(face& s) +{ + return (point) s.sh[sapexpivot[s.shver]]; } -// 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::setsorg(face& s, point pointptr) +{ + s.sh[sorgpivot[s.shver]] = (shellface) pointptr; } -inline void tetgenmesh::sesymself(face& s) { - s.shver += (EdgeRing(s.shver) ? -1 : 1); +inline void tetgenmesh::setsdest(face& s, point pointptr) +{ + s.sh[sdestpivot[s.shver]] = (shellface) pointptr; } -inline void tetgenmesh::senext(face& s1, face& s2) { - s2.sh = s1.sh; - s2.shver = ve[s1.shver]; +inline void tetgenmesh::setsapex(face& s, point pointptr) +{ + s.sh[sapexpivot[s.shver]] = (shellface) pointptr; } -inline void tetgenmesh::senextself(face& s) { - s.shver = ve[s.shver]; -} +#define setshvertices(s, pa, pb, pc)\ + setsorg(s, pa);\ + setsdest(s, pb);\ + setsapex(s, pc) + +// sesym() reserves the direction of the lead edge. -inline void tetgenmesh::senext2(face& s1, face& s2) { +inline void tetgenmesh::sesym(face& s1, face& s2) +{ s2.sh = s1.sh; - s2.shver = ve[ve[s1.shver]]; + s2.shver = (s1.shver ^ 1); // Inverse the last bit. } -inline void tetgenmesh::senext2self(face& s) { - s.shver = ve[ve[s.shver]]; +inline void tetgenmesh::sesymself(face& s) +{ + s.shver ^= 1; } -// If f0 and f1 are both in the same face ring, then f1 = f0.fnext(), +// senext() finds the next edge (counterclockwise) in the same orientation +// of this face. -inline void tetgenmesh::sfnext(face& s1, face& s2) { - getnextsface(&s1, &s2); +inline void tetgenmesh::senext(face& s1, face& s2) +{ + s2.sh = s1.sh; + s2.shver = snextpivot[s1.shver]; } -inline void tetgenmesh::sfnextself(face& s) { - getnextsface(&s, NULL); +inline void tetgenmesh::senextself(face& s) +{ + s.shver = snextpivot[s.shver]; } -// 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::senext2(face& s1, face& s2) +{ + s2.sh = s1.sh; + s2.shver = snextpivot[snextpivot[s1.shver]]; } -inline void tetgenmesh::setshell2badface(face& s, badface* value) { - s.sh[11] = (shellface) value; +inline void tetgenmesh::senext2self(face& s) +{ + s.shver = snextpivot[snextpivot[s.shver]]; } + // Check or set a subface's maximum area bound. -inline REAL tetgenmesh::areabound(face& s) { +inline REAL tetgenmesh::areabound(face& s) +{ return ((REAL *) (s.sh))[areaboundindex]; } -inline void tetgenmesh::setareabound(face& s, REAL value) { +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. -// The last two bits of the int ((int *) ((s).sh))[shmarkindex] are used -// by sinfect() and smarktest(). -inline int tetgenmesh::shellmark(face& s) { - return (((int *) ((s).sh))[shmarkindex]) >> (int) 2; - // return ((int *) (s.sh))[shmarkindex]; +inline int tetgenmesh::shellmark(face& s) +{ + 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; +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]; + +// sinfect(), sinfected(), suninfect() -- primitives to flag or unflag a +// subface. The last bit of ((int *) ((s).sh))[shmarkindex+1] is flagged. + +inline void tetgenmesh::sinfect(face& s) +{ + ((int *) ((s).sh))[shmarkindex+1] = + (((int *) ((s).sh))[shmarkindex+1] | (int) 1); } -inline void tetgenmesh::setshelltype(face& s, enum shestype value) { - ((int *) (s.sh))[shmarkindex + 1] = (int) value; +inline void tetgenmesh::suninfect(face& s) +{ + ((int *) ((s).sh))[shmarkindex+1] = + (((int *) ((s).sh))[shmarkindex+1] & ~(int) 1); } -// These two primitives set or read the pbc group of the subface. +// Test a subface for viral infection. -inline int tetgenmesh::shellpbcgroup(face& s) { - return ((int *) (s.sh))[shmarkindex + 2]; +inline bool tetgenmesh::sinfected(face& s) +{ + return (((int *) ((s).sh))[shmarkindex+1] & (int) 1) != 0; } -inline void tetgenmesh::setshellpbcgroup(face& s, int value) { - ((int *) (s.sh))[shmarkindex + 2] = value; -} +// smarktest(), smarktested(), sunmarktest() -- primitives to flag or unflag +// a subface. The last 2nd bit of the integer is flagged. -// sinfect(), sinfected(), suninfect() -- primitives to flag or unflag a -// subface. The last bit of ((int *) ((s).sh))[shmarkindex] is flaged. +inline void tetgenmesh::smarktest(face& s) +{ + ((int *) ((s).sh))[shmarkindex+1] = + (((int *)((s).sh))[shmarkindex+1] | (int) 2); +} -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::sunmarktest(face& s) +{ + ((int *) ((s).sh))[shmarkindex+1] = + (((int *)((s).sh))[shmarkindex+1] & ~(int)2); } -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); +inline bool tetgenmesh::smarktested(face& s) +{ + return ((((int *) ((s).sh))[shmarkindex+1] & (int) 2) != 0); } -// Test a subface for viral infection. +// smarktest2(), smarktest2ed(), sunmarktest2() -- primitives to flag or +// unflag a subface. The last 3rd bit of the integer is flagged. -inline bool tetgenmesh::sinfected(face& s) { - return (((int *) ((s).sh))[shmarkindex] & (int) 1) != 0; +inline void tetgenmesh::smarktest2(face& s) +{ + ((int *) ((s).sh))[shmarkindex+1] = + (((int *)((s).sh))[shmarkindex+1] | (int) 4); } -// smarktest(), smarktested(), sunmarktest() -- primitives to flag or unflag -// a subface. The last 2nd bit of ((int *) ((s).sh))[shmarkindex] is flaged. +inline void tetgenmesh::sunmarktest2(face& s) +{ + ((int *) ((s).sh))[shmarkindex+1] = + (((int *)((s).sh))[shmarkindex+1] & ~(int)4); +} -#define smarktest(s) \ - ((int *) ((s).sh))[shmarkindex] = (((int *)((s).sh))[shmarkindex] | (int) 2) +inline bool tetgenmesh::smarktest2ed(face& s) +{ + return ((((int *) ((s).sh))[shmarkindex+1] & (int) 4) != 0); +} -#define sunmarktest(s) \ - ((int *) ((s).sh))[shmarkindex] = (((int *)((s).sh))[shmarkindex] & ~(int) 2) +// The last 4th bit of ((int *) ((s).sh))[shmarkindex+1] is flagged. -#define smarktested(s) ((((int *) ((s).sh))[shmarkindex] & (int) 2) != 0) +inline void tetgenmesh::smarktest3(face& s) +{ + ((int *) ((s).sh))[shmarkindex+1] = + (((int *)((s).sh))[shmarkindex+1] | (int) 8); +} -// -// End of primitives for subfaces/subsegments -// +inline void tetgenmesh::sunmarktest3(face& s) +{ + ((int *) ((s).sh))[shmarkindex+1] = + (((int *)((s).sh))[shmarkindex+1] & ~(int)8); +} -// -// Begin of primitives for interacting between tetrahedra and subfaces -// +inline bool tetgenmesh::smarktest3ed(face& s) +{ + return ((((int *) ((s).sh))[shmarkindex+1] & (int) 8) != 0); +} -// 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); -} +// Each facet has a unique index (automatically indexed). Starting from '0'. +// We save this index in the same field of the shell type. -// stpivot() finds a tetrahedron abutting a subface. +inline void tetgenmesh::setfacetindex(face& s, int value) +{ + ((int *) (s.sh))[shmarkindex + 2] = value; +} -inline void tetgenmesh::stpivot(face& s, triface& t) { - tetrahedron ptr = (tetrahedron) s.sh[6 + EdgeRing(s.shver)]; - decode(ptr, t); +inline int tetgenmesh::getfacetindex(face& s) +{ + return ((int *) (s.sh))[shmarkindex + 2]; } -// tsbond() bond a tetrahedron to a subface. +/////////////////////////////////////////////////////////////////////////////// +// // +// Primitives for interacting between tetrahedra and subfaces // +// // +/////////////////////////////////////////////////////////////////////////////// -inline void tetgenmesh::tsbond(triface& t, face& s) { +// tsbond() bond a tetrahedron (t) and a subface (s) together. +// Note that t and s must be the same face and the same edge. Moreover, +// t and s have the same orientation. +// Since the edge number in t and in s can be any number in {0,1,2}. We bond +// the edge in s which corresponds to t's 0th edge, and vice versa. + +inline void tetgenmesh::tsbond(triface& t, face& s) +{ if ((t).tet[9] == NULL) { // Allocate space for this tet. (t).tet[9] = (tetrahedron) tet2subpool->alloc(); - // NULL all fields in this space. + // Initialize. for (int i = 0; i < 4; i++) { - ((shellface *) (t).tet[9])[i] = (shellface) dummysh; + ((shellface *) (t).tet[9])[i] = NULL; } } - // 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); + // Bond t <== s. + ((shellface *) (t).tet[9])[(t).ver & 3] = + sencode2((s).sh, tsbondtbl[t.ver][s.shver]); + // Bond s <== t. + s.sh[9 + ((s).shver & 1)] = + (shellface) encode2((t).tet, stbondtbl[t.ver][s.shver]); } -// tsdissolve() dissolve a bond (from the tetrahedron side). +// tspivot() finds a subface (s) abutting on the given tetrahdera (t). +// Return s.sh = NULL if there is no subface at t. Otherwise, return +// the subface s, and s and t must be at the same edge wth the same +// orientation. -inline void tetgenmesh::tsdissolve(triface& t) { - if ((t).tet[9] != NULL) { - ((shellface *) (t).tet[9])[(t).loc] = (shellface) dummysh; +inline void tetgenmesh::tspivot(triface& t, face& s) +{ + if ((t).tet[9] == NULL) { + (s).sh = NULL; + return; } - // t.tet[8 + t.loc] = (tetrahedron) dummysh; + // Get the attached subface s. + sdecode(((shellface *) (t).tet[9])[(t).ver & 3], (s)); + (s).shver = tspivottbl[t.ver][s.shver]; } -// stdissolve() dissolve a bond (from the subface side). +// Quickly check if the handle (t, v) is a subface. +#define issubface(t) \ + ((t).tet[9] && ((t).tet[9])[(t).ver & 3]) + +// stpivot() finds a tetrahedron (t) abutting a given subface (s). +// Return the t (if it exists) with the same edge and the same +// orientation of s. -inline void tetgenmesh::stdissolve(face& s) { - s.sh[6 + EdgeRing(s.shver)] = (shellface) dummytet; +inline void tetgenmesh::stpivot(face& s, triface& t) +{ + decode((tetrahedron) s.sh[9 + (s.shver & 1)], t); + if ((t).tet == NULL) { + return; + } + (t).ver = stpivottbl[t.ver][s.shver]; } -// -// End of primitives for interacting between tetrahedra and subfaces -// +// Quickly check if this subface is attached to a tetrahedron. -// -// Begin of primitives for interacting between subfaces and subsegs -// +#define isshtet(s) \ + ((s).sh[9 + ((s).shver & 1)]) -// sspivot() finds a subsegment abutting a subface. +// tsdissolve() dissolve a bond (from the tetrahedron side). -inline void tetgenmesh::sspivot(face& s, face& edge) { - shellface sptr = (shellface) s.sh[8 + Orient(s.shver)]; - sdecode(sptr, edge); +inline void tetgenmesh::tsdissolve(triface& t) +{ + if ((t).tet[9] != NULL) { + ((shellface *) (t).tet[9])[(t).ver & 3] = NULL; + } +} + +// stdissolve() dissolve a bond (from the subface side). + +inline void tetgenmesh::stdissolve(face& s) +{ + (s).sh[9] = NULL; + (s).sh[10] = NULL; } +/////////////////////////////////////////////////////////////////////////////// +// // +// Primitives for interacting between subfaces and segments // +// // +/////////////////////////////////////////////////////////////////////////////// + // ssbond() bond a subface to a subsegment. -inline void tetgenmesh::ssbond(face& s, face& edge) { - s.sh[8 + Orient(s.shver)] = sencode(edge); +inline void tetgenmesh::ssbond(face& s, face& edge) +{ + s.sh[6 + (s.shver >> 1)] = sencode(edge); edge.sh[0] = sencode(s); } +inline void tetgenmesh::ssbond1(face& s, face& edge) +{ + s.sh[6 + (s.shver >> 1)] = 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; +inline void tetgenmesh::ssdissolve(face& s) +{ + s.sh[6 + (s.shver >> 1)] = NULL; } -// -// End of primitives for interacting between subfaces and subsegs -// - -// -// Begin of primitives for interacting between tet and subsegs. -// +// sspivot() finds a subsegment abutting a subface. -inline void tetgenmesh::tsspivot1(triface& t, face& s) +inline void tetgenmesh::sspivot(face& s, face& edge) { - 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); + sdecode((shellface) s.sh[6 + (s.shver >> 1)], edge); } -// Only bond/dissolve at tet's side, but not vice versa. +// Quickly check if the edge is a subsegment. + +#define isshsubseg(s) \ + ((s).sh[6 + ((s).shver >> 1)]) + +/////////////////////////////////////////////////////////////////////////////// +// // +// Primitives for interacting between tetrahedra and segments // +// // +/////////////////////////////////////////////////////////////////////////////// inline void tetgenmesh::tssbond1(triface& t, face& s) { if ((t).tet[8] == NULL) { // Allocate space for this tet. (t).tet[8] = (tetrahedron) tet2segpool->alloc(); - // NULL all fields in this space. + // Initialization. for (int i = 0; i < 6; i++) { - ((shellface *) (t).tet[8])[i] = (shellface) dummysh; + ((shellface *) (t).tet[8])[i] = NULL; } } - // 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); + ((shellface *) (t).tet[8])[ver2edge[(t).ver]] = sencode((s)); +} + +inline void tetgenmesh::sstbond1(face& s, triface& t) +{ + ((tetrahedron *) (s).sh)[9] = encode(t); } inline void tetgenmesh::tssdissolve1(triface& t) { if ((t).tet[8] != NULL) { - ((shellface *) (t).tet[8])[locver2edge[(t).loc][(t).ver]] - = (shellface) dummysh; + ((shellface *) (t).tet[8])[ver2edge[(t).ver]] = NULL; + } +} + +inline void tetgenmesh::sstdissolve1(face& s) +{ + ((tetrahedron *) (s).sh)[9] = NULL; +} + +inline void tetgenmesh::tsspivot1(triface& t, face& s) +{ + if ((t).tet[8] != NULL) { + sdecode(((shellface *) (t).tet[8])[ver2edge[(t).ver]], s); + } else { + (s).sh = NULL; } - // t.tet[8 + locver2edge[t.loc][t.ver]] = (tetrahedron) dummysh; } -// -// End of primitives for interacting between tet and subsegs. -// +// Quickly check whether 't' is a segment or not. + +#define issubseg(t) \ + ((t).tet[8] && ((t).tet[8])[ver2edge[(t).ver]]) -// -// Begin of primitives for points -// +inline void tetgenmesh::sstpivot1(face& s, triface& t) +{ + decode((tetrahedron) s.sh[9], t); +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// Primitives for points // +// // +/////////////////////////////////////////////////////////////////////////////// inline int tetgenmesh::pointmark(point pt) { return ((int *) (pt))[pointmarkindex]; @@ -3171,20 +3091,40 @@ 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); + return (enum verttype) (((int *) (pt))[pointmarkindex + 1] >> (int) 8); } inline void tetgenmesh::setpointtype(point pt, enum verttype value) { ((int *) (pt))[pointmarkindex + 1] = - ((int) value << 1) + (((int *) (pt))[pointmarkindex + 1] & (int) 1); + ((int) value << 8) + (((int *) (pt))[pointmarkindex + 1] & (int) 255); +} + +// Read and set the geometry tag of the point (used by -s option). + +inline int tetgenmesh::pointgeomtag(point pt) { + return ((int *) (pt))[pointmarkindex + 2]; +} + +inline void tetgenmesh::setpointgeomtag(point pt, int value) { + ((int *) (pt))[pointmarkindex + 2] = value; +} + +// Read and set the u,v coordinates of the point (used by -s option). + +inline REAL tetgenmesh::pointgeomuv(point pt, int i) { + return pt[pointparamindex + i]; +} + +inline void tetgenmesh::setpointgeomuv(point pt, int i, REAL value) { + pt[pointparamindex + i] = value; } // pinfect(), puninfect(), pinfected() -- primitives to flag or unflag -// a point. The last bit of the integer '[pointindex+1]' is flaged. +// a point. The last bit of the integer '[pointindex+1]' is flagged. inline void tetgenmesh::pinfect(point pt) { ((int *) (pt))[pointmarkindex + 1] |= (int) 1; @@ -3198,133 +3138,171 @@ 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. +// pmarktest(), punmarktest(), pmarktested() -- more primitives to +// flag or unflag a point. -inline tetgenmesh::tetrahedron tetgenmesh::point2tet(point pt) { - return ((tetrahedron *) (pt))[point2simindex]; +inline void tetgenmesh::pmarktest(point pt) { + ((int *) (pt))[pointmarkindex + 1] |= (int) 2; } -inline void tetgenmesh::setpoint2tet(point pt, tetrahedron value) { - ((tetrahedron *) (pt))[point2simindex] = value; +inline void tetgenmesh::punmarktest(point pt) { + ((int *) (pt))[pointmarkindex + 1] &= ~(int) 2; } -inline tetgenmesh::shellface tetgenmesh::point2sh(point pt) { - return (shellface) ((tetrahedron *) (pt))[point2simindex + 1]; +inline bool tetgenmesh::pmarktested(point pt) { + return (((int *) (pt))[pointmarkindex + 1] & (int) 2) != 0; } -inline void tetgenmesh::setpoint2sh(point pt, shellface value) { - ((tetrahedron *) (pt))[point2simindex + 1] = (tetrahedron) value; +inline void tetgenmesh::pmarktest2(point pt) { + ((int *) (pt))[pointmarkindex + 1] |= (int) 4; } -inline tetgenmesh::shellface tetgenmesh::point2seg(point pt) { - return (shellface) ((tetrahedron *) (pt))[point2simindex + 2]; +inline void tetgenmesh::punmarktest2(point pt) { + ((int *) (pt))[pointmarkindex + 1] &= ~(int) 4; } -inline void tetgenmesh::setpoint2seg(point pt, shellface value) { - ((tetrahedron *) (pt))[point2simindex + 2] = (tetrahedron) value; +inline bool tetgenmesh::pmarktest2ed(point pt) { + return (((int *) (pt))[pointmarkindex + 1] & (int) 4) != 0; } -inline tetgenmesh::point tetgenmesh::point2ppt(point pt) { - return (point) ((tetrahedron *) (pt))[point2simindex + 3]; +inline void tetgenmesh::pmarktest3(point pt) { + ((int *) (pt))[pointmarkindex + 1] |= (int) 8; } -inline void tetgenmesh::setpoint2ppt(point pt, point value) { - ((tetrahedron *) (pt))[point2simindex + 3] = (tetrahedron) value; +inline void tetgenmesh::punmarktest3(point pt) { + ((int *) (pt))[pointmarkindex + 1] &= ~(int) 8; } -inline tetgenmesh::tetrahedron tetgenmesh::point2bgmtet(point pt) { - return ((tetrahedron *) (pt))[point2simindex + 4]; +inline bool tetgenmesh::pmarktest3ed(point pt) { + return (((int *) (pt))[pointmarkindex + 1] & (int) 8) != 0; } -inline void tetgenmesh::setpoint2bgmtet(point pt, tetrahedron value) { - ((tetrahedron *) (pt))[point2simindex + 4] = 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]; } -// These primitives set and read a pointer to its pbc point. +inline void tetgenmesh::setpoint2tet(point pt, tetrahedron value) { + ((tetrahedron *) (pt))[point2simindex] = value; +} -inline tetgenmesh::point tetgenmesh::point2pbcpt(point pt) { - return (point) ((tetrahedron *) (pt))[point2pbcptindex]; +inline tetgenmesh::point tetgenmesh::point2ppt(point pt) { + return (point) ((tetrahedron *) (pt))[point2simindex + 1]; } -inline void tetgenmesh::setpoint2pbcpt(point pt, point value) { - ((tetrahedron *) (pt))[point2pbcptindex] = (tetrahedron) value; +inline void tetgenmesh::setpoint2ppt(point pt, point value) { + ((tetrahedron *) (pt))[point2simindex + 1] = (tetrahedron) value; } -// -// End of primitives for points -// +inline tetgenmesh::shellface tetgenmesh::point2sh(point pt) { + return (shellface) ((tetrahedron *) (pt))[point2simindex + 2]; +} -// -// Begin of advanced primitives -// +inline void tetgenmesh::setpoint2sh(point pt, shellface value) { + ((tetrahedron *) (pt))[point2simindex + 2] = (tetrahedron) value; +} -// 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 tetgenmesh::tetrahedron tetgenmesh::point2bgmtet(point pt) { + return ((tetrahedron *) (pt))[point2simindex + 3]; } -inline void tetgenmesh::adjustedgering(face& s, int direction) { - if (EdgeRing(s.shver) != direction) { - sesymself(s); - } +inline void tetgenmesh::setpoint2bgmtet(point pt, tetrahedron value) { + ((tetrahedron *) (pt))[point2simindex + 3] = value; } -// 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; +// The primitives for saving and getting the insertion radius. +inline void tetgenmesh::setpointinsradius(point pt, REAL value) +{ + pt[pointmtrindex + sizeoftensor - 1] = value; } -inline bool tetgenmesh::isdead(face* s) { - if (s->sh == (shellface *) NULL) return true; - else return s->sh[3] == (shellface) NULL; +inline REAL tetgenmesh::getpointinsradius(point pt) +{ + return pt[pointmtrindex + sizeoftensor - 1]; } -// isfacehaspoint() returns TRUE if the 'testpoint' is one of the vertices -// of the tetface 't' subface 's'. +// point2tetorg() Get the tetrahedron whose origin is the point. -inline bool tetgenmesh::isfacehaspoint(triface* t, point testpoint) { - return ((org(*t) == testpoint) || (dest(*t) == testpoint) || - (apex(*t) == testpoint)); +inline void tetgenmesh::point2tetorg(point pa, triface& searchtet) +{ + decode(point2tet(pa), searchtet); + if ((point) searchtet.tet[4] == pa) { + searchtet.ver = 11; + } else if ((point) searchtet.tet[5] == pa) { + searchtet.ver = 3; + } else if ((point) searchtet.tet[6] == pa) { + searchtet.ver = 7; + } else { + assert((point) searchtet.tet[7] == pa); // SELF_CHECK + searchtet.ver = 0; + } } -inline bool tetgenmesh::isfacehaspoint(face* s, point testpoint) { - return (s->sh[3] == (shellface) testpoint) || - (s->sh[4] == (shellface) testpoint) || - (s->sh[5] == (shellface) testpoint); +// point2shorg() Get the subface/segment whose origin is the point. + +inline void tetgenmesh::point2shorg(point pa, face& searchsh) +{ + sdecode(point2sh(pa), searchsh); + if ((point) searchsh.sh[3] == pa) { + searchsh.shver = 0; + } else if ((point) searchsh.sh[4] == pa) { + searchsh.shver = (searchsh.sh[5] != NULL ? 2 : 1); + } else { + assert((point) searchsh.sh[5] == pa); // SELF_CHECK + searchsh.shver = 4; + } } -// isfacehasedge() returns TRUE if the edge (given by its two endpoints) is -// one of the three edges of the subface 's'. +// farsorg() Return the origin of the subsegment. +// farsdest() Return the destination of the subsegment. -inline bool tetgenmesh::isfacehasedge(face* s, point tend1, point tend2) { - return (isfacehaspoint(s, tend1) && isfacehaspoint(s, tend2)); +inline tetgenmesh::point tetgenmesh::farsorg(face& s) +{ + face travesh, neighsh; + + travesh = s; + while (1) { + senext2(travesh, neighsh); + spivotself(neighsh); + if (neighsh.sh == NULL) break; + if (sorg(neighsh) != sorg(travesh)) sesymself(neighsh); + senext2(neighsh, travesh); + } + return sorg(travesh); } -// 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; +inline tetgenmesh::point tetgenmesh::farsdest(face& s) +{ + face travesh, neighsh; + + travesh = s; + while (1) { + senext(travesh, neighsh); + spivotself(neighsh); + if (neighsh.sh == NULL) break; + if (sdest(neighsh) != sdest(travesh)) sesymself(neighsh); + senext(neighsh, travesh); + } + return sdest(travesh); } -// dot() returns the dot product: v1 dot v2. +/////////////////////////////////////////////////////////////////////////////// +// // +// Linear algebra operators. // +// // +/////////////////////////////////////////////////////////////////////////////// +// dot() returns the dot product: v1 dot v2. inline REAL tetgenmesh::dot(REAL* v1, REAL* v2) { return v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2]; } // cross() computes the cross product: n = v1 cross v2. - inline void tetgenmesh::cross(REAL* v1, REAL* v2, REAL* n) { n[0] = v1[1] * v2[2] - v2[1] * v1[2]; @@ -3332,7 +3310,7 @@ inline void tetgenmesh::cross(REAL* v1, REAL* v2, REAL* n) n[2] = v1[0] * v2[1] - v2[0] * v1[1]; } -// distance() computs the Euclidean distance between two points. +// distance() computes the Euclidean distance between two points. inline REAL tetgenmesh::distance(REAL* p1, REAL* p2) { return sqrt((p2[0] - p1[0]) * (p2[0] - p1[0]) + @@ -3340,50 +3318,11 @@ inline REAL tetgenmesh::distance(REAL* p1, REAL* p2) (p2[2] - p1[2]) * (p2[2] - p1[2])); } -// Linear algebra operators. - -#define NORM2(x, y, z) ((x) * (x) + (y) * (y) + (z) * (z)) - -#define DIST(p1, p2) \ - sqrt(NORM2((p2)[0] - (p1)[0], (p2)[1] - (p1)[1], (p2)[2] - (p1)[2])) - -#define DOT(v1, v2) \ - ((v1)[0] * (v2)[0] + (v1)[1] * (v2)[1] + (v1)[2] * (v2)[2]) - -#define CROSS(v1, v2, n) \ - (n)[0] = (v1)[1] * (v2)[2] - (v2)[1] * (v1)[2];\ - (n)[1] = -((v1)[0] * (v2)[2] - (v2)[0] * (v1)[2]);\ - (n)[2] = (v1)[0] * (v2)[1] - (v2)[0] * (v1)[1] - -#define SETVECTOR3(V, a0, a1, a2) (V)[0] = (a0); (V)[1] = (a1); (V)[2] = (a2) - -#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) +inline REAL tetgenmesh::norm2(REAL x, REAL y, REAL z) { - int i = 0; - int j = size - 1; - char c; - - while (i < j) { - c = var[i]; var[i] = var[j]; var[j] = c; - i++, j--; - } + return (x) * (x) + (y) * (y) + (z) * (z); } -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 23dba12..034ba91 100644 --- a/src/cpp/wrap_tetgen.cpp +++ b/src/cpp/wrap_tetgen.cpp @@ -43,8 +43,6 @@ namespace tForeignArray FacetConstraints; tForeignArray SegmentConstraints; - tForeignArray PBCGroups; - tForeignArray Faces; tForeignArray AdjacentElements; tForeignArray FaceMarkers; @@ -75,8 +73,6 @@ namespace FacetConstraints(facetconstraintlist, numberoffacetconstraints, 2), SegmentConstraints(facetconstraintlist, numberofsegmentconstraints, 3), - PBCGroups(pbcgrouplist, numberofpbcgroups), - Faces(trifacelist, numberoftrifaces, 3), AdjacentElements(adjtetlist, numberoftrifaces, 2, &Faces), FaceMarkers(trifacemarkerlist, numberoftrifaces, 1, &Faces), @@ -140,14 +136,12 @@ namespace } OVERRIDE_LOAD_WITH_ERROR_CHECK(node,); - OVERRIDE_LOAD_WITH_ERROR_CHECK(pbc,); OVERRIDE_LOAD_WITH_ERROR_CHECK(var,); OVERRIDE_LOAD_WITH_ERROR_CHECK(mtr,); OVERRIDE_LOAD_WITH_ERROR_CHECK(poly,); OVERRIDE_LOAD_WITH_ERROR_CHECK(off,); 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) @@ -156,9 +150,18 @@ namespace throw std::runtime_error("load_plc failed"); } - OVERRIDE_LOAD_WITH_ERROR_CHECK(tetmesh, - Elements.fixUnit(numberofcorners); - ); + void load_medit(char* filename, int object) + { + if (!super::load_medit(filename, object)) + throw std::runtime_error("load_tetmesh failed"); + } + + void load_tetmesh(char* filename, int object) + { + if (!super::load_tetmesh(filename, object)) + throw std::runtime_error("load_tetmesh failed"); + Elements.fixUnit(numberofcorners); + } /* tTriangulationParameters &operator=(const tTriangulationParameters &src) @@ -260,43 +263,6 @@ namespace { return new tForeignArray(self.vertexlist, self.numberofvertices); } - - - - - - REAL pbcgroup_get_transmat_entry(tetgenio::pbcgroup &self, long i, long j) - { - if (i < 0) i += 4; - if (j < 0) j += 4; - - if (i < 0 || i >= 4 || j < 0 || j >= 4) - PYTHON_ERROR(IndexError, "transform matrix index out of bounds"); - return self.transmat[i][j]; - } - - - - - - void pbcgroup_set_transmat_entry(tetgenio::pbcgroup &self, long i, long j, REAL value) - { - if (i < 0) i += 4; - if (j < 0) j += 4; - - if (i < 0 || i >= 4 || j < 0 || j >= 4) - PYTHON_ERROR(IndexError, "transform matrix index out of bounds"); - self.transmat[i][j] = value; - } - - - - - - tForeignArray *pbcgroup_get_pointpairs(tetgenio::pbcgroup &self) - { - return new tForeignArray(self.pointpairlist, self.numberofpointpairs, 2); - } } @@ -338,8 +304,6 @@ BOOST_PYTHON_MODULE(_tetgen) .def_readonly("facet_constraints", &cl::FacetConstraints) .def_readonly("segment_constraints", &cl::SegmentConstraints) - .def_readonly("pbc_groups", &cl::PBCGroups) - .def_readonly("faces", &cl::Faces) .def_readonly("adjacent_elements", &cl::AdjacentElements) .def_readonly("face_markers", &cl::FaceMarkers) @@ -401,71 +365,80 @@ BOOST_PYTHON_MODULE(_tetgen) ; } - { - typedef tetgenio::pbcgroup cl; - class_("PBCGroup", no_init) - .def_readwrite("facet_marker_1", &cl::fmark1) - .def_readwrite("facet_marker_2", &cl::fmark2) - .def("get_transmat_entry", pbcgroup_get_transmat_entry) - .def("set_transmat_entry", pbcgroup_set_transmat_entry) - .add_property("point_pairs", - make_function(pbcgroup_get_pointpairs, manage_new_internal_reference<>())) - ; - } - { typedef tetgenbehavior cl; class_("Options", init<>()) .DEF_RW_MEMBER(plc) - .DEF_RW_MEMBER(quality) + .DEF_RW_MEMBER(psc) .DEF_RW_MEMBER(refine) - .DEF_RW_MEMBER(coarse) + .DEF_RW_MEMBER(quality) + .DEF_RW_MEMBER(nobisect) + .DEF_RW_MEMBER(coarsen) + .DEF_RW_MEMBER(weighted) + .DEF_RW_MEMBER(brio_hilbert) + .DEF_RW_MEMBER(incrflip) + .DEF_RW_MEMBER(flipinsert) .DEF_RW_MEMBER(metric) .DEF_RW_MEMBER(varvolume) .DEF_RW_MEMBER(fixedvolume) - .DEF_RW_MEMBER(insertaddpoints) .DEF_RW_MEMBER(regionattrib) - .DEF_RW_MEMBER(offcenter) - .DEF_RW_MEMBER(conformdel) + .DEF_RW_MEMBER(conforming) + .DEF_RW_MEMBER(insertaddpoints) .DEF_RW_MEMBER(diagnose) + .DEF_RW_MEMBER(convex) + .DEF_RW_MEMBER(nomergefacet) + .DEF_RW_MEMBER(nomergevertex) + .DEF_RW_MEMBER(noexact) + .DEF_RW_MEMBER(nostaticfilter) .DEF_RW_MEMBER(zeroindex) - .DEF_RW_MEMBER(optlevel) - .DEF_RW_MEMBER(optpasses) - .DEF_RW_MEMBER(order) .DEF_RW_MEMBER(facesout) .DEF_RW_MEMBER(edgesout) .DEF_RW_MEMBER(neighout) .DEF_RW_MEMBER(voroout) .DEF_RW_MEMBER(meditview) - .DEF_RW_MEMBER(gidview) - .DEF_RW_MEMBER(geomview) + .DEF_RW_MEMBER(vtkview) .DEF_RW_MEMBER(nobound) .DEF_RW_MEMBER(nonodewritten) .DEF_RW_MEMBER(noelewritten) .DEF_RW_MEMBER(nofacewritten) .DEF_RW_MEMBER(noiterationnum) - .DEF_RW_MEMBER(nomerge) - .DEF_RW_MEMBER(nobisect) - .DEF_RW_MEMBER(noflip) .DEF_RW_MEMBER(nojettison) - .DEF_RW_MEMBER(steiner) - .DEF_RW_MEMBER(fliprepair) - .DEF_RW_MEMBER(offcenter) + .DEF_RW_MEMBER(reversetetori) .DEF_RW_MEMBER(docheck) .DEF_RW_MEMBER(quiet) .DEF_RW_MEMBER(verbose) - .DEF_RW_MEMBER(useshelles) - .DEF_RW_MEMBER(minratio) - .DEF_RW_MEMBER(goodratio) - .DEF_RW_MEMBER(minangle) - .DEF_RW_MEMBER(goodangle) + + .DEF_RW_MEMBER(vertexperblock) + .DEF_RW_MEMBER(tetrahedraperblock) + .DEF_RW_MEMBER(shellfaceperblock) + .DEF_RW_MEMBER(nobisect_param) + .DEF_RW_MEMBER(addsteiner_algo) + .DEF_RW_MEMBER(coarsen_param) + .DEF_RW_MEMBER(weighted_param) + .DEF_RW_MEMBER(fliplinklevel) + .DEF_RW_MEMBER(flipstarsize) + .DEF_RW_MEMBER(fliplinklevelinc) + .DEF_RW_MEMBER(reflevel) + .DEF_RW_MEMBER(optlevel) + .DEF_RW_MEMBER(optscheme) + .DEF_RW_MEMBER(delmaxfliplevel) + .DEF_RW_MEMBER(order) + .DEF_RW_MEMBER(steinerleft) + .DEF_RW_MEMBER(no_sort) + .DEF_RW_MEMBER(hilbert_order) + .DEF_RW_MEMBER(hilbert_limit) + .DEF_RW_MEMBER(brio_threshold) + .DEF_RW_MEMBER(brio_ratio) + .DEF_RW_MEMBER(facet_ang_tol) .DEF_RW_MEMBER(maxvolume) - .DEF_RW_MEMBER(maxdihedral) - .DEF_RW_MEMBER(alpha1) - .DEF_RW_MEMBER(alpha2) - .DEF_RW_MEMBER(alpha3) + .DEF_RW_MEMBER(minratio) + .DEF_RW_MEMBER(mindihedral) + .DEF_RW_MEMBER(optmaxdihedral) + .DEF_RW_MEMBER(optminsmtdihed) + .DEF_RW_MEMBER(optminslidihed) .DEF_RW_MEMBER(epsilon) - .DEF_RW_MEMBER(epsilon2) + .DEF_RW_MEMBER(minedgelength) + .DEF_RW_MEMBER(coarsen_percent) .def("parse_switches", (bool (tetgenbehavior::*)(char *)) &cl::parse_commandline) ; @@ -475,5 +448,4 @@ BOOST_PYTHON_MODULE(_tetgen) exposePODForeignArray("IntArray"); exposeStructureForeignArray("FacetArray"); exposeStructureForeignArray("PolygonArray"); - exposeStructureForeignArray("PBCGroupArray"); } -- GitLab