diff --git a/tetgen.cxx b/tetgen.cxx index 599b2e1..1ffb8f8 100644 --- a/tetgen.cxx +++ b/tetgen.cxx @@ -36,150 +36,148 @@ // // /////////////////////////////////////////////////////////////////////////////// -bool tetgenio::load_node_call(FILE* infile, int markers, int uvflag, - char* infilename) -{ - char inputline[INPUTLINESIZE]; - char *stringptr; - REAL x, y, z, attrib; - int firstnode, currentmarker; - int index, attribindex; - int i, j; - - // Initialize 'pointlist', 'pointattributelist', and 'pointmarkerlist'. - pointlist = new REAL[numberofpoints * 3]; - if (pointlist == (REAL *) NULL) { - terminatetetgen(NULL, 1); - } - if (numberofpointattributes > 0) { - pointattributelist = new REAL[numberofpoints * numberofpointattributes]; - if (pointattributelist == (REAL *) NULL) { - terminatetetgen(NULL, 1); - } - } - if (markers) { - pointmarkerlist = new int[numberofpoints]; - if (pointmarkerlist == (int *) NULL) { - terminatetetgen(NULL, 1); - } - } - if (uvflag) { - pointparamlist = new pointparam[numberofpoints]; - if (pointparamlist == NULL) { - terminatetetgen(NULL, 1); - } - } - - // Read the point section. - index = 0; - attribindex = 0; - for (i = 0; i < numberofpoints; i++) { - stringptr = readnumberline(inputline, infile, infilename); - if (useindex) { - if (i == 0) { - firstnode = (int) strtol (stringptr, &stringptr, 0); - if ((firstnode == 0) || (firstnode == 1)) { - firstnumber = firstnode; - } - } - stringptr = findnextnumber(stringptr); - } // if (useindex) - if (*stringptr == '\0') { - printf("Error: Point %d has no x coordinate.\n", firstnumber + i); - break; - } - x = (REAL) strtod(stringptr, &stringptr); - stringptr = findnextnumber(stringptr); - if (*stringptr == '\0') { - printf("Error: Point %d has no y coordinate.\n", firstnumber + i); - break; +bool tetgenio::load_node_call(FILE* infile, int markers, int uvflag, char* infilename) { + char inputline[INPUTLINESIZE]; + char* stringptr; + REAL x, y, z, attrib; + int firstnode, currentmarker; + int index, attribindex; + int i, j; + + // Initialize 'pointlist', 'pointattributelist', and 'pointmarkerlist'. + pointlist = new REAL[numberofpoints * 3]; + if (pointlist == (REAL*)NULL) { + terminatetetgen(NULL, 1); } - y = (REAL) strtod(stringptr, &stringptr); - if (mesh_dim == 3) { - stringptr = findnextnumber(stringptr); - if (*stringptr == '\0') { - printf("Error: Point %d has no z coordinate.\n", firstnumber + i); - break; - } - z = (REAL) strtod(stringptr, &stringptr); - } else { - z = 0.0; // mesh_dim == 2; - } - pointlist[index++] = x; - pointlist[index++] = y; - pointlist[index++] = z; - // Read the point attributes. - for (j = 0; j < numberofpointattributes; j++) { - stringptr = findnextnumber(stringptr); - if (*stringptr == '\0') { - attrib = 0.0; - } else { - attrib = (REAL) strtod(stringptr, &stringptr); - } - pointattributelist[attribindex++] = attrib; + if (numberofpointattributes > 0) { + pointattributelist = new REAL[numberofpoints * numberofpointattributes]; + if (pointattributelist == (REAL*)NULL) { + terminatetetgen(NULL, 1); + } } if (markers) { - // Read a point marker. - stringptr = findnextnumber(stringptr); - if (*stringptr == '\0') { - currentmarker = 0; - } else { - currentmarker = (int) strtol (stringptr, &stringptr, 0); - } - pointmarkerlist[i] = currentmarker; + pointmarkerlist = new int[numberofpoints]; + if (pointmarkerlist == (int*)NULL) { + terminatetetgen(NULL, 1); + } } 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. - delete [] pointlist; - pointlist = (REAL *) NULL; - if (markers) { - delete [] pointmarkerlist; - pointmarkerlist = (int *) NULL; + pointparamlist = new pointparam[numberofpoints]; + if (pointparamlist == NULL) { + terminatetetgen(NULL, 1); + } } - if (numberofpointattributes > 0) { - delete [] pointattributelist; - pointattributelist = (REAL *) NULL; + + // Read the point section. + index = 0; + attribindex = 0; + for (i = 0; i < numberofpoints; i++) { + stringptr = readnumberline(inputline, infile, infilename); + if (useindex) { + if (i == 0) { + firstnode = (int)strtol(stringptr, &stringptr, 0); + if ((firstnode == 0) || (firstnode == 1)) { + firstnumber = firstnode; + } + } + stringptr = findnextnumber(stringptr); + } // if (useindex) + if (*stringptr == '\0') { + printf("Error: Point %d has no x coordinate.\n", firstnumber + i); + break; + } + x = (REAL)strtod(stringptr, &stringptr); + stringptr = findnextnumber(stringptr); + if (*stringptr == '\0') { + printf("Error: Point %d has no y coordinate.\n", firstnumber + i); + break; + } + y = (REAL)strtod(stringptr, &stringptr); + if (mesh_dim == 3) { + stringptr = findnextnumber(stringptr); + if (*stringptr == '\0') { + printf("Error: Point %d has no z coordinate.\n", firstnumber + i); + break; + } + z = (REAL)strtod(stringptr, &stringptr); + } else { + z = 0.0; // mesh_dim == 2; + } + pointlist[index++] = x; + pointlist[index++] = y; + pointlist[index++] = z; + // Read the point attributes. + for (j = 0; j < numberofpointattributes; j++) { + stringptr = findnextnumber(stringptr); + if (*stringptr == '\0') { + attrib = 0.0; + } else { + attrib = (REAL)strtod(stringptr, &stringptr); + } + pointattributelist[attribindex++] = attrib; + } + if (markers) { + // Read a point marker. + stringptr = findnextnumber(stringptr); + if (*stringptr == '\0') { + currentmarker = 0; + } else { + currentmarker = (int)strtol(stringptr, &stringptr, 0); + } + 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 (uvflag) { - delete [] pointparamlist; - pointparamlist = NULL; + if (i < numberofpoints) { + // Failed to read points due to some error. + delete[] pointlist; + pointlist = (REAL*)NULL; + if (markers) { + delete[] pointmarkerlist; + pointmarkerlist = (int*)NULL; + } + if (numberofpointattributes > 0) { + delete[] pointattributelist; + pointattributelist = (REAL*)NULL; + } + if (uvflag) { + delete[] pointparamlist; + pointparamlist = NULL; + } + numberofpoints = 0; + return false; } - numberofpoints = 0; - return false; - } - return true; + return true; } /////////////////////////////////////////////////////////////////////////////// @@ -188,76 +186,75 @@ bool tetgenio::load_node_call(FILE* infile, int markers, int uvflag, // // /////////////////////////////////////////////////////////////////////////////// -bool tetgenio::load_node(char* filebasename) -{ - FILE *infile; - char innodefilename[FILENAMESIZE]; - char inputline[INPUTLINESIZE]; - char *stringptr; - bool okflag; - int markers; - int uvflag; // for psc input. - - // Assembling the actual file names we want to open. - strcpy(innodefilename, filebasename); - strcat(innodefilename, ".node"); - - // Try to open a .node file. - infile = fopen(innodefilename, "r"); - if (infile == (FILE *) NULL) { - printf(" Cannot access file %s.\n", innodefilename); - return false; - } - 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 column? - 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 = (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); +bool tetgenio::load_node(char* filebasename) { + FILE* infile; + char innodefilename[FILENAMESIZE]; + char inputline[INPUTLINESIZE]; + char* stringptr; + bool okflag; + int markers; + int uvflag; // for psc input. + + // Assembling the actual file names we want to open. + strcpy(innodefilename, filebasename); + strcat(innodefilename, ".node"); + + // Try to open a .node file. + infile = fopen(innodefilename, "r"); + if (infile == (FILE*)NULL) { + printf(" Cannot access file %s.\n", innodefilename); + return false; } - stringptr = findnextnumber(stringptr); - if (*stringptr != '\0') { - uvflag = (int) strtol (stringptr, &stringptr, 0); - } - } 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. + 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); - numberofpoints = (int) strtol (stringptr, &stringptr, 0); - // There is no index column. - useindex = 0; - } + // Does this file contain an index column? + 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 = (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); + } + stringptr = findnextnumber(stringptr); + if (*stringptr != '\0') { + uvflag = (int)strtol(stringptr, &stringptr, 0); + } + } 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, innodefilename); + numberofpoints = (int)strtol(stringptr, &stringptr, 0); + // There is no index column. + useindex = 0; + } - // Load the list of nodes. - okflag = load_node_call(infile, markers, uvflag, innodefilename); + // Load the list of nodes. + okflag = load_node_call(infile, markers, uvflag, innodefilename); - fclose(infile); - return okflag; + fclose(infile); + return okflag; } /////////////////////////////////////////////////////////////////////////////// @@ -266,79 +263,77 @@ bool tetgenio::load_node(char* filebasename) // // /////////////////////////////////////////////////////////////////////////////// -bool tetgenio::load_edge(char* filebasename) -{ - FILE *infile; - char inedgefilename[FILENAMESIZE]; - char inputline[INPUTLINESIZE]; - char *stringptr; - int markers, corner; - int index; - int i, j; - - strcpy(inedgefilename, filebasename); - strcat(inedgefilename, ".edge"); - - infile = fopen(inedgefilename, "r"); - if (infile != (FILE *) NULL) { - printf("Opening %s.\n", inedgefilename); - } else { - //printf(" Cannot access file %s.\n", inedgefilename); - return false; - } - - // 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. +bool tetgenio::load_edge(char* filebasename) { + FILE* infile; + char inedgefilename[FILENAMESIZE]; + char inputline[INPUTLINESIZE]; + char* stringptr; + int markers, corner; + int index; + int i, j; + + strcpy(inedgefilename, filebasename); + strcat(inedgefilename, ".edge"); + + infile = fopen(inedgefilename, "r"); + if (infile != (FILE*)NULL) { + printf("Opening %s.\n", inedgefilename); } else { - markers = (int) strtol (stringptr, &stringptr, 0); - } - if (markers > 0) { - edgemarkerlist = new int[numberofedges]; + // printf(" Cannot access file %s.\n", inedgefilename); + return false; } - } - // Read the list of edges. - index = 0; - for (i = 0; i < numberofedges; i++) { - // Read edge index and the edge's two endpoints. + // Read number of boundary edges. stringptr = readnumberline(inputline, infile, inedgefilename); - 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, inedgefilename); - terminatetetgen(NULL, 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(NULL, 1); - } - edgelist[index++] = corner; - } - if (numberofcorners == 10) { - // Skip an extra vertex (generated by a previous -o2 option). - stringptr = findnextnumber(stringptr); + 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]; + } } - // Read the edge marker if it has. - if (markers) { - stringptr = findnextnumber(stringptr); - edgemarkerlist[i] = (int) strtol(stringptr, &stringptr, 0); + + // 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: Edge %d is missing vertex %d in %s.\n", i + firstnumber, j + 1, + inedgefilename); + terminatetetgen(NULL, 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(NULL, 1); + } + edgelist[index++] = corner; + } + if (numberofcorners == 10) { + // Skip an extra vertex (generated by a previous -o2 option). + stringptr = findnextnumber(stringptr); + } + // Read the edge marker if it has. + if (markers) { + stringptr = findnextnumber(stringptr); + edgemarkerlist[i] = (int)strtol(stringptr, &stringptr, 0); + } } - } - fclose(infile); - return true; + fclose(infile); + return true; } /////////////////////////////////////////////////////////////////////////////// @@ -347,94 +342,92 @@ bool tetgenio::load_edge(char* filebasename) // // /////////////////////////////////////////////////////////////////////////////// -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 { - return false; - } - - // 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); - } +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 { + return false; } - } - // Read the list of faces. - index = 0; - for (i = 0; i < numberoftrifaces; i++) { - // Read face index and the face's three corners. + // Read number of faces, boundary markers. 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(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++) { + numberoftrifaces = (int)strtol(stringptr, &stringptr, 0); + stringptr = findnextnumber(stringptr); + if (mesh_dim == 2) { + // Skip a number. stringptr = findnextnumber(stringptr); - } } - // 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; + 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: 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') { + attrib = 0.0; + } else { + attrib = (REAL)strtod(stringptr, &stringptr); + } + trifacemarkerlist[i] = (int)attrib; + } } - } - fclose(infile); + fclose(infile); - return true; + return true; } /////////////////////////////////////////////////////////////////////////////// @@ -443,105 +436,101 @@ bool tetgenio::load_face(char* filebasename) // // /////////////////////////////////////////////////////////////////////////////// -bool tetgenio::load_tet(char* filebasename) -{ - FILE *infile; - char infilename[FILENAMESIZE]; - char inputline[INPUTLINESIZE]; - char *stringptr; - REAL attrib; - int corner; - int index, attribindex; - int i, j; - - strcpy(infilename, filebasename); - strcat(infilename, ".ele"); - - infile = fopen(infilename, "r"); - if (infile != (FILE *) NULL) { - printf("Opening %s.\n", infilename); - } else { - return false; - } - - // 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; - } - 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. - tetrahedronlist = new int[numberoftetrahedra * numberofcorners]; - if (tetrahedronlist == (int *) NULL) { - terminatetetgen(NULL, 1); - } - // 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. +bool tetgenio::load_tet(char* filebasename) { + FILE* infile; + char infilename[FILENAMESIZE]; + char inputline[INPUTLINESIZE]; + char* stringptr; + REAL attrib; + int corner; + int index, attribindex; + int i, j; + + strcpy(infilename, filebasename); + strcat(infilename, ".ele"); + + infile = fopen(infilename, "r"); + if (infile != (FILE*)NULL) { + printf("Opening %s.\n", infilename); + } else { + return false; + } + + // Read number of elements, number of corners (4 or 10), number of + // element attributes. 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(NULL, 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); + numberoftetrahedra = (int)strtol(stringptr, &stringptr, 0); + if (numberoftetrahedra <= 0) { + printf("Error: Invalid number of tetrahedra.\n"); + fclose(infile); + return false; + } + 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. + tetrahedronlist = new int[numberoftetrahedra * numberofcorners]; + if (tetrahedronlist == (int*)NULL) { 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; + // 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: Tetrahedron %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: 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); + fclose(infile); - return true; + return true; } /////////////////////////////////////////////////////////////////////////////// @@ -550,2234 +539,2194 @@ bool tetgenio::load_tet(char* filebasename) // // /////////////////////////////////////////////////////////////////////////////// -bool tetgenio::load_vol(char* filebasename) -{ - 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; - } +bool tetgenio::load_vol(char* filebasename) { + FILE* infile; + char inelefilename[FILENAMESIZE]; + char infilename[FILENAMESIZE]; + char inputline[INPUTLINESIZE]; + char* stringptr; + REAL volume; + int volelements; + int i; - tetrahedronvolumelist = new REAL[volelements]; - if (tetrahedronvolumelist == (REAL *) NULL) { - terminatetetgen(NULL, 1); - } + strcpy(infilename, filebasename); + strcat(infilename, ".vol"); - // 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. + infile = fopen(infilename, "r"); + if (infile != (FILE*)NULL) { + printf("Opening %s.\n", infilename); } else { - volume = (REAL) strtod(stringptr, &stringptr); + return false; } - tetrahedronvolumelist[i] = volume; - } - fclose(infile); + // 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; + } - return true; -} + tetrahedronvolumelist = new REAL[volelements]; + if (tetrahedronvolumelist == (REAL*)NULL) { + terminatetetgen(NULL, 1); + } -/////////////////////////////////////////////////////////////////////////////// -// // -// 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 == NULL) { - // No region list, return. - fclose(infile); - return true; - } - 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 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; } - } - // Read the segment constraint section. - stringptr = readnumberline(inputline, infile, varfilename); - if (stringptr == NULL) { - // No segment list, return. fclose(infile); - return true; - } - 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; + return true; } /////////////////////////////////////////////////////////////////////////////// // // -// load_mtr() Load a size specification map from a .mtr file. // +// load_var() Load constraints applied on facets, segments, and nodes // +// from a .var 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 != 1) && (numberofpointmtrs != 3) && - (numberofpointmtrs != 6)) { - // Column number doesn't match. - numberofpointmtrs = 0; - printf(" !! Metric size does not match (1, 3, or 6). Ignored.\n"); - fclose(infile); - return false; - } - - // 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_var(char* filebasename) { + FILE* infile; + char varfilename[FILENAMESIZE]; + char inputline[INPUTLINESIZE]; + char* stringptr; + int index; + int i; -bool tetgenio::load_poly(char* filebasename) -{ - FILE *infile; - char inpolyfilename[FILENAMESIZE]; - char insmeshfilename[FILENAMESIZE]; - char inputline[INPUTLINESIZE]; - char *stringptr, *infilename; - int smesh, markers, uvflag, currentmarker; - int index; - int i, j, k; - - // Assembling the actual file names we want to open. - strcpy(inpolyfilename, filebasename); - strcpy(insmeshfilename, filebasename); - strcat(inpolyfilename, ".poly"); - strcat(insmeshfilename, ".smesh"); - - // First assume it is a .poly file. - smesh = 0; - // Try to open a .poly file. - infile = fopen(inpolyfilename, "r"); - if (infile == (FILE *) NULL) { - // .poly doesn't exist! Try to open a .smesh file. - infile = fopen(insmeshfilename, "r"); - if (infile == (FILE *) NULL) { - printf(" Cannot access file %s and %s.\n", - inpolyfilename, insmeshfilename); - return false; + // 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 { - 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-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, infile, infilename); - 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 (*stringptr != '\0') { - uvflag = (int) strtol (stringptr, &stringptr, 0); - } - - if (numberofpoints > 0) { - // 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. - if (!load_node(filebasename)) { - fclose(infile); - return false; + return false; } - } - - if ((mesh_dim != 3) && (mesh_dim != 2)) { - printf("Input error: TetGen only works for 2D & 3D point sets.\n"); - fclose(infile); - return false; - } - if (numberofpoints < (mesh_dim + 1)) { - printf("Input error: TetGen needs at least %d points.\n", mesh_dim + 1); - fclose(infile); - return false; - } - - facet *f; - polygon *p; - - if (mesh_dim == 3) { - // Read number of facets and number of boundary markers. - stringptr = readnumberline(inputline, infile, infilename); + // Read the facet constraint section. + stringptr = readnumberline(inputline, infile, varfilename); 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(infile); - return true; + // No region list, return. + fclose(infile); + return true; } - stringptr = findnextnumber(stringptr); - if (*stringptr == '\0') { - markers = 0; // no boundary marker. + if (*stringptr != '\0') { + numberoffacetconstraints = (int)strtol(stringptr, &stringptr, 0); } else { - markers = (int) strtol (stringptr, &stringptr, 0); - } - - // Initialize the 'facetlist', 'facetmarkerlist'. - facetlist = new facet[numberoffacets]; - if (markers == 1) { - facetmarkerlist = new int[numberoffacets]; - } - - // Read data into 'facetlist', 'facetmarkerlist'. - if (smesh == 0) { - // Facets are in .poly file format. - for (i = 1; i <= numberoffacets; i++) { - f = &(facetlist[i - 1]); - init(f); - f->numberofholes = 0; - currentmarker = 0; - // Read number of polygons, number of holes, and a boundary marker. - stringptr = readnumberline(inputline, infile, infilename); - f->numberofpolygons = (int) strtol (stringptr, &stringptr, 0); - stringptr = findnextnumber(stringptr); - if (*stringptr != '\0') { - f->numberofholes = (int) strtol (stringptr, &stringptr, 0); - if (markers == 1) { - stringptr = findnextnumber(stringptr); - if (*stringptr != '\0') { - currentmarker = (int) strtol(stringptr, &stringptr, 0); - } - } - } - // Initialize facetmarker if it needs. - if (markers == 1) { - facetmarkerlist[i - 1] = currentmarker; - } - // Each facet should has at least one polygon. - if (f->numberofpolygons <= 0) { - printf("Error: Wrong number of polygon in %d facet.\n", i); - break; - } - // Initialize the 'f->polygonlist'. - f->polygonlist = new polygon[f->numberofpolygons]; - // Go through all polygons, read in their vertices. - for (j = 1; j <= f->numberofpolygons; j++) { - p = &(f->polygonlist[j - 1]); - init(p); - // Read number of vertices of this polygon. - 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); - break; - } - // Initialize 'p->vertexlist'. - p->vertexlist = new int[p->numberofvertices]; - // Read all vertices of this polygon. - for (k = 1; k <= p->numberofvertices; k++) { + 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') { - // Try to load another non-empty line and continue to read the - // rest of vertices. - stringptr = readnumberline(inputline, infile, infilename); - if (*stringptr == '\0') { - printf("Error: Missing %d endpoints of polygon %d in facet %d", - p->numberofvertices - k, j, i); + printf("Error: facet constraint %d has no facet marker.\n", firstnumber + i); break; - } + } else { + facetconstraintlist[index++] = (REAL)strtod(stringptr, &stringptr); } - p->vertexlist[k - 1] = (int) strtol (stringptr, &stringptr, 0); - } - } - if (j <= f->numberofpolygons) { - // This must be caused by an error. However, there're j - 1 - // polygons have been read. Reset the 'f->numberofpolygon'. - if (j == 1) { - // This is the first polygon. - delete [] f->polygonlist; - } - f->numberofpolygons = j - 1; - // No hole will be read even it exists. - f->numberofholes = 0; - break; - } - // If this facet has hole pints defined, read them. - if (f->numberofholes > 0) { - // Initialize 'f->holelist'. - f->holelist = new REAL[f->numberofholes * 3]; - // Read the holes' coordinates. - index = 0; - for (j = 1; j <= f->numberofholes; j++) { - stringptr = readnumberline(inputline, infile, infilename); - for (k = 1; k <= 3; k++) { - stringptr = findnextnumber(stringptr); - if (*stringptr == '\0') { - printf("Error: Hole %d in facet %d has no coordinates", j, i); + stringptr = findnextnumber(stringptr); + if (*stringptr == '\0') { + printf("Error: facet constraint %d has no maximum area bound.\n", firstnumber + i); break; - } - f->holelist[index++] = (REAL) strtod (stringptr, &stringptr); - } - if (k <= 3) { - // This must be caused by an error. - break; + } else { + facetconstraintlist[index++] = (REAL)strtod(stringptr, &stringptr); } - } - if (j <= f->numberofholes) { + } + if (i < numberoffacetconstraints) { // This must be caused by an error. - break; - } + fclose(infile); + return false; } - } - if (i <= numberoffacets) { - // This must be caused by an error. - numberoffacets = i - 1; + } + + // Read the segment constraint section. + stringptr = readnumberline(inputline, infile, varfilename); + if (stringptr == NULL) { + // No segment list, return. fclose(infile); - return false; - } - } else { // poly == 0 - // Read the facets from a .smesh file. - for (i = 1; i <= numberoffacets; i++) { - f = &(facetlist[i - 1]); - init(f); - // Initialize 'f->facetlist'. In a .smesh file, each facetlist only - // contains exactly one polygon, no hole. - f->numberofpolygons = 1; - f->polygonlist = new polygon[f->numberofpolygons]; - p = &(f->polygonlist[0]); - init(p); - // Read number of vertices of this polygon. - 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); - break; - } - // Initialize 'p->vertexlist'. - p->vertexlist = new int[p->numberofvertices]; - for (k = 1; k <= p->numberofvertices; k++) { - stringptr = findnextnumber(stringptr); - if (*stringptr == '\0') { - // Try to load another non-empty line and continue to read the - // rest of vertices. - stringptr = readnumberline(inputline, infile, infilename); + return true; + } + 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: Missing %d endpoints in facet %d", - p->numberofvertices - k, i); - break; + 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); } - } - p->vertexlist[k - 1] = (int) strtol (stringptr, &stringptr, 0); - } - if (k <= p->numberofvertices) { - // This must be caused by an error. - break; } - // Read facet's boundary marker at last. - if (markers == 1) { - stringptr = findnextnumber(stringptr); - if (*stringptr == '\0') { - currentmarker = 0; - } else { - currentmarker = (int) strtol(stringptr, &stringptr, 0); - } - facetmarkerlist[i - 1] = currentmarker; + if (i < numberofsegmentconstraints) { + // This must be caused by an error. + fclose(infile); + return false; } - } - if (i <= numberoffacets) { - // This must be caused by an error. - numberoffacets = i - 1; - fclose(infile); + } + + 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 hole section. - stringptr = readnumberline(inputline, infile, infilename); - if (stringptr == NULL) { - // No hole list, return. - fclose(infile); - return true; + // 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') { - numberofholes = (int) strtol (stringptr, &stringptr, 0); - } else { - numberofholes = 0; + numberofpointmtrs = (int)strtol(stringptr, &stringptr, 0); } - if (numberofholes > 0) { - // Initialize 'holelist'. - holelist = new REAL[numberofholes * 3]; - for (i = 0; i < 3 * numberofholes; i += 3) { - stringptr = readnumberline(inputline, infile, infilename); - stringptr = findnextnumber(stringptr); - if (*stringptr == '\0') { - printf("Error: Hole %d has no x coord.\n", firstnumber + (i / 3)); - break; - } else { - holelist[i] = (REAL) strtod(stringptr, &stringptr); - } - stringptr = findnextnumber(stringptr); - if (*stringptr == '\0') { - printf("Error: Hole %d has no y coord.\n", firstnumber + (i / 3)); - break; - } else { - holelist[i + 1] = (REAL) strtod(stringptr, &stringptr); - } - stringptr = findnextnumber(stringptr); - if (*stringptr == '\0') { - printf("Error: Hole %d has no z coord.\n", firstnumber + (i / 3)); - break; - } else { - holelist[i + 2] = (REAL) strtod(stringptr, &stringptr); - } - } - if (i < 3 * numberofholes) { - // This must be caused by an error. + if ((numberofpointmtrs != 1) && (numberofpointmtrs != 3) && (numberofpointmtrs != 6)) { + // Column number doesn't match. + numberofpointmtrs = 0; + printf(" !! Metric size does not match (1, 3, or 6). Ignored.\n"); 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, infile, NULL); - if (stringptr != (char *) NULL && *stringptr != '\0') { - numberofregions = (int) strtol (stringptr, &stringptr, 0); - } else { - numberofregions = 0; + // Allocate space for pointmtrlist. + pointmtrlist = new REAL[numberofpoints * numberofpointmtrs]; + if (pointmtrlist == (REAL*)NULL) { + terminatetetgen(NULL, 1); } - if (numberofregions > 0) { - // Initialize 'regionlist'. - regionlist = new REAL[numberofregions * 5]; - index = 0; - for (i = 0; i < numberofregions; i++) { - stringptr = readnumberline(inputline, infile, infilename); - stringptr = findnextnumber(stringptr); - if (*stringptr == '\0') { - printf("Error: Region %d has no x coordinate.\n", firstnumber + i); - break; - } else { - regionlist[index++] = (REAL) strtod(stringptr, &stringptr); - } - stringptr = findnextnumber(stringptr); - if (*stringptr == '\0') { - printf("Error: Region %d has no y coordinate.\n", firstnumber + i); - break; - } else { - regionlist[index++] = (REAL) strtod(stringptr, &stringptr); - } - stringptr = findnextnumber(stringptr); - if (*stringptr == '\0') { - printf("Error: Region %d has no z coordinate.\n", firstnumber + i); - break; - } else { - regionlist[index++] = (REAL) strtod(stringptr, &stringptr); - } - stringptr = findnextnumber(stringptr); - if (*stringptr == '\0') { - printf("Error: Region %d has no region attrib.\n", firstnumber + i); - break; - } else { - regionlist[index++] = (REAL) strtod(stringptr, &stringptr); - } - stringptr = findnextnumber(stringptr); - if (*stringptr == '\0') { - regionlist[index] = regionlist[index - 1]; - } else { - regionlist[index] = (REAL) strtod(stringptr, &stringptr); + 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); } - index++; - } - if (i < numberofregions) { - // This must be caused by an error. - fclose(infile); - return false; - } } - } - - // End of reading poly/smesh file. - fclose(infile); - return true; + fclose(infile); + return true; } /////////////////////////////////////////////////////////////////////////////// // // -// load_off() Load a polyhedron from a .off file. // -// // -// The .off format is one of file formats of the Geomview, an interactive // -// program for viewing and manipulating geometric objects. More information // -// is available form: http://www.geomview.org. // +// load_poly() Load a PL complex from a .poly or a .smesh file. // // // /////////////////////////////////////////////////////////////////////////////// -bool tetgenio::load_off(char* filebasename) -{ - FILE *fp; - tetgenio::facet *f; - tetgenio::polygon *p; - char infilename[FILENAMESIZE]; - char buffer[INPUTLINESIZE]; - char *bufferp; - double *coord; - int nverts = 0, iverts = 0; - int nfaces = 0, ifaces = 0; - 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') { - printf("Error: No filename.\n"); - return false; - } - if (strcmp(&infilename[strlen(infilename) - 4], ".off") != 0) { - strcat(infilename, ".off"); - } - - if (!(fp = fopen(infilename, "r"))) { - printf(" Unable to open file %s\n", infilename); - return false; - } - printf("Opening %s.\n", infilename); - - while ((bufferp = readline(buffer, fp, &line_count)) != NULL) { - // Check section - if (nverts == 0) { - // Read header - bufferp = strstr(bufferp, "OFF"); - if (bufferp != NULL) { - // Read mesh counts - bufferp = findnextnumber(bufferp); // Skip field "OFF". - if (*bufferp == '\0') { - // Read a non-empty line. - bufferp = readline(buffer, fp, &line_count); - } - if ((sscanf(bufferp, "%d%d%d", &nverts, &nfaces, &nedges) != 3) - || (nverts == 0)) { - printf("Syntax error reading header on line %d in file %s\n", - line_count, infilename); - fclose(fp); - return false; - } - // Allocate memory for 'tetgenio' - if (nverts > 0) { - numberofpoints = nverts; - pointlist = new REAL[nverts * 3]; - smallestidx = nverts + 1; // A bigger enough number. - } - if (nfaces > 0) { - numberoffacets = nfaces; - facetlist = new tetgenio::facet[nfaces]; - } - } - } else if (iverts < nverts) { - // Read vertex coordinates - coord = &pointlist[iverts * 3]; - for (i = 0; i < 3; i++) { - if (*bufferp == '\0') { - printf("Syntax error reading vertex coords on line %d in file %s\n", - line_count, infilename); - fclose(fp); - return false; - } - coord[i] = (REAL) strtod(bufferp, &bufferp); - bufferp = findnextnumber(bufferp); - } - iverts++; - } else if (ifaces < nfaces) { - // Get next face - f = &facetlist[ifaces]; - init(f); - // In .off format, each facet has one polygon, no hole. - f->numberofpolygons = 1; - f->polygonlist = new tetgenio::polygon[1]; - p = &f->polygonlist[0]; - init(p); - // Read the number of vertices, it should be greater than 0. - p->numberofvertices = (int) strtol(bufferp, &bufferp, 0); - if (p->numberofvertices == 0) { - printf("Syntax error reading polygon on line %d in file %s\n", - line_count, infilename); - fclose(fp); - return false; - } - // Allocate memory for face vertices - p->vertexlist = new int[p->numberofvertices]; - for (i = 0; i < p->numberofvertices; i++) { - bufferp = findnextnumber(bufferp); - if (*bufferp == '\0') { - printf("Syntax error reading polygon on line %d in file %s\n", - line_count, infilename); - fclose(fp); - return false; - } - p->vertexlist[i] = (int) strtol(bufferp, &bufferp, 0); - // Detect the smallest index. - if (p->vertexlist[i] < smallestidx) { - smallestidx = p->vertexlist[i]; - } - } - ifaces++; +bool tetgenio::load_poly(char* filebasename) { + FILE* infile; + char inpolyfilename[FILENAMESIZE]; + char insmeshfilename[FILENAMESIZE]; + char inputline[INPUTLINESIZE]; + char *stringptr, *infilename; + int smesh, markers, uvflag, currentmarker; + int index; + int i, j, k; + + // Assembling the actual file names we want to open. + strcpy(inpolyfilename, filebasename); + strcpy(insmeshfilename, filebasename); + strcat(inpolyfilename, ".poly"); + strcat(insmeshfilename, ".smesh"); + + // First assume it is a .poly file. + smesh = 0; + // Try to open a .poly file. + infile = fopen(inpolyfilename, "r"); + if (infile == (FILE*)NULL) { + // .poly doesn't exist! Try to open a .smesh file. + 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 { - // Should never get here - printf("Found extra text starting at line %d in file %s\n", line_count, - infilename); - break; + printf("Opening %s.\n", inpolyfilename); + infilename = inpolyfilename; } - } - - // 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; - } - - if (iverts != nverts) { - printf("Expected %d vertices, but read only %d vertices in file %s\n", - nverts, iverts, infilename); - return false; - } - if (ifaces != nfaces) { - printf("Expected %d faces, but read only %d faces in file %s\n", - nfaces, ifaces, infilename); - return false; - } - - return true; -} + // Initialize the default values. + 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). -/////////////////////////////////////////////////////////////////////////////// -// // -// load_ply() Load a polyhedron from a .ply file. // -// // -// This is a simplified version of reading .ply files, which only reads the // -// set of vertices and the set of faces. Other informations (such as color, // -// material, texture, etc) in .ply file are ignored. Complete routines for // -// reading and writing ,ply files are available from: http://www.cc.gatech. // -// edu/projects/large_models/ply.html. Except the header section, ply file // -// format has exactly the same format for listing vertices and polygons as // -// off file format. // -// // -/////////////////////////////////////////////////////////////////////////////// + // Read number of points, number of dimensions, number of point + // attributes, and number of boundary markers. + stringptr = readnumberline(inputline, infile, infilename); + 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 (*stringptr != '\0') { + uvflag = (int)strtol(stringptr, &stringptr, 0); + } -bool tetgenio::load_ply(char* filebasename) -{ - FILE *fp; - tetgenio::facet *f; - tetgenio::polygon *p; - char infilename[FILENAMESIZE]; - char buffer[INPUTLINESIZE]; - char *bufferp, *str; - double *coord; - int endheader = 0, format = 0; - int nverts = 0, iverts = 0; - 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') { - printf("Error: No filename.\n"); - return false; - } - if (strcmp(&infilename[strlen(infilename) - 4], ".ply") != 0) { - strcat(infilename, ".ply"); - } - - if (!(fp = fopen(infilename, "r"))) { - printf("Error: Unable to open file %s\n", infilename); - return false; - } - printf("Opening %s.\n", infilename); - - while ((bufferp = readline(buffer, fp, &line_count)) != NULL) { - if (!endheader) { - // Find if it is the keyword "end_header". - str = strstr(bufferp, "end_header"); - // strstr() is case sensitive. - if (!str) str = strstr(bufferp, "End_header"); - if (!str) str = strstr(bufferp, "End_Header"); - if (str) { - // This is the end of the header section. - endheader = 1; - continue; - } - // Parse the number of vertices and the number of faces. - if (nverts == 0 || nfaces == 0) { - // Find if it si the keyword "element". - str = strstr(bufferp, "element"); - if (!str) str = strstr(bufferp, "Element"); - if (str) { - bufferp = findnextfield(str); - if (*bufferp == '\0') { - printf("Syntax error reading element type on line%d in file %s\n", - line_count, infilename); - fclose(fp); + if (numberofpoints > 0) { + // Load the list of nodes. + if (!load_node_call(infile, markers, uvflag, infilename)) { + fclose(infile); return false; - } - if (nverts == 0) { - // Find if it is the keyword "vertex". - str = strstr(bufferp, "vertex"); - if (!str) str = strstr(bufferp, "Vertex"); - if (str) { - bufferp = findnextnumber(str); - if (*bufferp == '\0') { - printf("Syntax error reading vertex number on line"); - printf(" %d in file %s\n", line_count, infilename); - fclose(fp); - return false; - } - nverts = (int) strtol(bufferp, &bufferp, 0); - // Allocate memory for 'tetgenio' - if (nverts > 0) { - numberofpoints = nverts; - pointlist = new REAL[nverts * 3]; - smallestidx = nverts + 1; // A big enough index. - } - } - } - if (nfaces == 0) { - // Find if it is the keyword "face". - str = strstr(bufferp, "face"); - if (!str) str = strstr(bufferp, "Face"); - if (str) { - bufferp = findnextnumber(str); - if (*bufferp == '\0') { - printf("Syntax error reading face number on line"); - printf(" %d in file %s\n", line_count, infilename); - fclose(fp); - return false; - } - nfaces = (int) strtol(bufferp, &bufferp, 0); - // Allocate memory for 'tetgenio' - if (nfaces > 0) { - numberoffacets = nfaces; - facetlist = new tetgenio::facet[nfaces]; - } - } - } - } // It is not the string "element". - } - if (format == 0) { - // Find the keyword "format". - str = strstr(bufferp, "format"); - if (!str) str = strstr(bufferp, "Format"); - if (str) { - format = 1; - bufferp = findnextfield(str); - // Find if it is the string "ascii". - str = strstr(bufferp, "ascii"); - if (!str) str = strstr(bufferp, "ASCII"); - if (!str) { - printf("This routine only reads ascii format of ply files.\n"); - printf("Hint: You can convert the binary to ascii format by\n"); - printf(" using the provided ply tools:\n"); - printf(" ply2ascii < %s > ascii_%s\n", infilename, infilename); - fclose(fp); + } + } else { + // If the .poly or .smesh file claims there are zero points, that + // means the points should be read from a separate .node file. + if (!load_node(filebasename)) { + fclose(infile); return false; - } } - } - } else if (iverts < nverts) { - // Read vertex coordinates - coord = &pointlist[iverts * 3]; - for (i = 0; i < 3; i++) { - if (*bufferp == '\0') { - printf("Syntax error reading vertex coords on line %d in file %s\n", - line_count, infilename); - fclose(fp); - return false; - } - coord[i] = (REAL) strtod(bufferp, &bufferp); - bufferp = findnextnumber(bufferp); - } - iverts++; - } else if (ifaces < nfaces) { - // Get next face - f = &facetlist[ifaces]; - init(f); - // In .off format, each facet has one polygon, no hole. - f->numberofpolygons = 1; - f->polygonlist = new tetgenio::polygon[1]; - p = &f->polygonlist[0]; - init(p); - // Read the number of vertices, it should be greater than 0. - p->numberofvertices = (int) strtol(bufferp, &bufferp, 0); - if (p->numberofvertices == 0) { - printf("Syntax error reading polygon on line %d in file %s\n", - line_count, infilename); - fclose(fp); + } + + if ((mesh_dim != 3) && (mesh_dim != 2)) { + printf("Input error: TetGen only works for 2D & 3D point sets.\n"); + fclose(infile); + return false; + } + if (numberofpoints < (mesh_dim + 1)) { + printf("Input error: TetGen needs at least %d points.\n", mesh_dim + 1); + fclose(infile); return false; - } - // Allocate memory for face vertices - p->vertexlist = new int[p->numberofvertices]; - for (i = 0; i < p->numberofvertices; i++) { - bufferp = findnextnumber(bufferp); - if (*bufferp == '\0') { - printf("Syntax error reading polygon on line %d in file %s\n", - line_count, infilename); - fclose(fp); - return false; - } - p->vertexlist[i] = (int) strtol(bufferp, &bufferp, 0); - if (p->vertexlist[i] < smallestidx) { - smallestidx = p->vertexlist[i]; - } - } - ifaces++; - } else { - // Should never get here - printf("Found extra text starting at line %d in file %s\n", line_count, - infilename); - break; } - } - // Close file - fclose(fp); + facet* f; + polygon* p; - // 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; - } - if (ifaces != nfaces) { - printf("Expected %d faces, but read only %d faces in file %s\n", - nfaces, ifaces, infilename); - return false; - } - - return true; -} + if (mesh_dim == 3) { -/////////////////////////////////////////////////////////////////////////////// -// // -// load_stl() Load a surface mesh from a .stl file. // -// // -// The .stl or stereolithography format is an ASCII or binary file used in // -// manufacturing. It is a list of the triangular surfaces that describe a // -// computer generated solid model. This is the standard input for most rapid // -// prototyping machines. // -// // -// Comment: A .stl file many contain many duplicated points. They will be // -// unified during the Delaunay tetrahedralization process. // -// // -/////////////////////////////////////////////////////////////////////////////// + // Read number of facets and number of boundary markers. + 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(infile); + return true; + } + stringptr = findnextnumber(stringptr); + if (*stringptr == '\0') { + markers = 0; // no boundary marker. + } else { + markers = (int)strtol(stringptr, &stringptr, 0); + } -void SwapBytes(char *array, int size, int n) -{ - char *x = new char[size]; - for(int i = 0; i < n; i++) { - char *a = &array[i * size]; - memcpy(x, a, size); - for(int c = 0; c < size; c++) - a[size - 1 - c] = x[c]; - } - delete [] x; -} + // Initialize the 'facetlist', 'facetmarkerlist'. + facetlist = new facet[numberoffacets]; + if (markers == 1) { + facetmarkerlist = new int[numberoffacets]; + } + + // Read data into 'facetlist', 'facetmarkerlist'. + if (smesh == 0) { + // Facets are in .poly file format. + for (i = 1; i <= numberoffacets; i++) { + f = &(facetlist[i - 1]); + init(f); + f->numberofholes = 0; + currentmarker = 0; + // Read number of polygons, number of holes, and a boundary marker. + stringptr = readnumberline(inputline, infile, infilename); + f->numberofpolygons = (int)strtol(stringptr, &stringptr, 0); + stringptr = findnextnumber(stringptr); + if (*stringptr != '\0') { + f->numberofholes = (int)strtol(stringptr, &stringptr, 0); + if (markers == 1) { + stringptr = findnextnumber(stringptr); + if (*stringptr != '\0') { + currentmarker = (int)strtol(stringptr, &stringptr, 0); + } + } + } + // Initialize facetmarker if it needs. + if (markers == 1) { + facetmarkerlist[i - 1] = currentmarker; + } + // Each facet should has at least one polygon. + if (f->numberofpolygons <= 0) { + printf("Error: Wrong number of polygon in %d facet.\n", i); + break; + } + // Initialize the 'f->polygonlist'. + f->polygonlist = new polygon[f->numberofpolygons]; + // Go through all polygons, read in their vertices. + for (j = 1; j <= f->numberofpolygons; j++) { + p = &(f->polygonlist[j - 1]); + init(p); + // Read number of vertices of this polygon. + 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); + break; + } + // Initialize 'p->vertexlist'. + p->vertexlist = new int[p->numberofvertices]; + // Read all vertices of this polygon. + for (k = 1; k <= p->numberofvertices; k++) { + stringptr = findnextnumber(stringptr); + if (*stringptr == '\0') { + // Try to load another non-empty line and continue to read the + // rest of vertices. + stringptr = readnumberline(inputline, infile, infilename); + if (*stringptr == '\0') { + printf("Error: Missing %d endpoints of polygon %d in facet %d", + p->numberofvertices - k, j, i); + break; + } + } + p->vertexlist[k - 1] = (int)strtol(stringptr, &stringptr, 0); + } + } + if (j <= f->numberofpolygons) { + // This must be caused by an error. However, there're j - 1 + // polygons have been read. Reset the 'f->numberofpolygon'. + if (j == 1) { + // This is the first polygon. + delete[] f->polygonlist; + } + f->numberofpolygons = j - 1; + // No hole will be read even it exists. + f->numberofholes = 0; + break; + } + // If this facet has hole pints defined, read them. + if (f->numberofholes > 0) { + // Initialize 'f->holelist'. + f->holelist = new REAL[f->numberofholes * 3]; + // Read the holes' coordinates. + index = 0; + for (j = 1; j <= f->numberofholes; j++) { + stringptr = readnumberline(inputline, infile, infilename); + for (k = 1; k <= 3; k++) { + stringptr = findnextnumber(stringptr); + if (*stringptr == '\0') { + printf("Error: Hole %d in facet %d has no coordinates", j, i); + break; + } + f->holelist[index++] = (REAL)strtod(stringptr, &stringptr); + } + if (k <= 3) { + // This must be caused by an error. + break; + } + } + if (j <= f->numberofholes) { + // This must be caused by an error. + break; + } + } + } + if (i <= numberoffacets) { + // This must be caused by an error. + numberoffacets = i - 1; + fclose(infile); + return false; + } + } else { // poly == 0 + // Read the facets from a .smesh file. + for (i = 1; i <= numberoffacets; i++) { + f = &(facetlist[i - 1]); + init(f); + // Initialize 'f->facetlist'. In a .smesh file, each facetlist only + // contains exactly one polygon, no hole. + f->numberofpolygons = 1; + f->polygonlist = new polygon[f->numberofpolygons]; + p = &(f->polygonlist[0]); + init(p); + // Read number of vertices of this polygon. + 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); + break; + } + // Initialize 'p->vertexlist'. + p->vertexlist = new int[p->numberofvertices]; + for (k = 1; k <= p->numberofvertices; k++) { + stringptr = findnextnumber(stringptr); + if (*stringptr == '\0') { + // Try to load another non-empty line and continue to read the + // rest of vertices. + stringptr = readnumberline(inputline, infile, infilename); + if (*stringptr == '\0') { + printf("Error: Missing %d endpoints in facet %d", + p->numberofvertices - k, i); + break; + } + } + p->vertexlist[k - 1] = (int)strtol(stringptr, &stringptr, 0); + } + if (k <= p->numberofvertices) { + // This must be caused by an error. + break; + } + // Read facet's boundary marker at last. + if (markers == 1) { + stringptr = findnextnumber(stringptr); + if (*stringptr == '\0') { + currentmarker = 0; + } else { + currentmarker = (int)strtol(stringptr, &stringptr, 0); + } + facetmarkerlist[i - 1] = currentmarker; + } + } + if (i <= numberoffacets) { + // This must be caused by an error. + numberoffacets = i - 1; + fclose(infile); + return false; + } + } -bool tetgenio::load_stl(char* filebasename) -{ - FILE *fp; - tetgenmesh::arraypool *plist; - tetgenio::facet *f; - tetgenio::polygon *p; - char infilename[FILENAMESIZE]; - char buffer[INPUTLINESIZE]; - char *bufferp, *str; - double *coord; - int solid = 0; - int nverts = 0, iverts = 0; - int nfaces = 0; - int line_count = 0, i; - - strncpy(infilename, filebasename, FILENAMESIZE - 1); - infilename[FILENAMESIZE - 1] = '\0'; - if (infilename[0] == '\0') { - printf("Error: No filename.\n"); - return false; - } - if (strcmp(&infilename[strlen(infilename) - 4], ".stl") != 0) { - strcat(infilename, ".stl"); - } - - if (!(fp = fopen(infilename, "rb"))) { - printf("Error: Unable to open file %s\n", infilename); - return false; - } - printf("Opening %s.\n", infilename); - - // "solid", or binary data header - if(!fgets(buffer, sizeof(buffer), fp)){ fclose(fp); return 0; } - bool binary = strncmp(buffer, "solid", 5) && strncmp(buffer, "SOLID", 5); - - // STL file has no number of points available. Use a list to read points. - plist = new tetgenmesh::arraypool(sizeof(double) * 3, 10); - - if(!binary){ - solid = 1; - while ((bufferp = readline(buffer, fp, &line_count)) != NULL) { - // The ASCII .stl file must start with the lower case keyword solid and - // end with endsolid. - if (solid == 0) { - // Read header - bufferp = strstr(bufferp, "solid"); - if (bufferp != NULL) { - solid = 1; - } - } else { - // We're inside the block of the solid. - str = bufferp; - // Is this the end of the solid. - bufferp = strstr(bufferp, "endsolid"); - if (bufferp != NULL) { - solid = 0; + // Read the hole section. + 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 { - // Read the XYZ coordinates if it is a vertex. - bufferp = str; - bufferp = strstr(bufferp, "vertex"); - if (bufferp != NULL) { - plist->newindex((void **) &coord); - for (i = 0; i < 3; i++) { - bufferp = findnextnumber(bufferp); - if (*bufferp == '\0') { - printf("Syntax error reading vertex coords on line %d\n", - line_count); - delete plist; - fclose(fp); + numberofholes = 0; + } + if (numberofholes > 0) { + // Initialize 'holelist'. + holelist = new REAL[numberofholes * 3]; + for (i = 0; i < 3 * numberofholes; i += 3) { + stringptr = readnumberline(inputline, infile, infilename); + stringptr = findnextnumber(stringptr); + if (*stringptr == '\0') { + printf("Error: Hole %d has no x coord.\n", firstnumber + (i / 3)); + break; + } else { + holelist[i] = (REAL)strtod(stringptr, &stringptr); + } + stringptr = findnextnumber(stringptr); + if (*stringptr == '\0') { + printf("Error: Hole %d has no y coord.\n", firstnumber + (i / 3)); + break; + } else { + holelist[i + 1] = (REAL)strtod(stringptr, &stringptr); + } + stringptr = findnextnumber(stringptr); + if (*stringptr == '\0') { + printf("Error: Hole %d has no z coord.\n", firstnumber + (i / 3)); + break; + } else { + holelist[i + 2] = (REAL)strtod(stringptr, &stringptr); + } + } + if (i < 3 * numberofholes) { + // This must be caused by an error. + fclose(infile); return false; - } - coord[i] = (REAL) strtod(bufferp, &bufferp); } - } } - } - } - } // if(!binary) - - else { - rewind(fp); - while(!feof(fp)) { - char header[80]; - if(!fread(header, sizeof(char), 80, fp)) break; - unsigned int nfacets = 0; - size_t ret = fread(&nfacets, sizeof(unsigned int), 1, fp); - bool swap = false; - if(nfacets > 100000000){ - //Msg::Info("Swapping bytes from binary file"); - swap = true; - SwapBytes((char*)&nfacets, sizeof(unsigned int), 1); - } - if(ret && nfacets){ - //points.resize(points.size() + 1); - char *data = new char[nfacets * 50 * sizeof(char)]; - ret = fread(data, sizeof(char), nfacets * 50, fp); - if(ret == nfacets * 50){ - for(unsigned int i = 0; i < nfacets; i++) { - float *xyz = (float *)&data[i * 50 * sizeof(char)]; - if(swap) SwapBytes((char*)xyz, sizeof(float), 12); - for(int j = 0; j < 3; j++){ - //SPoint3 p(xyz[3 + 3 * j], xyz[3 + 3 * j + 1], xyz[3 + 3 * j + 2]); - //points.back().push_back(p); - //bbox += p; - plist->newindex((void **) &coord); - coord[0] = xyz[3 + 3 * j]; - coord[1] = xyz[3 + 3 * j + 1]; - coord[2] = xyz[3 + 3 * j + 2]; + + // 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, infile, NULL); + if (stringptr != (char*)NULL && *stringptr != '\0') { + numberofregions = (int)strtol(stringptr, &stringptr, 0); + } else { + numberofregions = 0; + } + if (numberofregions > 0) { + // Initialize 'regionlist'. + regionlist = new REAL[numberofregions * 5]; + index = 0; + for (i = 0; i < numberofregions; i++) { + stringptr = readnumberline(inputline, infile, infilename); + stringptr = findnextnumber(stringptr); + if (*stringptr == '\0') { + printf("Error: Region %d has no x coordinate.\n", firstnumber + i); + break; + } else { + regionlist[index++] = (REAL)strtod(stringptr, &stringptr); + } + stringptr = findnextnumber(stringptr); + if (*stringptr == '\0') { + printf("Error: Region %d has no y coordinate.\n", firstnumber + i); + break; + } else { + regionlist[index++] = (REAL)strtod(stringptr, &stringptr); + } + stringptr = findnextnumber(stringptr); + if (*stringptr == '\0') { + printf("Error: Region %d has no z coordinate.\n", firstnumber + i); + break; + } else { + regionlist[index++] = (REAL)strtod(stringptr, &stringptr); + } + stringptr = findnextnumber(stringptr); + if (*stringptr == '\0') { + printf("Error: Region %d has no region attrib.\n", firstnumber + i); + break; + } else { + regionlist[index++] = (REAL)strtod(stringptr, &stringptr); + } + stringptr = findnextnumber(stringptr); + if (*stringptr == '\0') { + regionlist[index] = regionlist[index - 1]; + } else { + regionlist[index] = (REAL)strtod(stringptr, &stringptr); + } + index++; + } + if (i < numberofregions) { + // This must be caused by an error. + fclose(infile); + return false; } - } } - delete [] data; - } - } // while (!feof(fp)) - } // binary - - fclose(fp); + } - 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); - delete plist; - return false; - } - numberofpoints = nverts; - pointlist = new REAL[nverts * 3]; - for (i = 0; i < nverts; i++) { - coord = (double *) fastlookup(plist, i); - iverts = i * 3; - pointlist[iverts] = (REAL) coord[0]; - pointlist[iverts + 1] = (REAL) coord[1]; - pointlist[iverts + 2] = (REAL) coord[2]; - } - - nfaces = (int) (nverts / 3); - numberoffacets = nfaces; - facetlist = new tetgenio::facet[nfaces]; - - // Default use '1' as the array starting index. - firstnumber = 1; - iverts = firstnumber; - for (i = 0; i < nfaces; i++) { - f = &facetlist[i]; - init(f); - // In .stl format, each facet has one polygon, no hole. - f->numberofpolygons = 1; - f->polygonlist = new tetgenio::polygon[1]; - p = &f->polygonlist[0]; - init(p); - // Each polygon has three vertices. - p->numberofvertices = 3; - p->vertexlist = new int[p->numberofvertices]; - p->vertexlist[0] = iverts; - p->vertexlist[1] = iverts + 1; - p->vertexlist[2] = iverts + 2; - iverts += 3; - } - - delete plist; - return true; + // End of reading poly/smesh file. + fclose(infile); + return true; } /////////////////////////////////////////////////////////////////////////////// // // -// load_medit() Load a surface mesh from a .mesh file. // +// load_off() Load a polyhedron from a .off file. // // // -// The .mesh format is the file format of Medit, a user-friendly interactive // -// mesh viewer program. // +// The .off format is one of file formats of the Geomview, an interactive // +// program for viewing and manipulating geometric objects. More information // +// is available form: http://www.geomview.org. // // // /////////////////////////////////////////////////////////////////////////////// -bool tetgenio::load_medit(char* filebasename, int istetmesh) -{ - FILE *fp; - tetgenio::facet *tmpflist, *f; - tetgenio::polygon *p; - char infilename[FILENAMESIZE]; - char buffer[INPUTLINESIZE]; - char *bufferp, *str; - double *coord; - int *tmpfmlist; - 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') { - printf("Error: No filename.\n"); - return false; - } - if (strcmp(&infilename[strlen(infilename) - 5], ".mesh") != 0) { - strcat(infilename, ".mesh"); - } - - if (!(fp = fopen(infilename, "r"))) { - printf("Error: Unable to open file %s\n", infilename); - return false; - } - printf("Opening %s.\n", infilename); - - while ((bufferp = readline(buffer, fp, &line_count)) != NULL) { - if (*bufferp == '#') continue; // A comment line is skipped. - if (dimension == 0) { - // Find if it is the keyword "Dimension". - str = strstr(bufferp, "Dimension"); - if (!str) str = strstr(bufferp, "dimension"); - if (!str) str = strstr(bufferp, "DIMENSION"); - if (str) { - // Read the dimensions - bufferp = findnextnumber(str); // Skip field "Dimension". - if (*bufferp == '\0') { - // Read a non-empty line. - bufferp = readline(buffer, fp, &line_count); - } - dimension = (int) strtol(bufferp, &bufferp, 0); - if (dimension != 2 && dimension != 3) { - printf("Unknown dimension in file on line %d in file %s\n", - line_count, infilename); - fclose(fp); - return false; - } - mesh_dim = dimension; - } - } - if (nverts == 0) { - // Find if it is the keyword "Vertices". - str = strstr(bufferp, "Vertices"); - if (!str) str = strstr(bufferp, "vertices"); - if (!str) str = strstr(bufferp, "VERTICES"); - if (str) { - // Read the number of vertices. - bufferp = findnextnumber(str); // Skip field "Vertices". - if (*bufferp == '\0') { - // Read a non-empty line. - 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; - pointlist = new REAL[nverts * 3]; - } - // Read the follwoing node list. - for (i = 0; i < nverts; 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; - } - // Read vertex coordinates - coord = &pointlist[i * 3]; - for (j = 0; j < 3; j++) { - if (*bufferp == '\0') { - printf("Syntax error reading vertex coords on line"); - printf(" %d in file %s\n", line_count, infilename); - fclose(fp); - return false; - } - if ((j < 2) || (dimension == 3)) { - coord[j] = (REAL) strtod(bufferp, &bufferp); - } else { - coord[j] = 0.0; - } - bufferp = findnextnumber(bufferp); - } - } - 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; - str = strstr(bufferp, "Triangles"); - if (!str) str = strstr(bufferp, "triangles"); - if (!str) str = strstr(bufferp, "TRIANGLES"); - if (str) { - corners = 3; - } else { - str = strstr(bufferp, "Quadrilaterals"); - if (!str) str = strstr(bufferp, "quadrilaterals"); - if (!str) str = strstr(bufferp, "QUADRILATERALS"); - if (str) { - corners = 4; - } - } - if (corners == 3 || corners == 4) { - // Read the number of triangles (or quadrilaterals). - bufferp = findnextnumber(str); // Skip field "Triangles". - if (*bufferp == '\0') { - // Read a non-empty line. - bufferp = readline(buffer, fp, &line_count); - } - nfaces = strtol(bufferp, &bufferp, 0); - // Allocate memory for 'tetgenio' - if (nfaces > 0) { - 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]; +bool tetgenio::load_off(char* filebasename) { + FILE* fp; + tetgenio::facet* f; + tetgenio::polygon* p; + char infilename[FILENAMESIZE]; + char buffer[INPUTLINESIZE]; + char* bufferp; + double* coord; + int nverts = 0, iverts = 0; + int nfaces = 0, ifaces = 0; + 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') { + printf("Error: No filename.\n"); + return false; + } + if (strcmp(&infilename[strlen(infilename) - 4], ".off") != 0) { + strcat(infilename, ".off"); + } + + if (!(fp = fopen(infilename, "r"))) { + printf(" Unable to open file %s\n", infilename); + return false; + } + printf("Opening %s.\n", infilename); + + while ((bufferp = readline(buffer, fp, &line_count)) != NULL) { + // Check section + if (nverts == 0) { + // Read header + bufferp = strstr(bufferp, "OFF"); + if (bufferp != NULL) { + // Read mesh counts + bufferp = findnextnumber(bufferp); // Skip field "OFF". + if (*bufferp == '\0') { + // Read a non-empty line. + bufferp = readline(buffer, fp, &line_count); + } + if ((sscanf(bufferp, "%d%d%d", &nverts, &nfaces, &nedges) != 3) || (nverts == 0)) { + printf("Syntax error reading header on line %d in file %s\n", line_count, + infilename); + fclose(fp); + return false; + } + // Allocate memory for 'tetgenio' + if (nverts > 0) { + numberofpoints = nverts; + pointlist = new REAL[nverts * 3]; + smallestidx = nverts + 1; // A bigger enough number. + } + if (nfaces > 0) { + numberoffacets = nfaces; + facetlist = new tetgenio::facet[nfaces]; + } } - } else { - if (corners == 3) { - // It is a surface mesh of a tetrahedral mesh. - numberoftrifaces = nfaces; - trifacelist = new int[nfaces * 3]; - trifacemarkerlist = new int[nfaces]; + } else if (iverts < nverts) { + // Read vertex coordinates + coord = &pointlist[iverts * 3]; + for (i = 0; i < 3; i++) { + if (*bufferp == '\0') { + printf("Syntax error reading vertex coords on line %d in file %s\n", line_count, + infilename); + fclose(fp); + return false; + } + coord[i] = (REAL)strtod(bufferp, &bufferp); + bufferp = findnextnumber(bufferp); } - } - } // if (nfaces > 0) - // Read the following list of faces. - 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; - } - f = &facetlist[i]; - tetgenio::init(f); - // In .mesh format, each facet has one polygon, no hole. + iverts++; + } else if (ifaces < nfaces) { + // Get next face + f = &facetlist[ifaces]; + init(f); + // In .off format, each facet has one polygon, no hole. f->numberofpolygons = 1; f->polygonlist = new tetgenio::polygon[1]; p = &f->polygonlist[0]; - 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); + init(p); + // Read the number of vertices, it should be greater than 0. + p->numberofvertices = (int)strtol(bufferp, &bufferp, 0); + if (p->numberofvertices == 0) { + printf("Syntax error reading polygon 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); } - } - // 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++) { + // Allocate memory for face vertices + p->vertexlist = new int[p->numberofvertices]; + for (i = 0; i < p->numberofvertices; i++) { + bufferp = findnextnumber(bufferp); if (*bufferp == '\0') { - printf("Syntax error reading face on line %d in file %s\n", - line_count, infilename); - fclose(fp); - return false; + printf("Syntax error reading polygon 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]; + p->vertexlist[i] = (int)strtol(bufferp, &bufferp, 0); + // Detect the smallest index. + if (p->vertexlist[i] < smallestidx) { + smallestidx = p->vertexlist[i]; } - 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) + } + ifaces++; + } else { + // Should never get here + printf("Found extra text starting at line %d in file %s\n", line_count, infilename); + break; + } } - } - // Close file - fclose(fp); + // 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; - } + // 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; + } + if (ifaces != nfaces) { + printf("Expected %d faces, but read only %d faces in file %s\n", nfaces, ifaces, + infilename); + return false; + } - return true; + return true; } /////////////////////////////////////////////////////////////////////////////// // // -// load_vtk() Load VTK surface mesh from file (.vtk ascii or binary). // +// load_ply() Load a polyhedron from a .ply file. // // // -// This function is contributed by: Bryn Lloyd, Computer Vision Laboratory, // -// ETH, Zuerich. May 7, 2007. // +// This is a simplified version of reading .ply files, which only reads the // +// set of vertices and the set of faces. Other informations (such as color, // +// material, texture, etc) in .ply file are ignored. Complete routines for // +// reading and writing ,ply files are available from: http://www.cc.gatech. // +// edu/projects/large_models/ply.html. Except the header section, ply file // +// format has exactly the same format for listing vertices and polygons as // +// off file format. // // // /////////////////////////////////////////////////////////////////////////////// -// 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 tetgenio::load_ply(char* filebasename) { + FILE* fp; + tetgenio::facet* f; + tetgenio::polygon* p; + char infilename[FILENAMESIZE]; + char buffer[INPUTLINESIZE]; + char *bufferp, *str; + double* coord; + int endheader = 0, format = 0; + int nverts = 0, iverts = 0; + int nfaces = 0, ifaces = 0; + int line_count = 0, i; -bool testIsBigEndian() -{ - short word = 0x4321; - if((*(char *)& word) != 0x21) - return true; - else - return false; -} + // 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; -bool tetgenio::load_vtk(char* filebasename) -{ - FILE *fp; - tetgenio::facet *f; - tetgenio::polygon *p; - char infilename[FILENAMESIZE]; - char line[INPUTLINESIZE]; - char mode[128], id[256], fmt[64]; - char *bufferp; - double *coord; - float _x, _y, _z; - int nverts = 0; - int nfaces = 0; - int line_count = 0; - int dummy; - int id1, id2, id3; - int nn = -1; - int nn_old = -1; - int i, j; - bool ImALittleEndian = !testIsBigEndian(); - - int smallestidx = 0; - - strncpy(infilename, filebasename, FILENAMESIZE - 1); - infilename[FILENAMESIZE - 1] = '\0'; - if (infilename[0] == '\0') { - printf("Error: No filename.\n"); - return false; - } - if (strcmp(&infilename[strlen(infilename) - 4], ".vtk") != 0) { - strcat(infilename, ".vtk"); - } - if (!(fp = fopen(infilename, "r"))) { - printf("Error: Unable to open file %s\n", infilename); - return false; - } - printf("Opening %s.\n", infilename); - - // Default uses the index starts from '0'. - firstnumber = 0; - strcpy(mode, "BINARY"); - - while((bufferp = readline(line, fp, &line_count)) != NULL) { - if(strlen(line) == 0) continue; - //swallow lines beginning with a comment sign or white space - if(line[0] == '#' || line[0]=='\n' || line[0] == 10 || line[0] == 13 || - line[0] == 32) continue; - - sscanf(line, "%s", id); - if(!strcmp(id, "ASCII")) { - strcpy(mode, "ASCII"); - } - - if(!strcmp(id, "POINTS")) { - sscanf(line, "%s %d %s", id, &nverts, fmt); - if (nverts > 0) { - numberofpoints = nverts; - pointlist = new REAL[nverts * 3]; - smallestidx = nverts + 1; - } - - if(!strcmp(mode, "BINARY")) { - for(i = 0; i < nverts; i++) { - coord = &pointlist[i * 3]; - if(!strcmp(fmt, "double")) { - fread((char*)(&(coord[0])), sizeof(double), 1, fp); - fread((char*)(&(coord[1])), sizeof(double), 1, fp); - fread((char*)(&(coord[2])), sizeof(double), 1, fp); - if(ImALittleEndian){ - swapBytes((unsigned char *) &(coord[0]), sizeof(coord[0])); - swapBytes((unsigned char *) &(coord[1]), sizeof(coord[1])); - swapBytes((unsigned char *) &(coord[2]), sizeof(coord[2])); - } - } else if(!strcmp(fmt, "float")) { - fread((char*)(&_x), sizeof(float), 1, fp); - fread((char*)(&_y), sizeof(float), 1, fp); - fread((char*)(&_z), sizeof(float), 1, fp); - if(ImALittleEndian){ - swapBytes((unsigned char *) &_x, sizeof(_x)); - swapBytes((unsigned char *) &_y, sizeof(_y)); - swapBytes((unsigned char *) &_z, sizeof(_z)); - } - coord[0] = double(_x); - coord[1] = double(_y); - coord[2] = double(_z); - } else { - printf("Error: Only float or double formats are supported!\n"); - return false; - } - } - } else if(!strcmp(mode, "ASCII")) { - for(i = 0; i < nverts; i++){ - bufferp = readline(line, fp, &line_count); - if (bufferp == NULL) { - printf("Unexpected end of file on line %d in file %s\n", - line_count, infilename); - fclose(fp); - return false; - } - // Read vertex coordinates - coord = &pointlist[i * 3]; - for (j = 0; j < 3; j++) { - if (*bufferp == '\0') { - printf("Syntax error reading vertex coords on line"); - printf(" %d in file %s\n", line_count, infilename); - fclose(fp); - return false; - } - coord[j] = (REAL) strtod(bufferp, &bufferp); - bufferp = findnextnumber(bufferp); - } - } - } - continue; + strncpy(infilename, filebasename, FILENAMESIZE - 1); + infilename[FILENAMESIZE - 1] = '\0'; + if (infilename[0] == '\0') { + printf("Error: No filename.\n"); + return false; + } + if (strcmp(&infilename[strlen(infilename) - 4], ".ply") != 0) { + strcat(infilename, ".ply"); } - if(!strcmp(id, "POLYGONS")) { - sscanf(line, "%s %d %d", id, &nfaces, &dummy); - if (nfaces > 0) { - numberoffacets = nfaces; - facetlist = new tetgenio::facet[nfaces]; - } - - if(!strcmp(mode, "BINARY")) { - for(i = 0; i < nfaces; i++){ - fread((char*)(&nn), sizeof(int), 1, fp); - if(ImALittleEndian){ - swapBytes((unsigned char *) &nn, sizeof(nn)); - } - if (i == 0) - nn_old = nn; - if (nn != nn_old) { - printf("Error: No mixed cells are allowed.\n"); - return false; - } + if (!(fp = fopen(infilename, "r"))) { + printf("Error: Unable to open file %s\n", infilename); + return false; + } + printf("Opening %s.\n", infilename); - if(nn == 3){ - fread((char*)(&id1), sizeof(int), 1, fp); - fread((char*)(&id2), sizeof(int), 1, fp); - fread((char*)(&id3), sizeof(int), 1, fp); - if(ImALittleEndian){ - swapBytes((unsigned char *) &id1, sizeof(id1)); - swapBytes((unsigned char *) &id2, sizeof(id2)); - swapBytes((unsigned char *) &id3, sizeof(id3)); + while ((bufferp = readline(buffer, fp, &line_count)) != NULL) { + if (!endheader) { + // Find if it is the keyword "end_header". + str = strstr(bufferp, "end_header"); + // strstr() is case sensitive. + if (!str) str = strstr(bufferp, "End_header"); + if (!str) str = strstr(bufferp, "End_Header"); + if (str) { + // This is the end of the header section. + endheader = 1; + continue; } - f = &facetlist[i]; - init(f); - // In .off format, each facet has one polygon, no hole. - f->numberofpolygons = 1; - f->polygonlist = new tetgenio::polygon[1]; - p = &f->polygonlist[0]; - init(p); - // Set number of vertices - p->numberofvertices = 3; - // Allocate memory for face vertices - p->vertexlist = new int[p->numberofvertices]; - p->vertexlist[0] = id1; - p->vertexlist[1] = id2; - p->vertexlist[2] = id3; - // Detect the smallest index. - for (j = 0; j < 3; j++) { - if (p->vertexlist[j] < smallestidx) { - smallestidx = p->vertexlist[j]; - } + // Parse the number of vertices and the number of faces. + if (nverts == 0 || nfaces == 0) { + // Find if it si the keyword "element". + str = strstr(bufferp, "element"); + if (!str) str = strstr(bufferp, "Element"); + if (str) { + bufferp = findnextfield(str); + if (*bufferp == '\0') { + printf("Syntax error reading element type on line%d in file %s\n", + line_count, infilename); + fclose(fp); + return false; + } + if (nverts == 0) { + // Find if it is the keyword "vertex". + str = strstr(bufferp, "vertex"); + if (!str) str = strstr(bufferp, "Vertex"); + if (str) { + bufferp = findnextnumber(str); + if (*bufferp == '\0') { + printf("Syntax error reading vertex number on line"); + printf(" %d in file %s\n", line_count, infilename); + fclose(fp); + return false; + } + nverts = (int)strtol(bufferp, &bufferp, 0); + // Allocate memory for 'tetgenio' + if (nverts > 0) { + numberofpoints = nverts; + pointlist = new REAL[nverts * 3]; + smallestidx = nverts + 1; // A big enough index. + } + } + } + if (nfaces == 0) { + // Find if it is the keyword "face". + str = strstr(bufferp, "face"); + if (!str) str = strstr(bufferp, "Face"); + if (str) { + bufferp = findnextnumber(str); + if (*bufferp == '\0') { + printf("Syntax error reading face number on line"); + printf(" %d in file %s\n", line_count, infilename); + fclose(fp); + return false; + } + nfaces = (int)strtol(bufferp, &bufferp, 0); + // Allocate memory for 'tetgenio' + if (nfaces > 0) { + numberoffacets = nfaces; + facetlist = new tetgenio::facet[nfaces]; + } + } + } + } // It is not the string "element". } - } else { - printf("Error: Only triangles are supported\n"); - return false; - } - } - } else if(!strcmp(mode, "ASCII")) { - for(i = 0; i < nfaces; i++) { - bufferp = readline(line, fp, &line_count); - nn = (int) strtol(bufferp, &bufferp, 0); - if (i == 0) - nn_old = nn; - if (nn != nn_old) { - printf("Error: No mixed cells are allowed.\n"); - return false; - } - - if (nn == 3) { - bufferp = findnextnumber(bufferp); // Skip the first field. - id1 = (int) strtol(bufferp, &bufferp, 0); - bufferp = findnextnumber(bufferp); - id2 = (int) strtol(bufferp, &bufferp, 0); - bufferp = findnextnumber(bufferp); - id3 = (int) strtol(bufferp, &bufferp, 0); - f = &facetlist[i]; + if (format == 0) { + // Find the keyword "format". + str = strstr(bufferp, "format"); + if (!str) str = strstr(bufferp, "Format"); + if (str) { + format = 1; + bufferp = findnextfield(str); + // Find if it is the string "ascii". + str = strstr(bufferp, "ascii"); + if (!str) str = strstr(bufferp, "ASCII"); + if (!str) { + printf("This routine only reads ascii format of ply files.\n"); + printf("Hint: You can convert the binary to ascii format by\n"); + printf(" using the provided ply tools:\n"); + printf(" ply2ascii < %s > ascii_%s\n", infilename, infilename); + fclose(fp); + return false; + } + } + } + } else if (iverts < nverts) { + // Read vertex coordinates + coord = &pointlist[iverts * 3]; + for (i = 0; i < 3; i++) { + if (*bufferp == '\0') { + printf("Syntax error reading vertex coords on line %d in file %s\n", line_count, + infilename); + fclose(fp); + return false; + } + coord[i] = (REAL)strtod(bufferp, &bufferp); + bufferp = findnextnumber(bufferp); + } + iverts++; + } else if (ifaces < nfaces) { + // Get next face + f = &facetlist[ifaces]; init(f); // In .off format, each facet has one polygon, no hole. f->numberofpolygons = 1; f->polygonlist = new tetgenio::polygon[1]; p = &f->polygonlist[0]; init(p); - // Set number of vertices - p->numberofvertices = 3; + // Read the number of vertices, it should be greater than 0. + p->numberofvertices = (int)strtol(bufferp, &bufferp, 0); + if (p->numberofvertices == 0) { + printf("Syntax error reading polygon on line %d in file %s\n", line_count, + infilename); + fclose(fp); + return false; + } // Allocate memory for face vertices p->vertexlist = new int[p->numberofvertices]; - 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]; - } + for (i = 0; i < p->numberofvertices; i++) { + bufferp = findnextnumber(bufferp); + if (*bufferp == '\0') { + printf("Syntax error reading polygon on line %d in file %s\n", line_count, + infilename); + fclose(fp); + return false; + } + p->vertexlist[i] = (int)strtol(bufferp, &bufferp, 0); + if (p->vertexlist[i] < smallestidx) { + smallestidx = p->vertexlist[i]; + } } - } else { - printf("Error: Only triangles are supported.\n"); - return false; - } + ifaces++; + } else { + // Should never get here + printf("Found extra text starting at line %d in file %s\n", line_count, infilename); + break; } - } + } - fclose(fp); + // Close file + fclose(fp); - // Decide the firstnumber of the index. - if (smallestidx == 0) { - firstnumber = 0; - } else if (smallestidx == 1) { + // 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); + } else { + printf("A wrong smallest index (%d) was detected in file %s\n", smallestidx, infilename); return false; - } - - return true; } - if(!strcmp(id,"LINES") || !strcmp(id,"CELLS")){ - printf("Warning: load_vtk(): cannot read formats LINES, CELLS.\n"); + if (iverts != nverts) { + printf("Expected %d vertices, but read only %d vertices in file %s\n", nverts, iverts, + infilename); + return false; + } + if (ifaces != nfaces) { + printf("Expected %d faces, but read only %d faces in file %s\n", nfaces, ifaces, + infilename); + return false; } - } // while () - return true; + return true; } /////////////////////////////////////////////////////////////////////////////// // // -// load_plc() Load a piecewise linear complex from file(s). // +// load_stl() Load a surface mesh from a .stl file. // +// // +// The .stl or stereolithography format is an ASCII or binary file used in // +// manufacturing. It is a list of the triangular surfaces that describe a // +// computer generated solid model. This is the standard input for most rapid // +// prototyping machines. // +// // +// Comment: A .stl file many contain many duplicated points. They will be // +// unified during the Delaunay tetrahedralization process. // // // /////////////////////////////////////////////////////////////////////////////// -bool tetgenio::load_plc(char* filebasename, int object) -{ - 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; +void SwapBytes(char* array, int size, int n) { + char* x = new char[size]; + for (int i = 0; i < n; i++) { + char* a = &array[i * size]; + memcpy(x, a, size); + for (int c = 0; c < size; c++) + a[size - 1 - c] = x[c]; + } + delete[] x; } -/////////////////////////////////////////////////////////////////////////////// -// // -// load_mesh() Load a tetrahedral mesh from file(s). // -// // -/////////////////////////////////////////////////////////////////////////////// +bool tetgenio::load_stl(char* filebasename) { + FILE* fp; + tetgenmesh::arraypool* plist; + tetgenio::facet* f; + tetgenio::polygon* p; + char infilename[FILENAMESIZE]; + char buffer[INPUTLINESIZE]; + char *bufferp, *str; + double* coord; + int solid = 0; + int nverts = 0, iverts = 0; + int nfaces = 0; + int line_count = 0, i; + + strncpy(infilename, filebasename, FILENAMESIZE - 1); + infilename[FILENAMESIZE - 1] = '\0'; + if (infilename[0] == '\0') { + printf("Error: No filename.\n"); + return false; + } + if (strcmp(&infilename[strlen(infilename) - 4], ".stl") != 0) { + strcat(infilename, ".stl"); + } -bool tetgenio::load_tetmesh(char* filebasename, int object) -{ - bool success; - - if (object == (int) tetgenbehavior::MEDIT) { - success = load_medit(filebasename, 1); - } else if (object == (int) tetgenbehavior::NEU_MESH) { - //success = load_neumesh(filebasename, 1); - } else { - success = load_node(filebasename); - if (success) { - success = load_tet(filebasename); + if (!(fp = fopen(infilename, "rb"))) { + printf("Error: Unable to open file %s\n", infilename); + return false; } - if (success) { - // Try to load the following files (.face, .edge, .vol). - load_face(filebasename); - load_edge(filebasename); - load_vol(filebasename); + printf("Opening %s.\n", infilename); + + // "solid", or binary data header + if (!fgets(buffer, sizeof(buffer), fp)) { + fclose(fp); + return 0; + } + bool binary = strncmp(buffer, "solid", 5) && strncmp(buffer, "SOLID", 5); + + // STL file has no number of points available. Use a list to read points. + plist = new tetgenmesh::arraypool(sizeof(double) * 3, 10); + + if (!binary) { + solid = 1; + while ((bufferp = readline(buffer, fp, &line_count)) != NULL) { + // The ASCII .stl file must start with the lower case keyword solid and + // end with endsolid. + if (solid == 0) { + // Read header + bufferp = strstr(bufferp, "solid"); + if (bufferp != NULL) { + solid = 1; + } + } else { + // We're inside the block of the solid. + str = bufferp; + // Is this the end of the solid. + bufferp = strstr(bufferp, "endsolid"); + if (bufferp != NULL) { + solid = 0; + } else { + // Read the XYZ coordinates if it is a vertex. + bufferp = str; + bufferp = strstr(bufferp, "vertex"); + if (bufferp != NULL) { + plist->newindex((void**)&coord); + for (i = 0; i < 3; i++) { + bufferp = findnextnumber(bufferp); + if (*bufferp == '\0') { + printf("Syntax error reading vertex coords on line %d\n", + line_count); + delete plist; + fclose(fp); + return false; + } + coord[i] = (REAL)strtod(bufferp, &bufferp); + } + } + } + } + } + } // if(!binary) + + else { + rewind(fp); + while (!feof(fp)) { + char header[80]; + if (!fread(header, sizeof(char), 80, fp)) break; + unsigned int nfacets = 0; + size_t ret = fread(&nfacets, sizeof(unsigned int), 1, fp); + bool swap = false; + if (nfacets > 100000000) { + // Msg::Info("Swapping bytes from binary file"); + swap = true; + SwapBytes((char*)&nfacets, sizeof(unsigned int), 1); + } + if (ret && nfacets) { + // points.resize(points.size() + 1); + char* data = new char[nfacets * 50 * sizeof(char)]; + ret = fread(data, sizeof(char), nfacets * 50, fp); + if (ret == nfacets * 50) { + for (unsigned int i = 0; i < nfacets; i++) { + float* xyz = (float*)&data[i * 50 * sizeof(char)]; + if (swap) SwapBytes((char*)xyz, sizeof(float), 12); + for (int j = 0; j < 3; j++) { + // SPoint3 p(xyz[3 + 3 * j], xyz[3 + 3 * j + 1], xyz[3 + 3 * j + 2]); + // points.back().push_back(p); + // bbox += p; + plist->newindex((void**)&coord); + coord[0] = xyz[3 + 3 * j]; + coord[1] = xyz[3 + 3 * j + 1]; + coord[2] = xyz[3 + 3 * j + 2]; + } + } + } + delete[] data; + } + } // while (!feof(fp)) + } // binary + + fclose(fp); + + 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); + delete plist; + return false; + } + numberofpoints = nverts; + pointlist = new REAL[nverts * 3]; + for (i = 0; i < nverts; i++) { + coord = (double*)fastlookup(plist, i); + iverts = i * 3; + pointlist[iverts] = (REAL)coord[0]; + pointlist[iverts + 1] = (REAL)coord[1]; + pointlist[iverts + 2] = (REAL)coord[2]; } - } - //if (success) { - // Try to load the following files (.var, .mtr). - load_var(filebasename); - load_mtr(filebasename); - //} + nfaces = (int)(nverts / 3); + numberoffacets = nfaces; + facetlist = new tetgenio::facet[nfaces]; + + // Default use '1' as the array starting index. + firstnumber = 1; + iverts = firstnumber; + for (i = 0; i < nfaces; i++) { + f = &facetlist[i]; + init(f); + // In .stl format, each facet has one polygon, no hole. + f->numberofpolygons = 1; + f->polygonlist = new tetgenio::polygon[1]; + p = &f->polygonlist[0]; + init(p); + // Each polygon has three vertices. + p->numberofvertices = 3; + p->vertexlist = new int[p->numberofvertices]; + p->vertexlist[0] = iverts; + p->vertexlist[1] = iverts + 1; + p->vertexlist[2] = iverts + 2; + iverts += 3; + } - return success; + delete plist; + return true; } /////////////////////////////////////////////////////////////////////////////// // // -// save_nodes() Save points to a .node file. // +// load_medit() Load a surface mesh from a .mesh file. // +// // +// The .mesh format is the file format of Medit, a user-friendly interactive // +// mesh viewer program. // // // /////////////////////////////////////////////////////////////////////////////// -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]); +bool tetgenio::load_medit(char* filebasename, int istetmesh) { + FILE* fp; + tetgenio::facet *tmpflist, *f; + tetgenio::polygon* p; + char infilename[FILENAMESIZE]; + char buffer[INPUTLINESIZE]; + char *bufferp, *str; + double* coord; + int* tmpfmlist; + 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') { + printf("Error: No filename.\n"); + return false; + } + if (strcmp(&infilename[strlen(infilename) - 5], ".mesh") != 0) { + strcat(infilename, ".mesh"); + } + + if (!(fp = fopen(infilename, "r"))) { + printf("Error: Unable to open file %s\n", infilename); + return false; + } + printf("Opening %s.\n", infilename); + + while ((bufferp = readline(buffer, fp, &line_count)) != NULL) { + if (*bufferp == '#') continue; // A comment line is skipped. + if (dimension == 0) { + // Find if it is the keyword "Dimension". + str = strstr(bufferp, "Dimension"); + if (!str) str = strstr(bufferp, "dimension"); + if (!str) str = strstr(bufferp, "DIMENSION"); + if (str) { + // Read the dimensions + bufferp = findnextnumber(str); // Skip field "Dimension". + if (*bufferp == '\0') { + // Read a non-empty line. + bufferp = readline(buffer, fp, &line_count); + } + dimension = (int)strtol(bufferp, &bufferp, 0); + if (dimension != 2 && dimension != 3) { + printf("Unknown dimension in file on line %d in file %s\n", line_count, + infilename); + fclose(fp); + return false; + } + mesh_dim = dimension; + } + } + if (nverts == 0) { + // Find if it is the keyword "Vertices". + str = strstr(bufferp, "Vertices"); + if (!str) str = strstr(bufferp, "vertices"); + if (!str) str = strstr(bufferp, "VERTICES"); + if (str) { + // Read the number of vertices. + bufferp = findnextnumber(str); // Skip field "Vertices". + if (*bufferp == '\0') { + // Read a non-empty line. + 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; + pointlist = new REAL[nverts * 3]; + } + // Read the follwoing node list. + for (i = 0; i < nverts; 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; + } + // Read vertex coordinates + coord = &pointlist[i * 3]; + for (j = 0; j < 3; j++) { + if (*bufferp == '\0') { + printf("Syntax error reading vertex coords on line"); + printf(" %d in file %s\n", line_count, infilename); + fclose(fp); + return false; + } + if ((j < 2) || (dimension == 3)) { + coord[j] = (REAL)strtod(bufferp, &bufferp); + } else { + coord[j] = 0.0; + } + bufferp = findnextnumber(bufferp); + } + } + 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; + str = strstr(bufferp, "Triangles"); + if (!str) str = strstr(bufferp, "triangles"); + if (!str) str = strstr(bufferp, "TRIANGLES"); + if (str) { + corners = 3; + } else { + str = strstr(bufferp, "Quadrilaterals"); + if (!str) str = strstr(bufferp, "quadrilaterals"); + if (!str) str = strstr(bufferp, "QUADRILATERALS"); + if (str) { + corners = 4; + } + } + if (corners == 3 || corners == 4) { + // Read the number of triangles (or quadrilaterals). + bufferp = findnextnumber(str); // Skip field "Triangles". + if (*bufferp == '\0') { + // Read a non-empty line. + bufferp = readline(buffer, fp, &line_count); + } + nfaces = strtol(bufferp, &bufferp, 0); + // Allocate memory for 'tetgenio' + if (nfaces > 0) { + 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]; + } + } else { + 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. + 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; + } + 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); + } + } + // 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) + } + } + + // Close file + fclose(fp); + + // Decide the firstnumber of the index. + if (smallestidx == 0) { + firstnumber = 0; + } else if (smallestidx == 1) { + firstnumber = 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"); + printf("A wrong smallest index (%d) was detected in file %s\n", smallestidx, infilename); + return false; } - fclose(fout); - } + + return true; } /////////////////////////////////////////////////////////////////////////////// // // -// save_elements() Save elements to a .ele file. // +// load_vtk() Load VTK surface mesh from file (.vtk ascii or binary). // +// // +// This function is contributed by: Bryn Lloyd, Computer Vision Laboratory, // +// ETH, Zuerich. May 7, 2007. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenio::save_elements(char* filebasename) -{ - FILE *fout; - char outelefilename[FILENAMESIZE]; - int i, j; - - sprintf(outelefilename, "%s.ele", filebasename); - printf("Saving elements to %s\n", outelefilename); - fout = fopen(outelefilename, "w"); - if (mesh_dim == 3) { - fprintf(fout, "%d %d %d\n", numberoftetrahedra, numberofcorners, - numberoftetrahedronattributes); - for (i = 0; i < numberoftetrahedra; i++) { - fprintf(fout, "%d", i + firstnumber); - for (j = 0; j < numberofcorners; j++) { - fprintf(fout, " %5d", tetrahedronlist[i * numberofcorners + j]); - } - for (j = 0; j < numberoftetrahedronattributes; j++) { - fprintf(fout, " %g", - tetrahedronattributelist[i * numberoftetrahedronattributes + j]); - } - fprintf(fout, "\n"); - } - } else { - // Save a two-dimensional mesh. - fprintf(fout, "%d %d %d\n",numberoftrifaces,3,trifacemarkerlist ? 1 : 0); - for (i = 0; i < numberoftrifaces; i++) { - fprintf(fout, "%d", i + firstnumber); - for (j = 0; j < 3; j++) { - fprintf(fout, " %5d", trifacelist[i * 3 + j]); - } - if (trifacemarkerlist != NULL) { - fprintf(fout, " %d", trifacemarkerlist[i]); - } - fprintf(fout, "\n"); - } - } - - fclose(fout); +// 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--; + } } -/////////////////////////////////////////////////////////////////////////////// -// // -// save_faces() Save faces to a .face file. // -// // -/////////////////////////////////////////////////////////////////////////////// +bool testIsBigEndian() { + short word = 0x4321; + if ((*(char*)&word) != 0x21) + return true; + else + return false; +} -void tetgenio::save_faces(char* filebasename) -{ - FILE *fout; - char outfacefilename[FILENAMESIZE]; - int i; - - sprintf(outfacefilename, "%s.face", filebasename); - printf("Saving faces to %s\n", outfacefilename); - fout = fopen(outfacefilename, "w"); - fprintf(fout, "%d %d\n", numberoftrifaces, - trifacemarkerlist != NULL ? 1 : 0); - for (i = 0; i < numberoftrifaces; i++) { - fprintf(fout, "%d %5d %5d %5d", i + firstnumber, trifacelist[i * 3], - trifacelist[i * 3 + 1], trifacelist[i * 3 + 2]); - if (trifacemarkerlist != NULL) { - fprintf(fout, " %d", trifacemarkerlist[i]); - } - fprintf(fout, "\n"); - } - - fclose(fout); +bool tetgenio::load_vtk(char* filebasename) { + FILE* fp; + tetgenio::facet* f; + tetgenio::polygon* p; + char infilename[FILENAMESIZE]; + char line[INPUTLINESIZE]; + char mode[128], id[256], fmt[64]; + char* bufferp; + double* coord; + float _x, _y, _z; + int nverts = 0; + int nfaces = 0; + int line_count = 0; + int dummy; + int id1, id2, id3; + int nn = -1; + int nn_old = -1; + int i, j; + bool ImALittleEndian = !testIsBigEndian(); + + int smallestidx = 0; + + strncpy(infilename, filebasename, FILENAMESIZE - 1); + infilename[FILENAMESIZE - 1] = '\0'; + if (infilename[0] == '\0') { + printf("Error: No filename.\n"); + return false; + } + if (strcmp(&infilename[strlen(infilename) - 4], ".vtk") != 0) { + strcat(infilename, ".vtk"); + } + if (!(fp = fopen(infilename, "r"))) { + printf("Error: Unable to open file %s\n", infilename); + return false; + } + printf("Opening %s.\n", infilename); + + // Default uses the index starts from '0'. + firstnumber = 0; + strcpy(mode, "BINARY"); + + while ((bufferp = readline(line, fp, &line_count)) != NULL) { + if (strlen(line) == 0) continue; + // swallow lines beginning with a comment sign or white space + if (line[0] == '#' || line[0] == '\n' || line[0] == 10 || line[0] == 13 || line[0] == 32) + continue; + + sscanf(line, "%s", id); + if (!strcmp(id, "ASCII")) { + strcpy(mode, "ASCII"); + } + + if (!strcmp(id, "POINTS")) { + sscanf(line, "%s %d %s", id, &nverts, fmt); + if (nverts > 0) { + numberofpoints = nverts; + pointlist = new REAL[nverts * 3]; + smallestidx = nverts + 1; + } + + if (!strcmp(mode, "BINARY")) { + for (i = 0; i < nverts; i++) { + coord = &pointlist[i * 3]; + if (!strcmp(fmt, "double")) { + fread((char*)(&(coord[0])), sizeof(double), 1, fp); + fread((char*)(&(coord[1])), sizeof(double), 1, fp); + fread((char*)(&(coord[2])), sizeof(double), 1, fp); + if (ImALittleEndian) { + swapBytes((unsigned char*)&(coord[0]), sizeof(coord[0])); + swapBytes((unsigned char*)&(coord[1]), sizeof(coord[1])); + swapBytes((unsigned char*)&(coord[2]), sizeof(coord[2])); + } + } else if (!strcmp(fmt, "float")) { + fread((char*)(&_x), sizeof(float), 1, fp); + fread((char*)(&_y), sizeof(float), 1, fp); + fread((char*)(&_z), sizeof(float), 1, fp); + if (ImALittleEndian) { + swapBytes((unsigned char*)&_x, sizeof(_x)); + swapBytes((unsigned char*)&_y, sizeof(_y)); + swapBytes((unsigned char*)&_z, sizeof(_z)); + } + coord[0] = double(_x); + coord[1] = double(_y); + coord[2] = double(_z); + } else { + printf("Error: Only float or double formats are supported!\n"); + return false; + } + } + } else if (!strcmp(mode, "ASCII")) { + for (i = 0; i < nverts; i++) { + bufferp = readline(line, fp, &line_count); + if (bufferp == NULL) { + printf("Unexpected end of file on line %d in file %s\n", line_count, + infilename); + fclose(fp); + return false; + } + // Read vertex coordinates + coord = &pointlist[i * 3]; + for (j = 0; j < 3; j++) { + if (*bufferp == '\0') { + printf("Syntax error reading vertex coords on line"); + printf(" %d in file %s\n", line_count, infilename); + fclose(fp); + return false; + } + coord[j] = (REAL)strtod(bufferp, &bufferp); + bufferp = findnextnumber(bufferp); + } + } + } + continue; + } + + if (!strcmp(id, "POLYGONS")) { + sscanf(line, "%s %d %d", id, &nfaces, &dummy); + if (nfaces > 0) { + numberoffacets = nfaces; + facetlist = new tetgenio::facet[nfaces]; + } + + if (!strcmp(mode, "BINARY")) { + for (i = 0; i < nfaces; i++) { + fread((char*)(&nn), sizeof(int), 1, fp); + if (ImALittleEndian) { + swapBytes((unsigned char*)&nn, sizeof(nn)); + } + if (i == 0) nn_old = nn; + if (nn != nn_old) { + printf("Error: No mixed cells are allowed.\n"); + return false; + } + + if (nn == 3) { + fread((char*)(&id1), sizeof(int), 1, fp); + fread((char*)(&id2), sizeof(int), 1, fp); + fread((char*)(&id3), sizeof(int), 1, fp); + if (ImALittleEndian) { + swapBytes((unsigned char*)&id1, sizeof(id1)); + swapBytes((unsigned char*)&id2, sizeof(id2)); + swapBytes((unsigned char*)&id3, sizeof(id3)); + } + f = &facetlist[i]; + init(f); + // In .off format, each facet has one polygon, no hole. + f->numberofpolygons = 1; + f->polygonlist = new tetgenio::polygon[1]; + p = &f->polygonlist[0]; + init(p); + // Set number of vertices + p->numberofvertices = 3; + // Allocate memory for face vertices + p->vertexlist = new int[p->numberofvertices]; + p->vertexlist[0] = id1; + p->vertexlist[1] = id2; + p->vertexlist[2] = id3; + // 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; + } + } + } else if (!strcmp(mode, "ASCII")) { + for (i = 0; i < nfaces; i++) { + bufferp = readline(line, fp, &line_count); + nn = (int)strtol(bufferp, &bufferp, 0); + if (i == 0) nn_old = nn; + if (nn != nn_old) { + printf("Error: No mixed cells are allowed.\n"); + return false; + } + + if (nn == 3) { + bufferp = findnextnumber(bufferp); // Skip the first field. + id1 = (int)strtol(bufferp, &bufferp, 0); + bufferp = findnextnumber(bufferp); + id2 = (int)strtol(bufferp, &bufferp, 0); + bufferp = findnextnumber(bufferp); + id3 = (int)strtol(bufferp, &bufferp, 0); + f = &facetlist[i]; + init(f); + // In .off format, each facet has one polygon, no hole. + f->numberofpolygons = 1; + f->polygonlist = new tetgenio::polygon[1]; + p = &f->polygonlist[0]; + init(p); + // Set number of vertices + p->numberofvertices = 3; + // Allocate memory for face vertices + p->vertexlist = new int[p->numberofvertices]; + p->vertexlist[0] = id1; + p->vertexlist[1] = id2; + p->vertexlist[2] = id3; + // 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; + } + } + } + + 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; + } + + if (!strcmp(id, "LINES") || !strcmp(id, "CELLS")) { + printf("Warning: load_vtk(): cannot read formats LINES, CELLS.\n"); + } + } // while () + + return true; } /////////////////////////////////////////////////////////////////////////////// // // -// save_edges() Save egdes to a .edge file. // +// load_plc() Load a piecewise linear complex from file(s). // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenio::save_edges(char* filebasename) -{ - FILE *fout; - char outedgefilename[FILENAMESIZE]; - int i; - - sprintf(outedgefilename, "%s.edge", filebasename); - printf("Saving edges to %s\n", outedgefilename); - fout = fopen(outedgefilename, "w"); - fprintf(fout, "%d %d\n", numberofedges, edgemarkerlist != NULL ? 1 : 0); - for (i = 0; i < numberofedges; i++) { - fprintf(fout, "%d %4d %4d", i + firstnumber, edgelist[i * 2], - edgelist[i * 2 + 1]); - if (edgemarkerlist != NULL) { - fprintf(fout, " %d", edgemarkerlist[i]); - } - fprintf(fout, "\n"); - } - - fclose(fout); +bool tetgenio::load_plc(char* filebasename, int object) { + 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; } /////////////////////////////////////////////////////////////////////////////// // // -// save_neighbors() Save egdes to a .neigh file. // +// load_mesh() Load a tetrahedral mesh from file(s). // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenio::save_neighbors(char* filebasename) -{ - FILE *fout; - char outneighborfilename[FILENAMESIZE]; - int i; - - sprintf(outneighborfilename, "%s.neigh", filebasename); - printf("Saving neighbors to %s\n", outneighborfilename); - fout = fopen(outneighborfilename, "w"); - fprintf(fout, "%d %d\n", numberoftetrahedra, mesh_dim + 1); - for (i = 0; i < numberoftetrahedra; i++) { - if (mesh_dim == 2) { - fprintf(fout, "%d %5d %5d %5d", i + firstnumber, neighborlist[i * 3], - neighborlist[i * 3 + 1], neighborlist[i * 3 + 2]); +bool tetgenio::load_tetmesh(char* filebasename, int object) { + bool success; + + if (object == (int)tetgenbehavior::MEDIT) { + success = load_medit(filebasename, 1); + } else if (object == (int)tetgenbehavior::NEU_MESH) { + // success = load_neumesh(filebasename, 1); } else { - fprintf(fout, "%d %5d %5d %5d %5d", i + firstnumber, - neighborlist[i * 4], neighborlist[i * 4 + 1], - neighborlist[i * 4 + 2], neighborlist[i * 4 + 3]); + 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); + } } - fprintf(fout, "\n"); - } - fclose(fout); + // if (success) { + // Try to load the following files (.var, .mtr). + load_var(filebasename); + load_mtr(filebasename); + //} + + return success; } /////////////////////////////////////////////////////////////////////////////// // // -// save_poly() Save segments or facets to a .poly file. // -// // -// It only save the facets, holes and regions. No .node file is saved. // +// save_nodes() Save points to a .node file. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenio::save_poly(char* filebasename) -{ - FILE *fout; - facet *f; - polygon *p; - char outpolyfilename[FILENAMESIZE]; - int i, j, k; - - sprintf(outpolyfilename, "%s.poly", filebasename); - printf("Saving poly to %s\n", outpolyfilename); - fout = fopen(outpolyfilename, "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); - - // Save segments or facets. - if (mesh_dim == 2) { - // Number of segments, number of boundary markers (zero or one). - fprintf(fout, "%d %d\n", numberofedges, edgemarkerlist != NULL ? 1 : 0); - for (i = 0; i < numberofedges; i++) { - fprintf(fout, "%d %4d %4d", i + firstnumber, edgelist[i * 2], - edgelist[i * 2 + 1]); - if (edgemarkerlist != NULL) { - fprintf(fout, " %d", edgemarkerlist[i]); - } - fprintf(fout, "\n"); - } - } else { - // Number of facets, number of boundary markers (zero or one). - fprintf(fout, "%d %d\n", numberoffacets, facetmarkerlist != NULL ? 1 : 0); - for (i = 0; i < numberoffacets; i++) { - f = &(facetlist[i]); - fprintf(fout, "%d %d %d # %d\n", f->numberofpolygons,f->numberofholes, - facetmarkerlist != NULL ? facetmarkerlist[i] : 0, i + firstnumber); - // Output polygons of this facet. - for (j = 0; j < f->numberofpolygons; j++) { - p = &(f->polygonlist[j]); - fprintf(fout, "%d ", p->numberofvertices); - for (k = 0; k < p->numberofvertices; k++) { - if (((k + 1) % 10) == 0) { - fprintf(fout, "\n "); - } - fprintf(fout, " %d", p->vertexlist[k]); +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"); - } - // Output holes of this facet. - for (j = 0; j < f->numberofholes; j++) { - fprintf(fout, "%d %.12g %.12g %.12g\n", j + firstnumber, - f->holelist[j * 3], f->holelist[j * 3 + 1], f->holelist[j * 3 + 2]); - } - } - } - - // Save holes. - fprintf(fout, "%d\n", numberofholes); - for (i = 0; i < numberofholes; i++) { - // Output x, y coordinates. - fprintf(fout, "%d %.12g %.12g", i + firstnumber, holelist[i * mesh_dim], - holelist[i * mesh_dim + 1]); - if (mesh_dim == 3) { - // Output z coordinate. - fprintf(fout, " %.12g", holelist[i * mesh_dim + 2]); } - fprintf(fout, "\n"); - } + fclose(fout); - // Save regions. - fprintf(fout, "%d\n", numberofregions); - for (i = 0; i < numberofregions; i++) { - if (mesh_dim == 2) { - // Output the index, x, y coordinates, attribute (region number) - // and maximum area constraint (maybe -1). - fprintf(fout, "%d %.12g %.12g %.12g %.12g\n", i + firstnumber, - regionlist[i * 4], regionlist[i * 4 + 1], - regionlist[i * 4 + 2], regionlist[i * 4 + 3]); - } else { - // Output the index, x, y, z coordinates, attribute (region number) - // and maximum volume constraint (maybe -1). - fprintf(fout, "%d %.12g %.12g %.12g %.12g %.12g\n", i + firstnumber, - regionlist[i * 5], regionlist[i * 5 + 1], - regionlist[i * 5 + 2], regionlist[i * 5 + 3], - regionlist[i * 5 + 4]); + // 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(fout); } /////////////////////////////////////////////////////////////////////////////// // // -// save_faces2smesh() Save triangular faces to a .smesh file. // +// save_elements() Save elements to a .ele file. // // // -// It only save the facets. No holes and regions. No .node file. // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenio::save_elements(char* filebasename) { + FILE* fout; + char outelefilename[FILENAMESIZE]; + int i, j; + + sprintf(outelefilename, "%s.ele", filebasename); + printf("Saving elements to %s\n", outelefilename); + fout = fopen(outelefilename, "w"); + if (mesh_dim == 3) { + fprintf(fout, "%d %d %d\n", numberoftetrahedra, numberofcorners, + numberoftetrahedronattributes); + for (i = 0; i < numberoftetrahedra; i++) { + fprintf(fout, "%d", i + firstnumber); + for (j = 0; j < numberofcorners; j++) { + fprintf(fout, " %5d", tetrahedronlist[i * numberofcorners + j]); + } + for (j = 0; j < numberoftetrahedronattributes; j++) { + fprintf(fout, " %g", + tetrahedronattributelist[i * numberoftetrahedronattributes + j]); + } + fprintf(fout, "\n"); + } + } else { + // Save a two-dimensional mesh. + fprintf(fout, "%d %d %d\n", numberoftrifaces, 3, trifacemarkerlist ? 1 : 0); + for (i = 0; i < numberoftrifaces; i++) { + fprintf(fout, "%d", i + firstnumber); + for (j = 0; j < 3; j++) { + fprintf(fout, " %5d", trifacelist[i * 3 + j]); + } + if (trifacemarkerlist != NULL) { + fprintf(fout, " %d", trifacemarkerlist[i]); + } + fprintf(fout, "\n"); + } + } + + fclose(fout); +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// save_faces() Save faces to a .face 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); +void tetgenio::save_faces(char* filebasename) { + FILE* fout; + char outfacefilename[FILENAMESIZE]; + int i; + + sprintf(outfacefilename, "%s.face", filebasename); + printf("Saving faces to %s\n", outfacefilename); + fout = fopen(outfacefilename, "w"); + fprintf(fout, "%d %d\n", numberoftrifaces, trifacemarkerlist != NULL ? 1 : 0); + for (i = 0; i < numberoftrifaces; i++) { + fprintf(fout, "%d %5d %5d %5d", i + firstnumber, trifacelist[i * 3], + trifacelist[i * 3 + 1], trifacelist[i * 3 + 2]); + if (trifacemarkerlist != NULL) { + fprintf(fout, " %d", trifacemarkerlist[i]); + } + fprintf(fout, "\n"); + } + + fclose(fout); +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// save_edges() Save egdes to a .edge file. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenio::save_edges(char* filebasename) { + FILE* fout; + char outedgefilename[FILENAMESIZE]; + int i; + + sprintf(outedgefilename, "%s.edge", filebasename); + printf("Saving edges to %s\n", outedgefilename); + fout = fopen(outedgefilename, "w"); + fprintf(fout, "%d %d\n", numberofedges, edgemarkerlist != NULL ? 1 : 0); + for (i = 0; i < numberofedges; i++) { + fprintf(fout, "%d %4d %4d", i + firstnumber, edgelist[i * 2], edgelist[i * 2 + 1]); + if (edgemarkerlist != NULL) { + fprintf(fout, " %d", edgemarkerlist[i]); + } + fprintf(fout, "\n"); + } + + fclose(fout); +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// save_neighbors() Save egdes to a .neigh file. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenio::save_neighbors(char* filebasename) { + FILE* fout; + char outneighborfilename[FILENAMESIZE]; + int i; + + sprintf(outneighborfilename, "%s.neigh", filebasename); + printf("Saving neighbors to %s\n", outneighborfilename); + fout = fopen(outneighborfilename, "w"); + fprintf(fout, "%d %d\n", numberoftetrahedra, mesh_dim + 1); + for (i = 0; i < numberoftetrahedra; i++) { + if (mesh_dim == 2) { + fprintf(fout, "%d %5d %5d %5d", i + firstnumber, neighborlist[i * 3], + neighborlist[i * 3 + 1], neighborlist[i * 3 + 2]); + } else { + fprintf(fout, "%d %5d %5d %5d %5d", i + firstnumber, neighborlist[i * 4], + neighborlist[i * 4 + 1], neighborlist[i * 4 + 2], neighborlist[i * 4 + 3]); + } + fprintf(fout, "\n"); + } + + fclose(fout); +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// save_poly() Save segments or facets to a .poly file. // +// // +// It only save the facets, holes and regions. No .node file is saved. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenio::save_poly(char* filebasename) { + FILE* fout; + facet* f; + polygon* p; + char outpolyfilename[FILENAMESIZE]; + int i, j, k; + + sprintf(outpolyfilename, "%s.poly", filebasename); + printf("Saving poly to %s\n", outpolyfilename); + fout = fopen(outpolyfilename, "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); + + // Save segments or facets. + if (mesh_dim == 2) { + // Number of segments, number of boundary markers (zero or one). + fprintf(fout, "%d %d\n", numberofedges, edgemarkerlist != NULL ? 1 : 0); + for (i = 0; i < numberofedges; i++) { + fprintf(fout, "%d %4d %4d", i + firstnumber, edgelist[i * 2], edgelist[i * 2 + 1]); + if (edgemarkerlist != NULL) { + fprintf(fout, " %d", edgemarkerlist[i]); + } + fprintf(fout, "\n"); + } + } else { + // Number of facets, number of boundary markers (zero or one). + fprintf(fout, "%d %d\n", numberoffacets, facetmarkerlist != NULL ? 1 : 0); + for (i = 0; i < numberoffacets; i++) { + f = &(facetlist[i]); + fprintf(fout, "%d %d %d # %d\n", f->numberofpolygons, f->numberofholes, + facetmarkerlist != NULL ? facetmarkerlist[i] : 0, i + firstnumber); + // Output polygons of this facet. + for (j = 0; j < f->numberofpolygons; j++) { + p = &(f->polygonlist[j]); + fprintf(fout, "%d ", p->numberofvertices); + for (k = 0; k < p->numberofvertices; k++) { + if (((k + 1) % 10) == 0) { + fprintf(fout, "\n "); + } + fprintf(fout, " %d", p->vertexlist[k]); + } + fprintf(fout, "\n"); + } + // Output holes of this facet. + for (j = 0; j < f->numberofholes; j++) { + fprintf(fout, "%d %.12g %.12g %.12g\n", j + firstnumber, f->holelist[j * 3], + f->holelist[j * 3 + 1], f->holelist[j * 3 + 2]); + } + } + } + + // Save holes. + fprintf(fout, "%d\n", numberofholes); + for (i = 0; i < numberofholes; i++) { + // Output x, y coordinates. + fprintf(fout, "%d %.12g %.12g", i + firstnumber, holelist[i * mesh_dim], + holelist[i * mesh_dim + 1]); + if (mesh_dim == 3) { + // Output z coordinate. + fprintf(fout, " %.12g", holelist[i * mesh_dim + 2]); + } + fprintf(fout, "\n"); + } + + // Save regions. + fprintf(fout, "%d\n", numberofregions); + for (i = 0; i < numberofregions; i++) { + if (mesh_dim == 2) { + // Output the index, x, y coordinates, attribute (region number) + // and maximum area constraint (maybe -1). + fprintf(fout, "%d %.12g %.12g %.12g %.12g\n", i + firstnumber, regionlist[i * 4], + regionlist[i * 4 + 1], regionlist[i * 4 + 2], regionlist[i * 4 + 3]); + } else { + // Output the index, x, y, z coordinates, attribute (region number) + // and maximum volume constraint (maybe -1). + fprintf(fout, "%d %.12g %.12g %.12g %.12g %.12g\n", i + firstnumber, + regionlist[i * 5], regionlist[i * 5 + 1], regionlist[i * 5 + 2], + regionlist[i * 5 + 3], regionlist[i * 5 + 4]); + } + } + + 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); } /////////////////////////////////////////////////////////////////////////////// @@ -2792,22 +2741,22 @@ void tetgenio::save_faces2smesh(char* filebasename) // // /////////////////////////////////////////////////////////////////////////////// -char* tetgenio::readline(char *string, FILE *infile, int *linenumber) -{ - char *result; - - // Search for a non-empty line. - do { - result = fgets(string, INPUTLINESIZE - 1, infile); - if (linenumber) (*linenumber)++; - if (result == (char *) NULL) { - return (char *) NULL; - } - // Skip white spaces. - while ((*result == ' ') || (*result == '\t')) result++; - // If it's end of line, read another line and try again. - } while ((*result == '\0') || (*result == '\r') || (*result == '\n')); - return result; +char* tetgenio::readline(char* string, FILE* infile, int* linenumber) { + char* result; + + // Search for a non-empty line. + do { + result = fgets(string, INPUTLINESIZE - 1, infile); + if (linenumber) (*linenumber)++; + if (result == (char*)NULL) { + return (char*)NULL; + } + // Skip white spaces. + while ((*result == ' ') || (*result == '\t')) + result++; + // If it's end of line, read another line and try again. + } while ((*result == '\0') || (*result == '\r') || (*result == '\n')); + return result; } /////////////////////////////////////////////////////////////////////////////// @@ -2819,23 +2768,21 @@ char* tetgenio::readline(char *string, FILE *infile, int *linenumber) // // /////////////////////////////////////////////////////////////////////////////// -char* tetgenio::findnextfield(char *string) -{ - char *result; - - result = string; - // Skip the current field. Stop upon reaching whitespace or a comma. - while ((*result != '\0') && (*result != ' ') && (*result != '\t') && - (*result != ',') && (*result != ';')) { - result++; - } - // Now skip the whitespace or the comma, stop at anything else that looks - // like a character, or the end of a line. - while ((*result == ' ') || (*result == '\t') || (*result == ',') || - (*result == ';')) { - result++; - } - return result; +char* tetgenio::findnextfield(char* string) { + char* result; + + result = string; + // Skip the current field. Stop upon reaching whitespace or a comma. + while ((*result != '\0') && (*result != ' ') && (*result != '\t') && (*result != ',') && + (*result != ';')) { + result++; + } + // Now skip the whitespace or the comma, stop at anything else that looks + // like a character, or the end of a line. + while ((*result == ' ') || (*result == '\t') || (*result == ',') || (*result == ';')) { + result++; + } + return result; } /////////////////////////////////////////////////////////////////////////////// @@ -2847,26 +2794,24 @@ char* tetgenio::findnextfield(char *string) // // /////////////////////////////////////////////////////////////////////////////// -char* tetgenio::readnumberline(char *string, FILE *infile, char *infilename) -{ - char *result; - - // Search for something that looks like a number. - do { - result = fgets(string, INPUTLINESIZE, infile); - if (result == (char *) NULL) { - return result; - } - // Skip anything that doesn't look like a number, a comment, - // or the end of a line. - while ((*result != '\0') && (*result != '#') - && (*result != '.') && (*result != '+') && (*result != '-') - && ((*result < '0') || (*result > '9'))) { - result++; - } - // If it's a comment or end of line, read another line and try again. - } while ((*result == '#') || (*result == '\0')); - return result; +char* tetgenio::readnumberline(char* string, FILE* infile, char* infilename) { + char* result; + + // Search for something that looks like a number. + do { + result = fgets(string, INPUTLINESIZE, infile); + if (result == (char*)NULL) { + return result; + } + // Skip anything that doesn't look like a number, a comment, + // or the end of a line. + while ((*result != '\0') && (*result != '#') && (*result != '.') && (*result != '+') && + (*result != '-') && ((*result < '0') || (*result > '9'))) { + result++; + } + // If it's a comment or end of line, read another line and try again. + } while ((*result == '#') || (*result == '\0')); + return result; } /////////////////////////////////////////////////////////////////////////////// @@ -2879,35 +2824,32 @@ char* tetgenio::readnumberline(char *string, FILE *infile, char *infilename) // // /////////////////////////////////////////////////////////////////////////////// -char* tetgenio::findnextnumber(char *string) -{ - char *result; - - result = string; - // Skip the current field. Stop upon reaching whitespace or a comma. - while ((*result != '\0') && (*result != '#') && (*result != ' ') && - (*result != '\t') && (*result != ',')) { - result++; - } - // Now skip the whitespace and anything else that doesn't look like a - // number, a comment, or the end of a line. - while ((*result != '\0') && (*result != '#') - && (*result != '.') && (*result != '+') && (*result != '-') - && ((*result < '0') || (*result > '9'))) { - result++; - } - // Check for a comment (prefixed with `#'). - if (*result == '#') { - *result = '\0'; - } - return result; +char* tetgenio::findnextnumber(char* string) { + char* result; + + result = string; + // Skip the current field. Stop upon reaching whitespace or a comma. + while ((*result != '\0') && (*result != '#') && (*result != ' ') && (*result != '\t') && + (*result != ',')) { + result++; + } + // Now skip the whitespace and anything else that doesn't look like a + // number, a comment, or the end of a line. + while ((*result != '\0') && (*result != '#') && (*result != '.') && (*result != '+') && + (*result != '-') && ((*result < '0') || (*result > '9'))) { + result++; + } + // Check for a comment (prefixed with `#'). + if (*result == '#') { + *result = '\0'; + } + return result; } //// //// //// //// //// io_cxx /////////////////////////////////////////////////////////////////// - //// behavior_cxx ///////////////////////////////////////////////////////////// //// //// //// //// @@ -2918,43 +2860,42 @@ char* tetgenio::findnextnumber(char *string) // // /////////////////////////////////////////////////////////////////////////////// -void tetgenbehavior::syntax() -{ - 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(" -R Mesh coarsening (to reduce the mesh elements).\n"); - printf(" -A Assigns attributes to tetrahedra in different regions.\n"); - printf(" -a Applies a maximum tetrahedron volume constraint.\n"); - printf(" -m Applies a mesh sizing function.\n"); - printf(" -i Inserts a list of additional points.\n"); - printf(" -O Specifies the level of mesh optimization.\n"); - printf(" -S Specifies maximum number of added points.\n"); - printf(" -T Sets a tolerance for coplanar test (default 1e-8).\n"); - printf(" -X Suppresses use of exact arithmetic.\n"); - printf(" -M No merge of coplanar facets or very close vertices.\n"); - printf(" -w Generates weighted Delaunay (regular) triangulation.\n"); - printf(" -c Retains the convex hull of the PLC.\n"); - printf(" -d Detects self-intersections of facets of the PLC.\n"); - printf(" -z Numbers all output items starting from zero.\n"); - printf(" -f Outputs all faces to .face file.\n"); - 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(" -k Outputs mesh to .vtk file for viewing by Paraview.\n"); - printf(" -J No jettison of unused vertices from output .node file.\n"); - printf(" -B Suppresses output of boundary information.\n"); - printf(" -N Suppresses output of .node file.\n"); - printf(" -E Suppresses output of .ele file.\n"); - printf(" -F Suppresses output of .face and .edge file.\n"); - printf(" -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"); - printf(" -V Verbose: Detailed information, more terminal output.\n"); - printf(" -h Help: A brief instruction for using TetGen.\n"); +void tetgenbehavior::syntax() { + 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(" -R Mesh coarsening (to reduce the mesh elements).\n"); + printf(" -A Assigns attributes to tetrahedra in different regions.\n"); + printf(" -a Applies a maximum tetrahedron volume constraint.\n"); + printf(" -m Applies a mesh sizing function.\n"); + printf(" -i Inserts a list of additional points.\n"); + printf(" -O Specifies the level of mesh optimization.\n"); + printf(" -S Specifies maximum number of added points.\n"); + printf(" -T Sets a tolerance for coplanar test (default 1e-8).\n"); + printf(" -X Suppresses use of exact arithmetic.\n"); + printf(" -M No merge of coplanar facets or very close vertices.\n"); + printf(" -w Generates weighted Delaunay (regular) triangulation.\n"); + printf(" -c Retains the convex hull of the PLC.\n"); + printf(" -d Detects self-intersections of facets of the PLC.\n"); + printf(" -z Numbers all output items starting from zero.\n"); + printf(" -f Outputs all faces to .face file.\n"); + 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(" -k Outputs mesh to .vtk file for viewing by Paraview.\n"); + printf(" -J No jettison of unused vertices from output .node file.\n"); + printf(" -B Suppresses output of boundary information.\n"); + printf(" -N Suppresses output of .node file.\n"); + printf(" -E Suppresses output of .ele file.\n"); + printf(" -F Suppresses output of .face and .edge file.\n"); + printf(" -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"); + printf(" -V Verbose: Detailed information, more terminal output.\n"); + printf(" -h Help: A brief instruction for using TetGen.\n"); } /////////////////////////////////////////////////////////////////////////////// @@ -2963,58 +2904,57 @@ void tetgenbehavior::syntax() // // /////////////////////////////////////////////////////////////////////////////// -void tetgenbehavior::usage() -{ - printf("TetGen\n"); - printf("A Quality Tetrahedral Mesh Generator and 3D Delaunay "); - printf("Triangulator\n"); - printf("Version 1.5\n"); - printf("August 18, 2018\n"); - printf("\n"); - printf("Copyright (C) 2002 - 2018\n"); - printf("\n"); - printf("What Can TetGen Do?\n"); - printf("\n"); - printf(" TetGen generates Delaunay tetrahedralizations, constrained\n"); - printf(" Delaunay tetrahedralizations, and quality tetrahedral meshes.\n"); - printf("\n"); - printf("Command Line Syntax:\n"); - printf("\n"); - 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 "); - printf("switch\n"); - printf(" and its numeric parameter. \'input_file\' contains input data\n"); - printf(" depending on the switches you supplied which may be a "); - printf(" piecewise\n"); - printf(" linear complex or a list of nodes. File formats and detailed\n"); - printf(" description of command line switches are found in user's "); - printf("manual.\n"); - printf("\n"); - syntax(); - printf("\n"); - printf("Examples of How to Use TetGen:\n"); - printf("\n"); - printf(" \'tetgen object\' reads vertices from object.node, and writes "); - printf("their\n Delaunay tetrahedralization to object.1.node, "); - printf("object.1.ele\n (tetrahedra), and object.1.face"); - printf(" (convex hull faces).\n"); - printf("\n"); - printf(" \'tetgen -p object\' reads a PLC from object.poly or object."); - printf("smesh (and\n possibly object.node) and writes its constrained "); - printf("Delaunay\n tetrahedralization to object.1.node, object.1.ele, "); - printf("object.1.face,\n"); - printf(" (boundary faces) and object.1.edge (boundary edges).\n"); - printf("\n"); - printf(" \'tetgen -pq1.414a.1 object\' reads a PLC from object.poly or\n"); - printf(" object.smesh (and possibly object.node), generates a mesh "); - printf("whose\n tetrahedra have radius-edge ratio smaller than 1.414 and "); - printf("have volume\n of 0.1 or less, and writes the mesh to "); - printf("object.1.node, object.1.ele,\n object.1.face, and object.1.edge\n"); - printf("\n"); - printf("Please send bugs/comments to Hang Si \n"); - terminatetetgen(NULL, 0); +void tetgenbehavior::usage() { + printf("TetGen\n"); + printf("A Quality Tetrahedral Mesh Generator and 3D Delaunay "); + printf("Triangulator\n"); + printf("Version 1.5\n"); + printf("August 18, 2018\n"); + printf("\n"); + printf("Copyright (C) 2002 - 2018\n"); + printf("\n"); + printf("What Can TetGen Do?\n"); + printf("\n"); + printf(" TetGen generates Delaunay tetrahedralizations, constrained\n"); + printf(" Delaunay tetrahedralizations, and quality tetrahedral meshes.\n"); + printf("\n"); + printf("Command Line Syntax:\n"); + printf("\n"); + 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 "); + printf("switch\n"); + printf(" and its numeric parameter. \'input_file\' contains input data\n"); + printf(" depending on the switches you supplied which may be a "); + printf(" piecewise\n"); + printf(" linear complex or a list of nodes. File formats and detailed\n"); + printf(" description of command line switches are found in user's "); + printf("manual.\n"); + printf("\n"); + syntax(); + printf("\n"); + printf("Examples of How to Use TetGen:\n"); + printf("\n"); + printf(" \'tetgen object\' reads vertices from object.node, and writes "); + printf("their\n Delaunay tetrahedralization to object.1.node, "); + printf("object.1.ele\n (tetrahedra), and object.1.face"); + printf(" (convex hull faces).\n"); + printf("\n"); + printf(" \'tetgen -p object\' reads a PLC from object.poly or object."); + printf("smesh (and\n possibly object.node) and writes its constrained "); + printf("Delaunay\n tetrahedralization to object.1.node, object.1.ele, "); + printf("object.1.face,\n"); + printf(" (boundary faces) and object.1.edge (boundary edges).\n"); + printf("\n"); + printf(" \'tetgen -pq1.414a.1 object\' reads a PLC from object.poly or\n"); + printf(" object.smesh (and possibly object.node), generates a mesh "); + printf("whose\n tetrahedra have radius-edge ratio smaller than 1.414 and "); + printf("have volume\n of 0.1 or less, and writes the mesh to "); + printf("object.1.node, object.1.ele,\n object.1.face, and object.1.edge\n"); + printf("\n"); + printf("Please send bugs/comments to Hang Si \n"); + terminatetetgen(NULL, 0); } /////////////////////////////////////////////////////////////////////////////// @@ -3028,590 +2968,589 @@ void tetgenbehavior::usage() // // /////////////////////////////////////////////////////////////////////////////// -bool tetgenbehavior::parse_commandline(int argc, char **argv) -{ - int startindex; - int increment; - int meshnumber; - int i, j, k; - char workstring[1024]; - - // First determine the input style of the switches. - if (argc == 0) { - startindex = 0; // Switches are given without a dash. - argc = 1; // For running the following for-loop once. - commandline[0] = '\0'; - } else { - startindex = 1; - strcpy(commandline, argv[0]); - strcat(commandline, " "); - } - - for (i = startindex; i < argc; i++) { - // Remember the command line for output. - strcat(commandline, argv[i]); - strcat(commandline, " "); - if (startindex == 1) { - // Is this string a filename? - if (argv[i][0] != '-') { - strncpy(infilename, argv[i], 1024 - 1); - infilename[1024 - 1] = '\0'; - continue; - } - } - // Parse the individual switch from the string. - for (j = startindex; argv[i][j] != '\0'; j++) { - if (argv[i][j] == 'p') { +bool tetgenbehavior::parse_commandline(int argc, char** argv) { + int startindex; + int increment; + int meshnumber; + int i, j, k; + char workstring[1024]; + + // First determine the input style of the switches. + if (argc == 0) { + startindex = 0; // Switches are given without a dash. + argc = 1; // For running the following for-loop once. + commandline[0] = '\0'; + } else { + startindex = 1; + strcpy(commandline, argv[0]); + strcat(commandline, " "); + } + + for (i = startindex; i < argc; i++) { + // Remember the command line for output. + strcat(commandline, argv[i]); + strcat(commandline, " "); + if (startindex == 1) { + // Is this string a filename? + if (argv[i][0] != '-') { + strncpy(infilename, argv[i], 1024 - 1); + infilename[1024 - 1] = '\0'; + continue; + } + } + // Parse the individual switch from the string. + for (j = startindex; argv[i][j] != '\0'; j++) { + if (argv[i][j] == 'p') { + plc = 1; + if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || + (argv[i][j + 1] == '.')) { + k = 0; + while (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || + (argv[i][j + 1] == '.')) { + j++; + workstring[k] = argv[i][j]; + k++; + } + workstring[k] = '\0'; + facet_separate_ang_tol = (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] == '.') || (argv[i][j + 1] == 'e') || + (argv[i][j + 1] == '-') || (argv[i][j + 1] == '+')) { + j++; + workstring[k] = argv[i][j]; + k++; + } + workstring[k] = '\0'; + facet_overlap_ang_tol = (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'; + facet_small_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_nomerge = (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')) { + supsteiner_level = (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] == '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; + 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'; + 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] == '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; + 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_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') { + cdtrefine = 1; + if (argv[i][j + 1] == 'l') { + use_equatorial_lens = 1; + } else 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') { + if (argv[i][j + 1] == '1') { // -z1 + reversetetori = 1; + j++; + } else { + zeroindex = 1; // -z + } + } else if (argv[i][j] == 'f') { + facesout++; + } else if (argv[i][j] == 'e') { + edgesout++; + } else if (argv[i][j] == 'n') { + neighout++; + } else if (argv[i][j] == 'v') { + voroout = 1; + } else if (argv[i][j] == 'g') { + meditview = 1; + } else if (argv[i][j] == 'k') { + vtkview = 1; + } else if (argv[i][j] == 'J') { + nojettison = 1; + } else if (argv[i][j] == 'B') { + nobound = 1; + } else if (argv[i][j] == 'N') { + nonodewritten = 1; + } else if (argv[i][j] == 'E') { + noelewritten = 1; + } else if (argv[i][j] == 'F') { + nofacewritten = 1; + } else if (argv[i][j] == 'I') { + noiterationnum = 1; + } else if (argv[i][j] == 'S') { + if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || + (argv[i][j + 1] == '.')) { + k = 0; + while (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || + (argv[i][j + 1] == '.') || (argv[i][j + 1] == 'e') || + (argv[i][j + 1] == '-') || (argv[i][j + 1] == '+')) { + j++; + workstring[k] = argv[i][j]; + k++; + } + workstring[k] = '\0'; + 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; + 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'; + epsilon = (REAL)strtod(workstring, (char**)NULL); + } + } else if (argv[i][j] == 'C') { + docheck++; + } else if (argv[i][j] == 'Q') { + quiet = 1; + } else if (argv[i][j] == 'V') { + verbose++; + } else if (argv[i][j] == 'x') { + if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || + (argv[i][j + 1] == '.')) { + k = 0; + while (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || + (argv[i][j + 1] == '.') || (argv[i][j + 1] == 'e') || + (argv[i][j + 1] == '-') || (argv[i][j + 1] == '+')) { + j++; + workstring[k] = argv[i][j]; + k++; + } + workstring[k] = '\0'; + 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') { + if (argv[i + 1][0] != '-') { + hole_mesh = 1; + // It is a filename following by -H + strncpy(hole_mesh_filename, argv[i + 1], 1024 - 1); + hole_mesh_filename[1024 - 1] = '\0'; + i++; // Skip the next string. + break; // j + } + } else if (argv[i][j] == 'K') { + apply_flow_bc = 1; + } else if ((argv[i][j] == 'h') || // (argv[i][j] == 'H') + (argv[i][j] == '?')) { + usage(); + } else { + printf("Warning: Unknown switch -%c.\n", argv[i][j]); + } + } + } + + if (startindex == 0) { + // Set a temporary filename for debugging output. + strcpy(infilename, "tetgen-tmpfile"); + } else { + if (infilename[0] == '\0') { + // No input file name. Print the syntax and exit. + syntax(); + terminatetetgen(NULL, 0); + } + // Recognize the object from file extension if it is available. + if (!strcmp(&infilename[strlen(infilename) - 5], ".node")) { + infilename[strlen(infilename) - 5] = '\0'; + object = NODES; + } else if (!strcmp(&infilename[strlen(infilename) - 5], ".poly")) { + infilename[strlen(infilename) - 5] = '\0'; + object = POLY; + plc = 1; + } else if (!strcmp(&infilename[strlen(infilename) - 6], ".smesh")) { + infilename[strlen(infilename) - 6] = '\0'; + object = POLY; + plc = 1; + } else if (!strcmp(&infilename[strlen(infilename) - 4], ".off")) { + infilename[strlen(infilename) - 4] = '\0'; + object = OFF; + plc = 1; + } else if (!strcmp(&infilename[strlen(infilename) - 4], ".ply")) { + infilename[strlen(infilename) - 4] = '\0'; + object = PLY; + plc = 1; + } else if (!strcmp(&infilename[strlen(infilename) - 4], ".stl")) { + infilename[strlen(infilename) - 4] = '\0'; + object = STL; + plc = 1; + } else if (!strcmp(&infilename[strlen(infilename) - 5], ".mesh")) { + infilename[strlen(infilename) - 5] = '\0'; + object = MEDIT; + if (!refine) plc = 1; + } else if (!strcmp(&infilename[strlen(infilename) - 4], ".vtk")) { + infilename[strlen(infilename) - 4] = '\0'; + object = VTK; + plc = 1; + } else if (!strcmp(&infilename[strlen(infilename) - 4], ".ele")) { + infilename[strlen(infilename) - 4] = '\0'; + object = MESH; + refine = 1; + } else if (!strcmp(&infilename[strlen(infilename) - 4], ".neu")) { + infilename[strlen(infilename) - 4] = '\0'; + object = NEU_MESH; + refine = 1; + } + } + + if (nobisect && (!plc && !refine)) { // -Y + plc = 1; // Default -p option. + } + if (quality && (!plc && !refine)) { // -q + plc = 1; // Default -p option. + } + if (diagnose && !plc) { // -d plc = 1; - if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || - (argv[i][j + 1] == '.')) { - k = 0; - while (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || - (argv[i][j + 1] == '.')) { - j++; - workstring[k] = argv[i][j]; - k++; - } - workstring[k] = '\0'; - facet_separate_ang_tol = (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] == '.') || (argv[i][j + 1] == 'e') || - (argv[i][j + 1] == '-') || (argv[i][j + 1] == '+')) { - j++; - workstring[k] = argv[i][j]; - k++; - } - workstring[k] = '\0'; - facet_overlap_ang_tol = (REAL) strtod(workstring, (char **) NULL); - } + } + if (refine && !quality) { // -r only + // Reconstruct a mesh, no mesh optimization. + optlevel = 0; + } + 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; + } + + if (convex) { // -c + if (plc && !regionattrib) { + // -A (region attribute) is needed for marking exterior tets (-1). + regionattrib = 1; } - 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'; - facet_small_ang_tol = (REAL) strtod(workstring, (char **) NULL); - } + } + + // 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. + } } - } 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_nomerge = (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')) { - supsteiner_level = (argv[i][j + 1] - '0'); - j++; - } + } + // 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 ((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++; - } + if (optminsmtdihed < 179.999) { + optminsmtdihed = 179.999; } - } 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; - 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'; - 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); - } + if (optminslidihed < 179.999) { + optminslidihed = 179.999; } - } 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); - } + } + + increment = 0; + strcpy(workstring, infilename); + j = 1; + while (workstring[j] != '\0') { + if ((workstring[j] == '.') && (workstring[j + 1] != '\0')) { + increment = j + 1; } - } 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; - 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_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') { - cdtrefine = 1; - if (argv[i][j + 1] == 'l') { - use_equatorial_lens = 1; - } else 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') { - if (argv[i][j + 1] == '1') { // -z1 - reversetetori = 1; - j++; - } else { - zeroindex = 1; // -z - } - } else if (argv[i][j] == 'f') { - facesout++; - } else if (argv[i][j] == 'e') { - edgesout++; - } else if (argv[i][j] == 'n') { - neighout++; - } else if (argv[i][j] == 'v') { - voroout = 1; - } else if (argv[i][j] == 'g') { - meditview = 1; - } else if (argv[i][j] == 'k') { - vtkview = 1; - } else if (argv[i][j] == 'J') { - nojettison = 1; - } else if (argv[i][j] == 'B') { - nobound = 1; - } else if (argv[i][j] == 'N') { - nonodewritten = 1; - } else if (argv[i][j] == 'E') { - noelewritten = 1; - } else if (argv[i][j] == 'F') { - nofacewritten = 1; - } else if (argv[i][j] == 'I') { - noiterationnum = 1; - } else if (argv[i][j] == 'S') { - if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || - (argv[i][j + 1] == '.')) { - k = 0; - while (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || - (argv[i][j + 1] == '.') || (argv[i][j + 1] == 'e') || - (argv[i][j + 1] == '-') || (argv[i][j + 1] == '+')) { - j++; - workstring[k] = argv[i][j]; - k++; - } - workstring[k] = '\0'; - 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; - 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'; - epsilon = (REAL) strtod(workstring, (char **) NULL); - } - } else if (argv[i][j] == 'C') { - docheck++; - } else if (argv[i][j] == 'Q') { - quiet = 1; - } else if (argv[i][j] == 'V') { - verbose++; - } else if (argv[i][j] == 'x') { - if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || - (argv[i][j + 1] == '.')) { - k = 0; - while (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || - (argv[i][j + 1] == '.') || (argv[i][j + 1] == 'e') || - (argv[i][j + 1] == '-') || (argv[i][j + 1] == '+')) { + j++; + } + meshnumber = 0; + if (increment > 0) { + j = increment; + do { + if ((workstring[j] >= '0') && (workstring[j] <= '9')) { + meshnumber = meshnumber * 10 + (int)(workstring[j] - '0'); + } else { + increment = 0; + } j++; - workstring[k] = argv[i][j]; - k++; - } - workstring[k] = '\0'; - 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') { - if (argv[i+1][0] != '-') { - hole_mesh = 1; - // It is a filename following by -H - strncpy(hole_mesh_filename, argv[i+1], 1024 - 1); - hole_mesh_filename[1024 - 1] = '\0'; - i++; // Skip the next string. - break; // j - } - } else if (argv[i][j] == 'K') { - apply_flow_bc = 1; - } else if ((argv[i][j] == 'h') || // (argv[i][j] == 'H') - (argv[i][j] == '?')) { - usage(); - } else { - printf("Warning: Unknown switch -%c.\n", argv[i][j]); - } - } - } - - if (startindex == 0) { - // Set a temporary filename for debugging output. - strcpy(infilename, "tetgen-tmpfile"); - } else { - if (infilename[0] == '\0') { - // No input file name. Print the syntax and exit. - syntax(); - terminatetetgen(NULL, 0); - } - // Recognize the object from file extension if it is available. - if (!strcmp(&infilename[strlen(infilename) - 5], ".node")) { - infilename[strlen(infilename) - 5] = '\0'; - object = NODES; - } else if (!strcmp(&infilename[strlen(infilename) - 5], ".poly")) { - infilename[strlen(infilename) - 5] = '\0'; - object = POLY; - plc = 1; - } else if (!strcmp(&infilename[strlen(infilename) - 6], ".smesh")) { - infilename[strlen(infilename) - 6] = '\0'; - object = POLY; - plc = 1; - } else if (!strcmp(&infilename[strlen(infilename) - 4], ".off")) { - infilename[strlen(infilename) - 4] = '\0'; - object = OFF; - plc = 1; - } else if (!strcmp(&infilename[strlen(infilename) - 4], ".ply")) { - infilename[strlen(infilename) - 4] = '\0'; - object = PLY; - plc = 1; - } else if (!strcmp(&infilename[strlen(infilename) - 4], ".stl")) { - infilename[strlen(infilename) - 4] = '\0'; - object = STL; - plc = 1; - } else if (!strcmp(&infilename[strlen(infilename) - 5], ".mesh")) { - infilename[strlen(infilename) - 5] = '\0'; - object = MEDIT; - if (!refine) plc = 1; - } else if (!strcmp(&infilename[strlen(infilename) - 4], ".vtk")) { - infilename[strlen(infilename) - 4] = '\0'; - object = VTK; - plc = 1; - } else if (!strcmp(&infilename[strlen(infilename) - 4], ".ele")) { - infilename[strlen(infilename) - 4] = '\0'; - object = MESH; - refine = 1; - } else if (!strcmp(&infilename[strlen(infilename) - 4], ".neu")) { - infilename[strlen(infilename) - 4] = '\0'; - object = NEU_MESH; - refine = 1; - } - } - - if (nobisect && (!plc && !refine)) { // -Y - plc = 1; // Default -p option. - } - 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 (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; - } - - 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; - } - } - - increment = 0; - strcpy(workstring, infilename); - j = 1; - while (workstring[j] != '\0') { - if ((workstring[j] == '.') && (workstring[j + 1] != '\0')) { - increment = j + 1; - } - j++; - } - meshnumber = 0; - if (increment > 0) { - j = increment; - do { - if ((workstring[j] >= '0') && (workstring[j] <= '9')) { - meshnumber = meshnumber * 10 + (int) (workstring[j] - '0'); - } else { - increment = 0; - } - j++; - } while (workstring[j] != '\0'); - } - if (noiterationnum) { - strcpy(outfilename, infilename); - } else if (increment == 0) { - strcpy(outfilename, infilename); - strcat(outfilename, ".1"); - } else { - workstring[increment] = '%'; - workstring[increment + 1] = 'd'; - workstring[increment + 2] = '\0'; - sprintf(outfilename, workstring, meshnumber + 1); - } - // Additional input file name has the end ".a". - strcpy(addinfilename, infilename); - strcat(addinfilename, ".a"); - // Background filename has the form "*.b.ele", "*.b.node", ... - strcpy(bgmeshfilename, infilename); - strcat(bgmeshfilename, ".b"); - - return true; + } while (workstring[j] != '\0'); + } + if (noiterationnum) { + strcpy(outfilename, infilename); + } else if (increment == 0) { + strcpy(outfilename, infilename); + strcat(outfilename, ".1"); + } else { + workstring[increment] = '%'; + workstring[increment + 1] = 'd'; + workstring[increment + 2] = '\0'; + sprintf(outfilename, workstring, meshnumber + 1); + } + // Additional input file name has the end ".a". + strcpy(addinfilename, infilename); + strcat(addinfilename, ".a"); + // Background filename has the form "*.b.ele", "*.b.node", ... + strcpy(bgmeshfilename, infilename); + strcat(bgmeshfilename, ".b"); + + return true; } //// //// @@ -3624,20 +3563,62 @@ bool tetgenbehavior::parse_commandline(int argc, char **argv) // Initialize fast lookup tables for mesh maniplulation primitives. -int tetgenmesh::bondtbl[12][12] = {{0,},}; -int tetgenmesh::enexttbl[12] = {0,}; -int tetgenmesh::eprevtbl[12] = {0,}; -int tetgenmesh::enextesymtbl[12] = {0,}; -int tetgenmesh::eprevesymtbl[12] = {0,}; -int tetgenmesh::eorgoppotbl[12] = {0,}; -int tetgenmesh::edestoppotbl[12] = {0,}; -int tetgenmesh::fsymtbl[12][12] = {{0,},}; -int tetgenmesh::facepivot1[12] = {0,}; -int tetgenmesh::facepivot2[12][12] = {{0,},}; -int tetgenmesh::tsbondtbl[12][6] = {{0,},}; -int tetgenmesh::stbondtbl[12][6] = {{0,},}; -int tetgenmesh::tspivottbl[12][6] = {{0,},}; -int tetgenmesh::stpivottbl[12][6] = {{0,},}; +int tetgenmesh::bondtbl[12][12] = { + { + 0, + }, +}; +int tetgenmesh::enexttbl[12] = { + 0, +}; +int tetgenmesh::eprevtbl[12] = { + 0, +}; +int tetgenmesh::enextesymtbl[12] = { + 0, +}; +int tetgenmesh::eprevesymtbl[12] = { + 0, +}; +int tetgenmesh::eorgoppotbl[12] = { + 0, +}; +int tetgenmesh::edestoppotbl[12] = { + 0, +}; +int tetgenmesh::fsymtbl[12][12] = { + { + 0, + }, +}; +int tetgenmesh::facepivot1[12] = { + 0, +}; +int tetgenmesh::facepivot2[12][12] = { + { + 0, + }, +}; +int tetgenmesh::tsbondtbl[12][6] = { + { + 0, + }, +}; +int tetgenmesh::stbondtbl[12][6] = { + { + 0, + }, +}; +int tetgenmesh::tspivottbl[12][6] = { + { + 0, + }, +}; +int tetgenmesh::stpivottbl[12][6] = { + { + 0, + }, +}; // Table 'esymtbl' takes an directed edge (version) as input, returns the // inversed edge (version) of it. @@ -3648,7 +3629,7 @@ int tetgenmesh::esymtbl[12] = {9, 6, 11, 4, 3, 7, 1, 5, 10, 0, 8, 2}; // An offset 4 is added to each element for a direct access of the points // in the tetrahedron data structure. -int tetgenmesh:: orgpivot[12] = {7, 7, 5, 5, 6, 4, 4, 6, 5, 6, 7, 4}; +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}; @@ -3657,13 +3638,12 @@ int tetgenmesh::oppopivot[12] = {4, 5, 6, 7, 4, 5, 6, 7, 4, 5, 6, 7}; // tables map a version to an undirected edge and vice versa. 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::edge2ver[6] = {0, 1, 2, 3, 8, 5}; // Edge versions whose apex or opposite may be dummypoint. int tetgenmesh::epivot[12] = {4, 5, 2, 11, 4, 5, 2, 11, 4, 5, 2, 11}; - // Table 'snextpivot' takes an edge version as input, returns the next edge // version in the same edge ring. @@ -3673,7 +3653,7 @@ int tetgenmesh::snextpivot[6] = {2, 5, 4, 1, 0, 3}; // An offset 3 is added to each element for a direct access of the points // in the triangle data structure. -int tetgenmesh::sorgpivot [6] = {3, 4, 4, 5, 5, 3}; +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}; @@ -3683,86 +3663,80 @@ int tetgenmesh::sapexpivot[6] = {5, 5, 3, 3, 4, 4}; // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::inittables() -{ - int soffset, toffset; - int i, j; - +void tetgenmesh::inittables() { + int soffset, toffset; + int i, j; - // i = t1.ver; j = t2.ver; - for (i = 0; i < 12; i++) { - for (j = 0; j < 12; j++) { - bondtbl[i][j] = (j & 3) + (((i & 12) + (j & 12)) % 12); + // i = t1.ver; j = t2.ver; + for (i = 0; i < 12; i++) { + for (j = 0; j < 12; j++) { + bondtbl[i][j] = (j & 3) + (((i & 12) + (j & 12)) % 12); + } } - } - - // i = t1.ver; j = t2.ver - for (i = 0; i < 12; i++) { - for (j = 0; j < 12; j++) { - fsymtbl[i][j] = (j + 12 - (i & 12)) % 12; + // i = t1.ver; j = t2.ver + for (i = 0; i < 12; i++) { + for (j = 0; j < 12; j++) { + fsymtbl[i][j] = (j + 12 - (i & 12)) % 12; + } } - } - - - for (i = 0; i < 12; i++) { - facepivot1[i] = (esymtbl[i] & 3); - } - for (i = 0; i < 12; i++) { - for (j = 0; j < 12; j++) { - facepivot2[i][j] = fsymtbl[esymtbl[i]][j]; + for (i = 0; i < 12; i++) { + facepivot1[i] = (esymtbl[i] & 3); } - } - for (i = 0; i < 12; i++) { - enexttbl[i] = (i + 4) % 12; - eprevtbl[i] = (i + 8) % 12; - } + for (i = 0; i < 12; i++) { + for (j = 0; j < 12; j++) { + facepivot2[i][j] = fsymtbl[esymtbl[i]][j]; + } + } - for (i = 0; i < 12; i++) { - enextesymtbl[i] = esymtbl[enexttbl[i]]; - eprevesymtbl[i] = esymtbl[eprevtbl[i]]; - } + for (i = 0; i < 12; i++) { + enexttbl[i] = (i + 4) % 12; + eprevtbl[i] = (i + 8) % 12; + } - for (i = 0; i < 12; i++) { - eorgoppotbl [i] = eprevtbl[esymtbl[enexttbl[i]]]; - edestoppotbl[i] = enexttbl[esymtbl[eprevtbl[i]]]; - } + for (i = 0; i < 12; i++) { + enextesymtbl[i] = esymtbl[enexttbl[i]]; + eprevesymtbl[i] = esymtbl[eprevtbl[i]]; + } - // i = t.ver, j = s.shver - for (i = 0; i < 12; i++) { - for (j = 0; j < 6; j++) { - if ((j & 1) == 0) { - soffset = (6 - ((i & 12) >> 1)) % 6; - toffset = (12 - ((j & 6) << 1)) % 12; - } else { - soffset = (i & 12) >> 1; - toffset = (j & 6) << 1; - } - tsbondtbl[i][j] = (j & 1) + (((j & 6) + soffset) % 6); - stbondtbl[i][j] = (i & 3) + (((i & 12) + toffset) % 12); + for (i = 0; i < 12; i++) { + eorgoppotbl[i] = eprevtbl[esymtbl[enexttbl[i]]]; + edestoppotbl[i] = enexttbl[esymtbl[eprevtbl[i]]]; } - } + // i = t.ver, j = s.shver + for (i = 0; i < 12; i++) { + for (j = 0; j < 6; j++) { + if ((j & 1) == 0) { + soffset = (6 - ((i & 12) >> 1)) % 6; + toffset = (12 - ((j & 6) << 1)) % 12; + } else { + soffset = (i & 12) >> 1; + toffset = (j & 6) << 1; + } + tsbondtbl[i][j] = (j & 1) + (((j & 6) + soffset) % 6); + stbondtbl[i][j] = (i & 3) + (((i & 12) + toffset) % 12); + } + } - // i = t.ver, j = s.shver - for (i = 0; i < 12; i++) { - for (j = 0; j < 6; j++) { - if ((j & 1) == 0) { - soffset = (i & 12) >> 1; - toffset = (j & 6) << 1; - } else { - soffset = (6 - ((i & 12) >> 1)) % 6; - toffset = (12 - ((j & 6) << 1)) % 12; - } - tspivottbl[i][j] = (j & 1) + (((j & 6) + soffset) % 6); - stpivottbl[i][j] = (i & 3) + (((i & 12) + toffset) % 12); + // 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); + } } - } } - /////////////////////////////////////////////////////////////////////////////// // // // restart() Deallocate all objects in this pool. // @@ -3773,10 +3747,7 @@ void tetgenmesh::inittables() // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::arraypool::restart() -{ - objects = 0l; -} +void tetgenmesh::arraypool::restart() { objects = 0l; } /////////////////////////////////////////////////////////////////////////////// // // @@ -3787,24 +3758,23 @@ void tetgenmesh::arraypool::restart() // // /////////////////////////////////////////////////////////////////////////////// -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; - objectsperblockmark = objectsperblock - 1; - - // 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(); +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; + objectsperblockmark = objectsperblock - 1; + + // 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(); } /////////////////////////////////////////////////////////////////////////////// @@ -3813,34 +3783,32 @@ void tetgenmesh::arraypool::poolinit(int sizeofobject, int log2objperblk) // // /////////////////////////////////////////////////////////////////////////////// -tetgenmesh::arraypool::arraypool(int sizeofobject, int log2objperblk) -{ - poolinit(sizeofobject, log2objperblk); +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; +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; } /////////////////////////////////////////////////////////////////////////////// @@ -3855,62 +3823,61 @@ tetgenmesh::arraypool::~arraypool() // // /////////////////////////////////////////////////////////////////////////////// -char* tetgenmesh::arraypool::getblock(int objectindex) -{ - char **newarray; - char *block; - int newsize; - int topindex; - int i; - - // Compute the index in the top array (upper bits). - topindex = objectindex >> log2objectsperblock; - // Does the top array need to be allocated or resized? - if (toparray == (char **) NULL) { - // Allocate the top array big enough to hold 'topindex', and NULL out - // its contents. - newsize = topindex + 128; - toparray = (char **) malloc((size_t) (newsize * sizeof(char *))); - toparraylen = newsize; - for (i = 0; i < newsize; i++) { - toparray[i] = (char *) NULL; - } - // Account for the memory. - totalmemory = newsize * (uintptr_t) sizeof(char *); - } else if (topindex >= toparraylen) { - // Resize the top array, making sure it holds 'topindex'. - newsize = 3 * toparraylen; - if (topindex >= newsize) { - newsize = topindex + 128; - } - // Allocate the new array, copy the contents, NULL out the rest, and - // free the old array. - newarray = (char **) malloc((size_t) (newsize * sizeof(char *))); - for (i = 0; i < toparraylen; i++) { - newarray[i] = toparray[i]; - } - for (i = toparraylen; i < newsize; i++) { - newarray[i] = (char *) NULL; - } - free(toparray); - // Account for the memory. - totalmemory += (newsize - toparraylen) * sizeof(char *); - toparray = newarray; - toparraylen = newsize; - } - - // Find the block, or learn that it hasn't been allocated yet. - block = toparray[topindex]; - if (block == (char *) NULL) { - // Allocate a block at this index. - block = (char *) malloc((size_t) (objectsperblock * objectbytes)); - toparray[topindex] = block; - // Account for the memory. - totalmemory += objectsperblock * objectbytes; - } - - // Return a pointer to the block. - return block; +char* tetgenmesh::arraypool::getblock(int objectindex) { + char** newarray; + char* block; + int newsize; + int topindex; + int i; + + // Compute the index in the top array (upper bits). + topindex = objectindex >> log2objectsperblock; + // Does the top array need to be allocated or resized? + if (toparray == (char**)NULL) { + // Allocate the top array big enough to hold 'topindex', and NULL out + // its contents. + newsize = topindex + 128; + toparray = (char**)malloc((size_t)(newsize * sizeof(char*))); + toparraylen = newsize; + for (i = 0; i < newsize; i++) { + toparray[i] = (char*)NULL; + } + // Account for the memory. + totalmemory = newsize * (uintptr_t)sizeof(char*); + } else if (topindex >= toparraylen) { + // Resize the top array, making sure it holds 'topindex'. + newsize = 3 * toparraylen; + if (topindex >= newsize) { + newsize = topindex + 128; + } + // Allocate the new array, copy the contents, NULL out the rest, and + // free the old array. + newarray = (char**)malloc((size_t)(newsize * sizeof(char*))); + for (i = 0; i < toparraylen; i++) { + newarray[i] = toparray[i]; + } + for (i = toparraylen; i < newsize; i++) { + newarray[i] = (char*)NULL; + } + free(toparray); + // Account for the memory. + totalmemory += (newsize - toparraylen) * sizeof(char*); + toparray = newarray; + toparraylen = newsize; + } + + // Find the block, or learn that it hasn't been allocated yet. + block = toparray[topindex]; + if (block == (char*)NULL) { + // Allocate a block at this index. + block = (char*)malloc((size_t)(objectsperblock * objectbytes)); + toparray[topindex] = block; + // Account for the memory. + totalmemory += objectsperblock * objectbytes; + } + + // Return a pointer to the block. + return block; } /////////////////////////////////////////////////////////////////////////////// @@ -3920,33 +3887,32 @@ char* tetgenmesh::arraypool::getblock(int objectindex) // // /////////////////////////////////////////////////////////////////////////////// -void* tetgenmesh::arraypool::lookup(int objectindex) -{ - char *block; - int topindex; - - // Has the top array been allocated yet? - if (toparray == (char **) NULL) { - return (void *) NULL; - } - - // Compute the index in the top array (upper bits). - topindex = objectindex >> log2objectsperblock; - // Does the top index fit in the top array? - if (topindex >= toparraylen) { - return (void *) NULL; - } - - // Find the block, or learn that it hasn't been allocated yet. - block = toparray[topindex]; - if (block == (char *) NULL) { - return (void *) NULL; - } - - // Compute a pointer to the object with the given index. Note that - // 'objectsperblock' is a power of two, so the & operation is a bit mask - // that preserves the lower bits. - return (void *)(block + (objectindex & (objectsperblock - 1)) * objectbytes); +void* tetgenmesh::arraypool::lookup(int objectindex) { + char* block; + int topindex; + + // Has the top array been allocated yet? + if (toparray == (char**)NULL) { + return (void*)NULL; + } + + // Compute the index in the top array (upper bits). + topindex = objectindex >> log2objectsperblock; + // Does the top index fit in the top array? + if (topindex >= toparraylen) { + return (void*)NULL; + } + + // Find the block, or learn that it hasn't been allocated yet. + block = toparray[topindex]; + if (block == (char*)NULL) { + return (void*)NULL; + } + + // Compute a pointer to the object with the given index. Note that + // 'objectsperblock' is a power of two, so the & operation is a bit mask + // that preserves the lower bits. + return (void*)(block + (objectindex & (objectsperblock - 1)) * objectbytes); } /////////////////////////////////////////////////////////////////////////////// @@ -3957,43 +3923,37 @@ void* tetgenmesh::arraypool::lookup(int objectindex) // // /////////////////////////////////////////////////////////////////////////////// -int tetgenmesh::arraypool::newindex(void **newptr) -{ - // Allocate an object at index 'firstvirgin'. - int newindex = objects; - *newptr = (void *) (getblock(objects) + - (objects & (objectsperblock - 1)) * objectbytes); - objects++; +int tetgenmesh::arraypool::newindex(void** newptr) { + // Allocate an object at index 'firstvirgin'. + int newindex = objects; + *newptr = (void*)(getblock(objects) + (objects & (objectsperblock - 1)) * objectbytes); + objects++; - return newindex; + return newindex; } - /////////////////////////////////////////////////////////////////////////////// // // // memorypool() The constructors of memorypool. // // // /////////////////////////////////////////////////////////////////////////////// -tetgenmesh::memorypool::memorypool() -{ - firstblock = nowblock = (void **) NULL; - nextitem = (void *) NULL; - deaditemstack = (void *) NULL; - pathblock = (void **) NULL; - pathitem = (void *) NULL; - alignbytes = 0; - itembytes = itemwords = 0; - itemsperblock = 0; - items = maxitems = 0l; - unallocateditems = 0; - pathitemsleft = 0; +tetgenmesh::memorypool::memorypool() { + firstblock = nowblock = (void**)NULL; + nextitem = (void*)NULL; + deaditemstack = (void*)NULL; + pathblock = (void**)NULL; + pathitem = (void*)NULL; + alignbytes = 0; + itembytes = itemwords = 0; + itemsperblock = 0; + items = maxitems = 0l; + unallocateditems = 0; + pathitemsleft = 0; } -tetgenmesh::memorypool::memorypool(int bytecount, int itemcount, int wsize, - int alignment) -{ - poolinit(bytecount, itemcount, wsize, alignment); +tetgenmesh::memorypool::memorypool(int bytecount, int itemcount, int wsize, int alignment) { + poolinit(bytecount, itemcount, wsize, alignment); } /////////////////////////////////////////////////////////////////////////////// @@ -4002,13 +3962,12 @@ tetgenmesh::memorypool::memorypool(int bytecount, int itemcount, int wsize, // // /////////////////////////////////////////////////////////////////////////////// -tetgenmesh::memorypool::~memorypool() -{ - while (firstblock != (void **) NULL) { - nowblock = (void **) *(firstblock); - free(firstblock); - firstblock = nowblock; - } +tetgenmesh::memorypool::~memorypool() { + while (firstblock != (void**)NULL) { + nowblock = (void**)*(firstblock); + free(firstblock); + firstblock = nowblock; + } } /////////////////////////////////////////////////////////////////////////////// @@ -4027,38 +3986,34 @@ tetgenmesh::memorypool::~memorypool() // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::memorypool::poolinit(int bytecount,int itemcount,int wordsize, - int alignment) -{ - // Find the proper alignment, which must be at least as large as: - // - The parameter `alignment'. - // - The primary word type, to avoid unaligned accesses. - // - sizeof(void *), so the stack of dead items can be maintained - // without unaligned accesses. - if (alignment > wordsize) { - alignbytes = alignment; - } else { - alignbytes = wordsize; - } - if ((int) sizeof(void *) > alignbytes) { - alignbytes = (int) sizeof(void *); - } - itemwords = ((bytecount + alignbytes - 1) / alignbytes) - * (alignbytes / wordsize); - itembytes = itemwords * wordsize; - itemsperblock = itemcount; - - // Allocate a block of items. Space for `itemsperblock' items and one - // pointer (to point to the next block) are allocated, as well as space - // to ensure alignment of the items. - firstblock = (void **) malloc(itemsperblock * itembytes + sizeof(void *) - + alignbytes); - if (firstblock == (void **) NULL) { - terminatetetgen(NULL, 1); - } - // Set the next block pointer to NULL. - *(firstblock) = (void *) NULL; - restart(); +void tetgenmesh::memorypool::poolinit(int bytecount, int itemcount, int wordsize, int alignment) { + // Find the proper alignment, which must be at least as large as: + // - The parameter `alignment'. + // - The primary word type, to avoid unaligned accesses. + // - sizeof(void *), so the stack of dead items can be maintained + // without unaligned accesses. + if (alignment > wordsize) { + alignbytes = alignment; + } else { + alignbytes = wordsize; + } + if ((int)sizeof(void*) > alignbytes) { + alignbytes = (int)sizeof(void*); + } + itemwords = ((bytecount + alignbytes - 1) / alignbytes) * (alignbytes / wordsize); + itembytes = itemwords * wordsize; + itemsperblock = itemcount; + + // Allocate a block of items. Space for `itemsperblock' items and one + // pointer (to point to the next block) are allocated, as well as space + // to ensure alignment of the items. + firstblock = (void**)malloc(itemsperblock * itembytes + sizeof(void*) + alignbytes); + if (firstblock == (void**)NULL) { + terminatetetgen(NULL, 1); + } + // Set the next block pointer to NULL. + *(firstblock) = (void*)NULL; + restart(); } /////////////////////////////////////////////////////////////////////////////// @@ -4071,25 +4026,22 @@ void tetgenmesh::memorypool::poolinit(int bytecount,int itemcount,int wordsize, // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::memorypool::restart() -{ - uintptr_t alignptr; - - items = 0; - maxitems = 0; - - // Set the currently active block. - nowblock = firstblock; - // Find the first item in the pool. Increment by the size of (void *). - alignptr = (uintptr_t) (nowblock + 1); - // Align the item on an `alignbytes'-byte boundary. - nextitem = (void *) - (alignptr + (uintptr_t) alignbytes - - (alignptr % (uintptr_t) alignbytes)); - // There are lots of unallocated items left in this block. - unallocateditems = itemsperblock; - // The stack of deallocated items is empty. - deaditemstack = (void *) NULL; +void tetgenmesh::memorypool::restart() { + uintptr_t alignptr; + + items = 0; + maxitems = 0; + + // Set the currently active block. + nowblock = firstblock; + // Find the first item in the pool. Increment by the size of (void *). + alignptr = (uintptr_t)(nowblock + 1); + // Align the item on an `alignbytes'-byte boundary. + nextitem = (void*)(alignptr + (uintptr_t)alignbytes - (alignptr % (uintptr_t)alignbytes)); + // There are lots of unallocated items left in this block. + unallocateditems = itemsperblock; + // The stack of deallocated items is empty. + deaditemstack = (void*)NULL; } /////////////////////////////////////////////////////////////////////////////// @@ -4098,53 +4050,50 @@ void tetgenmesh::memorypool::restart() // // /////////////////////////////////////////////////////////////////////////////// -void* tetgenmesh::memorypool::alloc() -{ - void *newitem; - void **newblock; - uintptr_t alignptr; - - // First check the linked list of dead items. If the list is not - // empty, allocate an item from the list rather than a fresh one. - if (deaditemstack != (void *) NULL) { - newitem = deaditemstack; // Take first item in list. - deaditemstack = * (void **) deaditemstack; - } else { - // Check if there are any free items left in the current block. - if (unallocateditems == 0) { - // Check if another block must be allocated. - if (*nowblock == (void *) NULL) { - // Allocate a new block of items, pointed to by the previous block. - newblock = (void **) malloc(itemsperblock * itembytes + sizeof(void *) - + alignbytes); - if (newblock == (void **) NULL) { - terminatetetgen(NULL, 1); - } - *nowblock = (void *) newblock; - // The next block pointer is NULL. - *newblock = (void *) NULL; - } - // Move to the new block. - nowblock = (void **) *nowblock; - // Find the first item in the block. - // Increment by the size of (void *). - alignptr = (uintptr_t) (nowblock + 1); - // Align the item on an `alignbytes'-byte boundary. - nextitem = (void *) - (alignptr + (uintptr_t) alignbytes - - (alignptr % (uintptr_t) alignbytes)); - // There are lots of unallocated items left in this block. - unallocateditems = itemsperblock; - } - // Allocate a new item. - newitem = nextitem; - // Advance `nextitem' pointer to next free item in block. - nextitem = (void *) ((uintptr_t) nextitem + itembytes); - unallocateditems--; - maxitems++; - } - items++; - return newitem; +void* tetgenmesh::memorypool::alloc() { + void* newitem; + void** newblock; + uintptr_t alignptr; + + // First check the linked list of dead items. If the list is not + // empty, allocate an item from the list rather than a fresh one. + if (deaditemstack != (void*)NULL) { + newitem = deaditemstack; // Take first item in list. + deaditemstack = *(void**)deaditemstack; + } else { + // Check if there are any free items left in the current block. + if (unallocateditems == 0) { + // Check if another block must be allocated. + if (*nowblock == (void*)NULL) { + // Allocate a new block of items, pointed to by the previous block. + newblock = (void**)malloc(itemsperblock * itembytes + sizeof(void*) + alignbytes); + if (newblock == (void**)NULL) { + terminatetetgen(NULL, 1); + } + *nowblock = (void*)newblock; + // The next block pointer is NULL. + *newblock = (void*)NULL; + } + // Move to the new block. + nowblock = (void**)*nowblock; + // Find the first item in the block. + // Increment by the size of (void *). + alignptr = (uintptr_t)(nowblock + 1); + // Align the item on an `alignbytes'-byte boundary. + nextitem = + (void*)(alignptr + (uintptr_t)alignbytes - (alignptr % (uintptr_t)alignbytes)); + // There are lots of unallocated items left in this block. + unallocateditems = itemsperblock; + } + // Allocate a new item. + newitem = nextitem; + // Advance `nextitem' pointer to next free item in block. + nextitem = (void*)((uintptr_t)nextitem + itembytes); + unallocateditems--; + maxitems++; + } + items++; + return newitem; } /////////////////////////////////////////////////////////////////////////////// @@ -4155,12 +4104,11 @@ void* tetgenmesh::memorypool::alloc() // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::memorypool::dealloc(void *dyingitem) -{ - // Push freshly killed item onto stack. - *((void **) dyingitem) = deaditemstack; - deaditemstack = dyingitem; - items--; +void tetgenmesh::memorypool::dealloc(void* dyingitem) { + // Push freshly killed item onto stack. + *((void**)dyingitem) = deaditemstack; + deaditemstack = dyingitem; + items--; } /////////////////////////////////////////////////////////////////////////////// @@ -4171,20 +4119,17 @@ void tetgenmesh::memorypool::dealloc(void *dyingitem) // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::memorypool::traversalinit() -{ - 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 = (uintptr_t) (pathblock + 1); - // Align with item on an `alignbytes'-byte boundary. - pathitem = (void *) - (alignptr + (uintptr_t) alignbytes - - (alignptr % (uintptr_t) alignbytes)); - // Set the number of items left in the current block. - pathitemsleft = itemsperblock; +void tetgenmesh::memorypool::traversalinit() { + 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 = (uintptr_t)(pathblock + 1); + // Align with item on an `alignbytes'-byte boundary. + pathitem = (void*)(alignptr + (uintptr_t)alignbytes - (alignptr % (uintptr_t)alignbytes)); + // Set the number of items left in the current block. + pathitemsleft = itemsperblock; } /////////////////////////////////////////////////////////////////////////////// @@ -4199,33 +4144,30 @@ void tetgenmesh::memorypool::traversalinit() // // /////////////////////////////////////////////////////////////////////////////// -void* tetgenmesh::memorypool::traverse() -{ - void *newitem; - uintptr_t alignptr; - - // Stop upon exhausting the list of items. - if (pathitem == nextitem) { - return (void *) NULL; - } - // Check whether any untraversed items remain in the current block. - if (pathitemsleft == 0) { - // Find the next block. - pathblock = (void **) *pathblock; - // Find the first item in the block. Increment by the size of (void *). - alignptr = (uintptr_t) (pathblock + 1); - // Align with item on an `alignbytes'-byte boundary. - pathitem = (void *) - (alignptr + (uintptr_t) alignbytes - - (alignptr % (uintptr_t) alignbytes)); - // Set the number of items left in the current block. - pathitemsleft = itemsperblock; - } - newitem = pathitem; - // Find the next item in the block. - pathitem = (void *) ((uintptr_t) pathitem + itembytes); - pathitemsleft--; - return newitem; +void* tetgenmesh::memorypool::traverse() { + void* newitem; + uintptr_t alignptr; + + // Stop upon exhausting the list of items. + if (pathitem == nextitem) { + return (void*)NULL; + } + // Check whether any untraversed items remain in the current block. + if (pathitemsleft == 0) { + // Find the next block. + pathblock = (void**)*pathblock; + // Find the first item in the block. Increment by the size of (void *). + alignptr = (uintptr_t)(pathblock + 1); + // Align with item on an `alignbytes'-byte boundary. + pathitem = (void*)(alignptr + (uintptr_t)alignbytes - (alignptr % (uintptr_t)alignbytes)); + // Set the number of items left in the current block. + pathitemsleft = itemsperblock; + } + newitem = pathitem; + // Find the next item in the block. + pathitem = (void*)((uintptr_t)pathitem + itembytes); + pathitemsleft--; + return newitem; } /////////////////////////////////////////////////////////////////////////////// @@ -4238,24 +4180,23 @@ void* tetgenmesh::memorypool::traverse() // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::makeindex2pointmap(point*& idx2verlist) -{ - point pointloop; - int idx; +void tetgenmesh::makeindex2pointmap(point*& idx2verlist) { + point pointloop; + int idx; - if (b->verbose > 1) { - printf(" Constructing mapping from indices to points.\n"); - } + if (b->verbose > 1) { + printf(" Constructing mapping from indices to points.\n"); + } - idx2verlist = new point[points->items + 1]; + idx2verlist = new point[points->items + 1]; - points->traversalinit(); - pointloop = pointtraverse(); - idx = in->firstnumber; - while (pointloop != (point) NULL) { - idx2verlist[idx++] = pointloop; + points->traversalinit(); pointloop = pointtraverse(); - } + idx = in->firstnumber; + while (pointloop != (point)NULL) { + idx2verlist[idx++] = pointloop; + pointloop = pointtraverse(); + } } /////////////////////////////////////////////////////////////////////////////// @@ -4272,81 +4213,80 @@ void tetgenmesh::makeindex2pointmap(point*& idx2verlist) // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::makepoint2submap(memorypool* pool, int*& idx2faclist, - face*& facperverlist) -{ - face shloop; - int i, j, k; - - if (b->verbose > 1) { - printf(" Making a map from points to subfaces.\n"); - } - - // Initialize 'idx2faclist'. - idx2faclist = new int[points->items + 1]; - for (i = 0; i < points->items + 1; i++) idx2faclist[i] = 0; - - // 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]++; +void tetgenmesh::makepoint2submap(memorypool* pool, int*& idx2faclist, face*& facperverlist) { + face shloop; + int i, j, k; + + if (b->verbose > 1) { + printf(" Making a map from points to subfaces.\n"); } + + // Initialize 'idx2faclist'. + idx2faclist = new int[points->items + 1]; + for (i = 0; i < points->items + 1; i++) + idx2faclist[i] = 0; + + // Loop all subfaces, counter the number of subfaces incident at a vertex. + pool->traversalinit(); 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 = idx2faclist[i + 1]; - idx2faclist[i + 1] = idx2faclist[i] + j; - j = k; - } - - // 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]++; - } + 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 = idx2faclist[i + 1]; + idx2faclist[i + 1] = idx2faclist[i] + j; + j = k; + } + + // 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.sh = shellfacetraverse(pool); + } - // Contents in 'idx2faclist' are shifted, now shift them back. - for (i = points->items - 1; i >= 0; i--) { - idx2faclist[i + 1] = idx2faclist[i]; - } - idx2faclist[0] = 0; + // Contents in 'idx2faclist' are shifted, now shift them back. + for (i = points->items - 1; i >= 0; i--) { + idx2faclist[i + 1] = idx2faclist[i]; + } + idx2faclist[0] = 0; } /////////////////////////////////////////////////////////////////////////////// @@ -4355,21 +4295,20 @@ void tetgenmesh::makepoint2submap(memorypool* pool, int*& idx2faclist, // // /////////////////////////////////////////////////////////////////////////////// -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; - - // Dealloc the space to subfaces/subsegments. - if (dyingtetrahedron[8] != NULL) { - tet2segpool->dealloc((shellface *) dyingtetrahedron[8]); - } - if (dyingtetrahedron[9] != NULL) { - tet2subpool->dealloc((shellface *) dyingtetrahedron[9]); - } - - tetrahedrons->dealloc((void *) dyingtetrahedron); +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; + + // Dealloc the space to subfaces/subsegments. + if (dyingtetrahedron[8] != NULL) { + tet2segpool->dealloc((shellface*)dyingtetrahedron[8]); + } + if (dyingtetrahedron[9] != NULL) { + tet2subpool->dealloc((shellface*)dyingtetrahedron[9]); + } + + tetrahedrons->dealloc((void*)dyingtetrahedron); } /////////////////////////////////////////////////////////////////////////////// @@ -4378,31 +4317,28 @@ void tetgenmesh::tetrahedrondealloc(tetrahedron *dyingtetrahedron) // // /////////////////////////////////////////////////////////////////////////////// -tetgenmesh::tetrahedron* tetgenmesh::tetrahedrontraverse() -{ - tetrahedron *newtetrahedron; +tetgenmesh::tetrahedron* tetgenmesh::tetrahedrontraverse() { + tetrahedron* newtetrahedron; - do { - newtetrahedron = (tetrahedron *) tetrahedrons->traverse(); - if (newtetrahedron == (tetrahedron *) NULL) { - return (tetrahedron *) NULL; - } - } while ((newtetrahedron[4] == (tetrahedron) NULL) || - ((point) newtetrahedron[7] == dummypoint)); - return newtetrahedron; + do { + newtetrahedron = (tetrahedron*)tetrahedrons->traverse(); + if (newtetrahedron == (tetrahedron*)NULL) { + return (tetrahedron*)NULL; + } + } while ((newtetrahedron[4] == (tetrahedron)NULL) || ((point)newtetrahedron[7] == dummypoint)); + return newtetrahedron; } -tetgenmesh::tetrahedron* tetgenmesh::alltetrahedrontraverse() -{ - tetrahedron *newtetrahedron; +tetgenmesh::tetrahedron* tetgenmesh::alltetrahedrontraverse() { + tetrahedron* newtetrahedron; - do { - newtetrahedron = (tetrahedron *) tetrahedrons->traverse(); - if (newtetrahedron == (tetrahedron *) NULL) { - return (tetrahedron *) NULL; - } - } while (newtetrahedron[4] == (tetrahedron) NULL); // Skip dead ones. - return newtetrahedron; + do { + newtetrahedron = (tetrahedron*)tetrahedrons->traverse(); + if (newtetrahedron == (tetrahedron*)NULL) { + return (tetrahedron*)NULL; + } + } while (newtetrahedron[4] == (tetrahedron)NULL); // Skip dead ones. + return newtetrahedron; } /////////////////////////////////////////////////////////////////////////////// @@ -4412,12 +4348,11 @@ tetgenmesh::tetrahedron* tetgenmesh::alltetrahedrontraverse() // // /////////////////////////////////////////////////////////////////////////////// -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); +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); } /////////////////////////////////////////////////////////////////////////////// @@ -4427,32 +4362,29 @@ void tetgenmesh::shellfacedealloc(memorypool *pool, shellface *dyingsh) // // /////////////////////////////////////////////////////////////////////////////// -tetgenmesh::shellface* tetgenmesh::shellfacetraverse(memorypool *pool) -{ - shellface *newshellface; +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; + do { + newshellface = (shellface*)pool->traverse(); + if (newshellface == (shellface*)NULL) { + return (shellface*)NULL; + } + } while (newshellface[3] == (shellface)NULL); // Skip dead ones. + return newshellface; } - /////////////////////////////////////////////////////////////////////////////// // // // 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); +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); } /////////////////////////////////////////////////////////////////////////////// @@ -4461,17 +4393,16 @@ void tetgenmesh::pointdealloc(point dyingpoint) // // /////////////////////////////////////////////////////////////////////////////// -tetgenmesh::point tetgenmesh::pointtraverse() -{ - point newpoint; +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; + do { + newpoint = (point)points->traverse(); + if (newpoint == (point)NULL) { + return (point)NULL; + } + } while (pointtype(newpoint) == DEADVERTEX); // Skip dead ones. + return newpoint; } /////////////////////////////////////////////////////////////////////////////// @@ -4480,34 +4411,33 @@ tetgenmesh::point tetgenmesh::pointtraverse() // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::maketetrahedron(triface *newtet) -{ - newtet->tet = (tetrahedron *) tetrahedrons->alloc(); - - // Initialize the four adjoining tetrahedra to be "outer space". - newtet->tet[0] = NULL; - newtet->tet[1] = NULL; - 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); - } - if (b->varvolume) { - setvolumebound(newtet->tet, -1.0); - } - - // Initialize the version to be Zero. - newtet->ver = 11; +void tetgenmesh::maketetrahedron(triface* newtet) { + newtet->tet = (tetrahedron*)tetrahedrons->alloc(); + + // Initialize the four adjoining tetrahedra to be "outer space". + newtet->tet[0] = NULL; + newtet->tet[1] = NULL; + 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); + } + if (b->varvolume) { + setvolumebound(newtet->tet, -1.0); + } + + // Initialize the version to be Zero. + newtet->ver = 11; } /////////////////////////////////////////////////////////////////////////////// @@ -4517,38 +4447,37 @@ void tetgenmesh::maketetrahedron(triface *newtet) // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::makeshellface(memorypool *pool, face *newface) -{ - newface->sh = (shellface *) pool->alloc(); - - // No adjointing subfaces. - newface->sh[0] = NULL; - newface->sh[1] = NULL; - 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); - } - // Set the boundary marker to zero. - setshellmark(*newface, 0); - // Clear the infection and marktest bits. - ((int *) (newface->sh))[shmarkindex + 1] = 0; - if (useinsertradius) { - setfacetindex(*newface, 0); - } - - newface->shver = 0; +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); + } + // Set the boundary marker to zero. + setshellmark(*newface, 0); + // Clear the infection and marktest bits. + ((int*)(newface->sh))[shmarkindex + 1] = 0; + if (useinsertradius) { + setfacetindex(*newface, 0); + } + + newface->shver = 0; } /////////////////////////////////////////////////////////////////////////////// @@ -4557,35 +4486,34 @@ void tetgenmesh::makeshellface(memorypool *pool, face *newface) // // /////////////////////////////////////////////////////////////////////////////// -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); +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); } /////////////////////////////////////////////////////////////////////////////// @@ -4599,276 +4527,268 @@ void tetgenmesh::makepoint(point* pnewpoint, enum verttype vtype) // // /////////////////////////////////////////////////////////////////////////////// -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; +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 { - // 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++; - } - pointinsradiusindex = pointmtrindex + sizeoftensor - 1; - // 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 three pointers, which are: - // - a pointer to a tet, read by point2tet(); - // - a pointer to a parent point, read by point2ppt()). - // - a pointer to a subface or segment, read by point2sh(); - if (b->metric && (bgm != (tetgenmesh *) NULL)) { - // Increase one pointer into the background mesh, point2bgmtet(). - pointsize = (point2simindex + 4) * sizeof(tetrahedron); + // 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++; + } + pointinsradiusindex = pointmtrindex + sizeoftensor - 1; + // 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 three pointers, which are: + // - a pointer to a tet, read by point2tet(); + // - a pointer to a parent point, read by point2ppt()). + // - a pointer to a subface or segment, read by point2sh(); + if (b->metric && (bgm != (tetgenmesh*)NULL)) { + // Increase one pointer into the background mesh, point2bgmtet(). + pointsize = (point2simindex + 4) * sizeof(tetrahedron); + } else { + pointsize = (point2simindex + 3) * sizeof(tetrahedron); + } } else { - pointsize = (point2simindex + 3) * sizeof(tetrahedron); - } - } else { - // Increase the point size by two pointer, which are: - // - a pointer to a tet, read by point2tet(); - // - 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 (indicated by pointmarkindex) plus: - // - an integer for boundary marker; - // - an integer for vertex type; - // - 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, b->vertexperblock, sizeof(REAL), 0); - - if (b->verbose) { - printf(" Size of a point: %d bytes.\n", points->itembytes); - } - - // 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. - if (!(sizeof(int) <= sizeof(tetrahedron)) || - ((sizeof(tetrahedron) % sizeof(int)))) { - terminatetetgen(this, 2); - } - 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 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 (numelemattrib > 0) { - elesize = volumeboundindex * sizeof(REAL); - } - - - // Having determined the memory size of an element, initialize the pool. - tetrahedrons = new memorypool(elesize, b->tetrahedraperblock, sizeof(void *), - 16); - - if (b->verbose) { - printf(" Size of a tetrahedron: %d (%d) bytes.\n", elesize, - tetrahedrons->itembytes); - } - - 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. - shsize = 11 * sizeof(shellface); - // The index within each subface at which the maximum area bound is + // Increase the point size by two pointer, which are: + // - a pointer to a tet, read by point2tet(); + // - 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 (indicated by pointmarkindex) plus: + // - an integer for boundary marker; + // - an integer for vertex type; + // - 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, b->vertexperblock, sizeof(REAL), 0); + + if (b->verbose) { + printf(" Size of a point: %d bytes.\n", points->itembytes); + } + + // 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. + if (!(sizeof(int) <= sizeof(tetrahedron)) || ((sizeof(tetrahedron) % sizeof(int)))) { + terminatetetgen(this, 2); + } + 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 volume 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 (checkconstraints) { - shsize = (areaboundindex + 1) * sizeof(REAL); - } else { - shsize = areaboundindex * sizeof(REAL); + 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 (numelemattrib > 0) { + elesize = volumeboundindex * sizeof(REAL); } - // The index within subface at which the facet marker is found. Ensure - // the marker is aligned to a sizeof(int)-byte address. - shmarkindex = (shsize + sizeof(int) - 1) / sizeof(int); - // Increase the number of bytes by two or three integers, one for facet - // marker, one for shellface type and flags, and optionally one - // for storing facet index (for mesh refinement). - shsize = (shmarkindex + 2 + useinsertradius) * sizeof(shellface); - // Initialize the pool of subfaces. Each subface record is eight-byte - // aligned so it has room to store an edge version (from 0 to 5) in - // the least three bits. - subfaces = new memorypool(shsize, b->shellfaceperblock, sizeof(void *), 8); + // Having determined the memory size of an element, initialize the pool. + tetrahedrons = new memorypool(elesize, b->tetrahedraperblock, sizeof(void*), 16); 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, b->shellfaceperblock, sizeof(void *), 8); - - // Initialize the pool for tet-subseg connections. - tet2segpool = new memorypool(6 * sizeof(shellface), b->shellfaceperblock, - sizeof(void *), 0); - // Initialize the pool for tet-subface connections. - 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); - subvertstack = new arraypool(sizeof(point), 8); - - // Initialize arraypools for surface point insertion/deletion. - caveshlist = new arraypool(sizeof(face), 8); - caveshbdlist = new arraypool(sizeof(face), 8); - cavesegshlist = new arraypool(sizeof(face), 4); - - cavetetshlist = new arraypool(sizeof(face), 8); - cavetetseglist = new arraypool(sizeof(face), 8); - caveencshlist = new arraypool(sizeof(face), 8); - caveencseglist = new arraypool(sizeof(face), 8); - } - - // Initialize the 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); + printf(" Size of a tetrahedron: %d (%d) bytes.\n", elesize, tetrahedrons->itembytes); + } + + 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. + 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 (checkconstraints) { + shsize = (areaboundindex + 1) * sizeof(REAL); + } else { + shsize = areaboundindex * sizeof(REAL); + } + // The index within subface at which the facet marker is found. Ensure + // the marker is aligned to a sizeof(int)-byte address. + shmarkindex = (shsize + sizeof(int) - 1) / sizeof(int); + // Increase the number of bytes by two or three integers, one for facet + // marker, one for shellface type and flags, and optionally one + // for storing facet index (for mesh refinement). + shsize = (shmarkindex + 2 + useinsertradius) * sizeof(shellface); + + // Initialize the pool of subfaces. Each subface record is eight-byte + // aligned so it has room to store an edge version (from 0 to 5) in + // the least three bits. + subfaces = new memorypool(shsize, b->shellfaceperblock, sizeof(void*), 8); + + 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, b->shellfaceperblock, sizeof(void*), 8); + + // Initialize the pool for tet-subseg connections. + tet2segpool = new memorypool(6 * sizeof(shellface), b->shellfaceperblock, sizeof(void*), 0); + // Initialize the pool for tet-subface connections. + 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); + subvertstack = new arraypool(sizeof(point), 8); + + // Initialize arraypools for surface point insertion/deletion. + caveshlist = new arraypool(sizeof(face), 8); + caveshbdlist = new arraypool(sizeof(face), 8); + cavesegshlist = new arraypool(sizeof(face), 4); + + cavetetshlist = new arraypool(sizeof(face), 8); + cavetetseglist = new arraypool(sizeof(face), 8); + caveencshlist = new arraypool(sizeof(face), 8); + caveencseglist = new arraypool(sizeof(face), 8); + } + + // Initialize the 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); } //// //// @@ -4898,58 +4818,59 @@ REAL tetgenmesh::PI = 3.14159265358979323846264338327950288419716939937510582; // // /////////////////////////////////////////////////////////////////////////////// -REAL tetgenmesh::insphere_s(REAL* pa, REAL* pb, REAL* pc, REAL* pd, REAL* pe) -{ - REAL sign; +REAL tetgenmesh::insphere_s(REAL* pa, REAL* pb, REAL* pc, REAL* pd, REAL* pe) { + REAL sign; - sign = insphere(pa, pb, pc, pd, pe); - if (sign != 0.0) { - return sign; - } - - // Symbolic perturbation. - point pt[5], swappt; - REAL oriA, oriB; - int swaps, count; - int n, i; - - pt[0] = pa; - pt[1] = pb; - pt[2] = pc; - pt[3] = pd; - pt[4] = pe; - - // Sort the five points such that their indices are in the increasing - // order. An optimized bubble sort algorithm is used, i.e., it has - // the worst case O(n^2) runtime, but it is usually much faster. - swaps = 0; // Record the total number of swaps. - n = 5; - do { - count = 0; - n = n - 1; - for (i = 0; i < n; i++) { - if (pointmark(pt[i]) > pointmark(pt[i+1])) { - swappt = pt[i]; pt[i] = pt[i+1]; pt[i+1] = swappt; - count++; - } + sign = insphere(pa, pb, pc, pd, pe); + if (sign != 0.0) { + return sign; } - 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) { + // Symbolic perturbation. + point pt[5], swappt; + REAL oriA, oriB; + int swaps, count; + int n, i; + + pt[0] = pa; + pt[1] = pb; + pt[2] = pc; + pt[3] = pd; + pt[4] = pe; + + // Sort the five points such that their indices are in the increasing + // order. An optimized bubble sort algorithm is used, i.e., it has + // the worst case O(n^2) runtime, but it is usually much faster. + swaps = 0; // Record the total number of swaps. + n = 5; + do { + count = 0; + n = n - 1; + for (i = 0; i < n; i++) { + if (pointmark(pt[i]) > pointmark(pt[i + 1])) { + swappt = pt[i]; + pt[i] = pt[i + 1]; + pt[i + 1] = swappt; + count++; + } + } + swaps += count; + } while (count > 0); // Continue if some points are swapped. + + oriA = orient3d(pt[1], pt[2], pt[3], pt[4]); + if (oriA != 0.0) { + // Flip the sign if there are odd number of swaps. + if ((swaps % 2) != 0) oriA = -oriA; + return oriA; + } + + oriB = -orient3d(pt[0], pt[2], pt[3], pt[4]); + if (oriB == 0.0) { + terminatetetgen(this, 2); + } // 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]); - if (oriB == 0.0) { - terminatetetgen(this, 2); - } - // Flip the sign if there are odd number of swaps. - if ((swaps % 2) != 0) oriB = -oriB; - return oriB; + if ((swaps % 2) != 0) oriB = -oriB; + return oriB; } /////////////////////////////////////////////////////////////////////////////// @@ -4969,61 +4890,60 @@ REAL tetgenmesh::insphere_s(REAL* pa, REAL* pb, REAL* pc, REAL* pd, REAL* pe) // // /////////////////////////////////////////////////////////////////////////////// -REAL tetgenmesh::orient4d_s(REAL* pa, REAL* pb, REAL* pc, REAL* pd, REAL* pe, - REAL aheight, REAL bheight, REAL cheight, - REAL dheight, REAL eheight) -{ - REAL sign; +REAL tetgenmesh::orient4d_s(REAL* pa, REAL* pb, REAL* pc, REAL* pd, REAL* pe, REAL aheight, + REAL bheight, REAL cheight, REAL dheight, REAL eheight) { + REAL sign; - sign = orient4d(pa, pb, pc, pd, pe, - aheight, bheight, cheight, dheight, eheight); - if (sign != 0.0) { - return sign; - } - - // Symbolic perturbation. - point pt[5], swappt; - REAL oriA, oriB; - int swaps, count; - int n, i; - - pt[0] = pa; - pt[1] = pb; - pt[2] = pc; - pt[3] = pd; - pt[4] = pe; - - // Sort the five points such that their indices are in the increasing - // order. An optimized bubble sort algorithm is used, i.e., it has - // the worst case O(n^2) runtime, but it is usually much faster. - swaps = 0; // Record the total number of swaps. - n = 5; - do { - count = 0; - n = n - 1; - for (i = 0; i < n; i++) { - if (pointmark(pt[i]) > pointmark(pt[i+1])) { - swappt = pt[i]; pt[i] = pt[i+1]; pt[i+1] = swappt; - count++; - } + sign = orient4d(pa, pb, pc, pd, pe, aheight, bheight, cheight, dheight, eheight); + if (sign != 0.0) { + return sign; } - 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]); - if (oriB == 0.0) { - terminatetetgen(this, 2); - } - // Flip the sign if there are odd number of swaps. - if ((swaps % 2) != 0) oriB = -oriB; - return oriB; + // Symbolic perturbation. + point pt[5], swappt; + REAL oriA, oriB; + int swaps, count; + int n, i; + + pt[0] = pa; + pt[1] = pb; + pt[2] = pc; + pt[3] = pd; + pt[4] = pe; + + // Sort the five points such that their indices are in the increasing + // order. An optimized bubble sort algorithm is used, i.e., it has + // the worst case O(n^2) runtime, but it is usually much faster. + swaps = 0; // Record the total number of swaps. + n = 5; + do { + count = 0; + n = n - 1; + for (i = 0; i < n; i++) { + if (pointmark(pt[i]) > pointmark(pt[i + 1])) { + swappt = pt[i]; + pt[i] = pt[i + 1]; + pt[i + 1] = swappt; + count++; + } + } + swaps += count; + } while (count > 0); // Continue if some points are swapped. + + oriA = orient3d(pt[1], pt[2], pt[3], pt[4]); + if (oriA != 0.0) { + // Flip the sign if there are odd number of swaps. + if ((swaps % 2) != 0) oriA = -oriA; + return oriA; + } + + oriB = -orient3d(pt[0], pt[2], pt[3], pt[4]); + if (oriB == 0.0) { + terminatetetgen(this, 2); + } + // Flip the sign if there are odd number of swaps. + if ((swaps % 2) != 0) oriB = -oriB; + return oriB; } /////////////////////////////////////////////////////////////////////////////// @@ -5048,829 +4968,829 @@ REAL tetgenmesh::orient4d_s(REAL* pa, REAL* pb, REAL* pc, REAL* pd, REAL* pe, // // /////////////////////////////////////////////////////////////////////////////// -#define SETVECTOR3(V, a0, a1, a2) (V)[0] = (a0); (V)[1] = (a1); (V)[2] = (a2) +#define 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) { + 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; + + 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. + // !!! A non-save return value.!!! + return 0; // DISJOINT + } + } + } -#define SWAP2(a0, a1, tmp) (tmp) = (a0); (a0) = (a1); (a1) = (tmp) + // 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); -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 abovept[3]; - REAL sA, sB, sC; - REAL s1, s2, s3, s4; - int z1; - - 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. - // !!! A non-save return value.!!! - return 0; // DISJOINT - } - } - } - - // 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); - - - if (sA < 0) { - if (sB < 0) { - if (sC < 0) { // (---). - return 0; - } else { - if (sC > 0) { // (--+). - // All points are in the right positions. - SETVECTOR3(U, A, B, C); // I3 - SETVECTOR3(V, P, Q, R); // I2 - SETVECTOR3(pu, 0, 1, 2); - SETVECTOR3(pv, 0, 1, 2); - z1 = 0; - } else { // (--0). - SETVECTOR3(U, A, B, C); // I3 - SETVECTOR3(V, P, Q, R); // I2 - SETVECTOR3(pu, 0, 1, 2); - SETVECTOR3(pv, 0, 1, 2); - z1 = 1; - } - } - } else { - if (sB > 0) { - if (sC < 0) { // (-+-). - SETVECTOR3(U, C, A, B); // PT = ST - SETVECTOR3(V, P, Q, R); // I2 - SETVECTOR3(pu, 2, 0, 1); - SETVECTOR3(pv, 0, 1, 2); - z1 = 0; - } else { - if (sC > 0) { // (-++). - SETVECTOR3(U, B, C, A); // PT = ST x ST - SETVECTOR3(V, Q, P, R); // PL = SL - SETVECTOR3(pu, 1, 2, 0); - SETVECTOR3(pv, 1, 0, 2); - z1 = 0; - } else { // (-+0). - SETVECTOR3(U, C, A, B); // PT = ST - SETVECTOR3(V, P, Q, R); // I2 - SETVECTOR3(pu, 2, 0, 1); - SETVECTOR3(pv, 0, 1, 2); - z1 = 2; - } - } - } else { - if (sC < 0) { // (-0-). - SETVECTOR3(U, C, A, B); // PT = ST - SETVECTOR3(V, P, Q, R); // I2 - SETVECTOR3(pu, 2, 0, 1); - SETVECTOR3(pv, 0, 1, 2); - z1 = 1; - } else { - if (sC > 0) { // (-0+). - SETVECTOR3(U, B, C, A); // PT = ST x ST - SETVECTOR3(V, Q, P, R); // PL = SL - SETVECTOR3(pu, 1, 2, 0); - SETVECTOR3(pv, 1, 0, 2); - z1 = 2; - } else { // (-00). - SETVECTOR3(U, B, C, A); // PT = ST x ST - SETVECTOR3(V, Q, P, R); // PL = SL - SETVECTOR3(pu, 1, 2, 0); - SETVECTOR3(pv, 1, 0, 2); - z1 = 3; - } - } - } - } - } else { - if (sA > 0) { - if (sB < 0) { - if (sC < 0) { // (+--). - SETVECTOR3(U, B, C, A); // PT = ST x ST - SETVECTOR3(V, P, Q, R); // I2 - SETVECTOR3(pu, 1, 2, 0); - SETVECTOR3(pv, 0, 1, 2); - z1 = 0; - } else { - if (sC > 0) { // (+-+). - SETVECTOR3(U, C, A, B); // PT = ST - SETVECTOR3(V, Q, P, R); // PL = SL - SETVECTOR3(pu, 2, 0, 1); - SETVECTOR3(pv, 1, 0, 2); - z1 = 0; - } else { // (+-0). - SETVECTOR3(U, C, A, B); // PT = ST - SETVECTOR3(V, Q, P, R); // PL = SL - SETVECTOR3(pu, 2, 0, 1); - SETVECTOR3(pv, 1, 0, 2); - z1 = 2; - } - } - } else { - if (sB > 0) { - if (sC < 0) { // (++-). - SETVECTOR3(U, A, B, C); // I3 - SETVECTOR3(V, Q, P, R); // PL = SL - SETVECTOR3(pu, 0, 1, 2); - SETVECTOR3(pv, 1, 0, 2); - z1 = 0; - } else { - if (sC > 0) { // (+++). - return 0; - } else { // (++0). - SETVECTOR3(U, A, B, C); // I3 - SETVECTOR3(V, Q, P, R); // PL = SL - SETVECTOR3(pu, 0, 1, 2); - SETVECTOR3(pv, 1, 0, 2); - z1 = 1; - } - } - } else { // (+0#) - if (sC < 0) { // (+0-). - SETVECTOR3(U, B, C, A); // PT = ST x ST - SETVECTOR3(V, P, Q, R); // I2 - SETVECTOR3(pu, 1, 2, 0); - SETVECTOR3(pv, 0, 1, 2); - z1 = 2; - } else { - if (sC > 0) { // (+0+). - SETVECTOR3(U, C, A, B); // PT = ST - SETVECTOR3(V, Q, P, R); // PL = SL - SETVECTOR3(pu, 2, 0, 1); - SETVECTOR3(pv, 1, 0, 2); - z1 = 1; - } else { // (+00). - SETVECTOR3(U, B, C, A); // PT = ST x ST - SETVECTOR3(V, P, Q, R); // I2 - SETVECTOR3(pu, 1, 2, 0); - SETVECTOR3(pv, 0, 1, 2); - z1 = 3; + 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) { // (0--). - SETVECTOR3(U, B, C, A); // PT = ST x ST - SETVECTOR3(V, P, Q, R); // I2 - SETVECTOR3(pu, 1, 2, 0); - SETVECTOR3(pv, 0, 1, 2); - z1 = 1; } else { - if (sC > 0) { // (0-+). - SETVECTOR3(U, A, B, C); // I3 - SETVECTOR3(V, P, Q, R); // I2 - SETVECTOR3(pu, 0, 1, 2); - SETVECTOR3(pv, 0, 1, 2); - z1 = 2; - } else { // (0-0). - SETVECTOR3(U, C, A, B); // PT = ST - SETVECTOR3(V, Q, P, R); // PL = SL - SETVECTOR3(pu, 2, 0, 1); - SETVECTOR3(pv, 1, 0, 2); - z1 = 3; - } + if (sB > 0) { + if (sC < 0) { // (-+-). + SETVECTOR3(U, C, A, B); // PT = ST + SETVECTOR3(V, P, Q, R); // I2 + SETVECTOR3(pu, 2, 0, 1); + SETVECTOR3(pv, 0, 1, 2); + z1 = 0; + } else { + if (sC > 0) { // (-++). + SETVECTOR3(U, B, C, A); // PT = ST x ST + SETVECTOR3(V, Q, P, R); // PL = SL + SETVECTOR3(pu, 1, 2, 0); + SETVECTOR3(pv, 1, 0, 2); + z1 = 0; + } else { // (-+0). + SETVECTOR3(U, C, A, B); // PT = ST + SETVECTOR3(V, P, Q, R); // I2 + SETVECTOR3(pu, 2, 0, 1); + SETVECTOR3(pv, 0, 1, 2); + z1 = 2; + } + } + } else { + if (sC < 0) { // (-0-). + SETVECTOR3(U, C, A, B); // PT = ST + SETVECTOR3(V, P, Q, R); // I2 + SETVECTOR3(pu, 2, 0, 1); + SETVECTOR3(pv, 0, 1, 2); + z1 = 1; + } else { + if (sC > 0) { // (-0+). + SETVECTOR3(U, B, C, A); // PT = ST x ST + SETVECTOR3(V, Q, P, R); // PL = SL + SETVECTOR3(pu, 1, 2, 0); + SETVECTOR3(pv, 1, 0, 2); + z1 = 2; + } else { // (-00). + SETVECTOR3(U, B, C, A); // PT = ST x ST + SETVECTOR3(V, Q, P, R); // PL = SL + SETVECTOR3(pu, 1, 2, 0); + SETVECTOR3(pv, 1, 0, 2); + z1 = 3; + } + } + } } - } else { - if (sB > 0) { - if (sC < 0) { // (0+-). - SETVECTOR3(U, A, B, C); // I3 - SETVECTOR3(V, Q, P, R); // PL = SL - SETVECTOR3(pu, 0, 1, 2); - SETVECTOR3(pv, 1, 0, 2); - z1 = 2; - } else { - if (sC > 0) { // (0++). - SETVECTOR3(U, B, C, A); // PT = ST x ST - SETVECTOR3(V, Q, P, R); // PL = SL - SETVECTOR3(pu, 1, 2, 0); - SETVECTOR3(pv, 1, 0, 2); - z1 = 1; - } else { // (0+0). - SETVECTOR3(U, C, A, B); // PT = ST - SETVECTOR3(V, P, Q, R); // I2 - SETVECTOR3(pu, 2, 0, 1); - SETVECTOR3(pv, 0, 1, 2); - z1 = 3; + } else { + if (sA > 0) { + if (sB < 0) { + if (sC < 0) { // (+--). + SETVECTOR3(U, B, C, A); // PT = ST x ST + SETVECTOR3(V, P, Q, R); // I2 + SETVECTOR3(pu, 1, 2, 0); + SETVECTOR3(pv, 0, 1, 2); + z1 = 0; + } else { + if (sC > 0) { // (+-+). + SETVECTOR3(U, C, A, B); // PT = ST + SETVECTOR3(V, Q, P, R); // PL = SL + SETVECTOR3(pu, 2, 0, 1); + SETVECTOR3(pv, 1, 0, 2); + z1 = 0; + } else { // (+-0). + SETVECTOR3(U, C, A, B); // PT = ST + SETVECTOR3(V, Q, P, R); // PL = SL + SETVECTOR3(pu, 2, 0, 1); + SETVECTOR3(pv, 1, 0, 2); + z1 = 2; + } + } + } else { + if (sB > 0) { + if (sC < 0) { // (++-). + SETVECTOR3(U, A, B, C); // I3 + SETVECTOR3(V, Q, P, R); // PL = SL + SETVECTOR3(pu, 0, 1, 2); + SETVECTOR3(pv, 1, 0, 2); + z1 = 0; + } else { + if (sC > 0) { // (+++). + return 0; + } else { // (++0). + SETVECTOR3(U, A, B, C); // I3 + SETVECTOR3(V, Q, P, R); // PL = SL + SETVECTOR3(pu, 0, 1, 2); + SETVECTOR3(pv, 1, 0, 2); + z1 = 1; + } + } + } else { // (+0#) + if (sC < 0) { // (+0-). + SETVECTOR3(U, B, C, A); // PT = ST x ST + SETVECTOR3(V, P, Q, R); // I2 + SETVECTOR3(pu, 1, 2, 0); + SETVECTOR3(pv, 0, 1, 2); + z1 = 2; + } else { + if (sC > 0) { // (+0+). + SETVECTOR3(U, C, A, B); // PT = ST + SETVECTOR3(V, Q, P, R); // PL = SL + SETVECTOR3(pu, 2, 0, 1); + SETVECTOR3(pv, 1, 0, 2); + z1 = 1; + } else { // (+00). + SETVECTOR3(U, B, C, A); // PT = ST x ST + SETVECTOR3(V, P, Q, R); // I2 + SETVECTOR3(pu, 1, 2, 0); + SETVECTOR3(pv, 0, 1, 2); + z1 = 3; + } + } + } } - } - } else { // (00#) - if (sC < 0) { // (00-). - SETVECTOR3(U, A, B, C); // I3 - SETVECTOR3(V, Q, P, R); // PL = SL - SETVECTOR3(pu, 0, 1, 2); - SETVECTOR3(pv, 1, 0, 2); - z1 = 3; - } else { - if (sC > 0) { // (00+). - SETVECTOR3(U, A, B, C); // I3 - SETVECTOR3(V, P, Q, R); // I2 - SETVECTOR3(pu, 0, 1, 2); - SETVECTOR3(pv, 0, 1, 2); - z1 = 3; - } else { // (000) - // Not possible unless ABC is degenerate. - // 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; + } else { + if (sB < 0) { + if (sC < 0) { // (0--). + SETVECTOR3(U, B, C, A); // PT = ST x ST + SETVECTOR3(V, P, Q, R); // I2 + SETVECTOR3(pu, 1, 2, 0); + SETVECTOR3(pv, 0, 1, 2); + z1 = 1; + } else { + if (sC > 0) { // (0-+). + SETVECTOR3(U, A, B, C); // I3 + SETVECTOR3(V, P, Q, R); // I2 + SETVECTOR3(pu, 0, 1, 2); + SETVECTOR3(pv, 0, 1, 2); + z1 = 2; + } else { // (0-0). + SETVECTOR3(U, C, A, B); // PT = ST + SETVECTOR3(V, Q, P, R); // PL = SL + SETVECTOR3(pu, 2, 0, 1); + SETVECTOR3(pv, 1, 0, 2); + z1 = 3; + } + } + } else { + if (sB > 0) { + if (sC < 0) { // (0+-). + SETVECTOR3(U, A, B, C); // I3 + SETVECTOR3(V, Q, P, R); // PL = SL + SETVECTOR3(pu, 0, 1, 2); + SETVECTOR3(pv, 1, 0, 2); + z1 = 2; + } else { + if (sC > 0) { // (0++). + SETVECTOR3(U, B, C, A); // PT = ST x ST + SETVECTOR3(V, Q, P, R); // PL = SL + SETVECTOR3(pu, 1, 2, 0); + SETVECTOR3(pv, 1, 0, 2); + z1 = 1; + } else { // (0+0). + SETVECTOR3(U, C, A, B); // PT = ST + SETVECTOR3(V, P, Q, R); // I2 + SETVECTOR3(pu, 2, 0, 1); + SETVECTOR3(pv, 0, 1, 2); + z1 = 3; + } + } + } else { // (00#) + if (sC < 0) { // (00-). + SETVECTOR3(U, A, B, C); // I3 + SETVECTOR3(V, Q, P, R); // PL = SL + SETVECTOR3(pu, 0, 1, 2); + SETVECTOR3(pv, 1, 0, 2); + z1 = 3; + } else { + if (sC > 0) { // (00+). + SETVECTOR3(U, A, B, C); // I3 + SETVECTOR3(V, P, Q, R); // I2 + SETVECTOR3(pu, 0, 1, 2); + SETVECTOR3(pv, 0, 1, 2); + z1 = 3; + } else { // (000) + // Not possible unless ABC is degenerate. + // 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; + } + } + } } - } } - } } - } - - 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 - - if (s1 > 0) { - return 0; - } - if (s2 < 0) { - return 0; - } - if (level == 0) { - return 1; // They are intersected. - } + 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 + if (s1 > 0) { + return 0; + } + if (s2 < 0) { + return 0; + } - if (z1 == 1) { - if (s1 == 0) { // (0###) - // C = Q. - 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) SHAREVERT; - pos[0] = pu[2]; // C - pos[1] = pv[0]; // P - types[1] = (int) DISJOINT; - } else { // (-+##) - // C in [P, Q]. - types[0] = (int) ACROSSVERT; - pos[0] = pu[2]; // C - pos[1] = pv[0]; // [P, Q] - types[1] = (int) DISJOINT; - } + if (level == 0) { + return 1; // They are intersected. } - 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 - - if (z1 == 0) { // (tritri-03) - if (s1 < 0) { - if (s3 > 0) { - if (s4 > 0) { - // [P, Q] overlaps [k, l] (-+++). - types[0] = (int) ACROSSEDGE; - pos[0] = pu[2]; // [C, A] - pos[1] = pv[0]; // [P, Q] - types[1] = (int) TOUCHFACE; - pos[2] = 3; // [A, B, C] - pos[3] = pv[1]; // Q + if (z1 == 1) { + if (s1 == 0) { // (0###) + // C = Q. + types[0] = (int)SHAREVERT; + pos[0] = pu[2]; // C + pos[1] = pv[1]; // Q + types[1] = (int)DISJOINT; } else { - if (s4 == 0) { - // Q = l, [P, Q] contains [k, l] (-++0). - types[0] = (int) ACROSSEDGE; - pos[0] = pu[2]; // [C, A] - pos[1] = pv[0]; // [P, Q] - types[1] = (int) TOUCHEDGE; - pos[2] = pu[1]; // [B, C] - pos[3] = pv[1]; // Q - } else { // s4 < 0 - // [P, Q] contains [k, l] (-++-). - types[0] = (int) ACROSSEDGE; - pos[0] = pu[2]; // [C, A] - pos[1] = pv[0]; // [P, Q] - types[1] = (int) ACROSSEDGE; - pos[2] = pu[1]; // [B, C] - pos[3] = pv[0]; // [P, Q] - } + if (s2 == 0) { // (#0##) + // C = P. + 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)ACROSSVERT; + pos[0] = pu[2]; // C + pos[1] = pv[0]; // [P, Q] + types[1] = (int)DISJOINT; + } } - } else { - if (s3 == 0) { - if (s4 > 0) { - // P = k, [P, Q] in [k, l] (-+0+). - types[0] = (int) TOUCHEDGE; - pos[0] = pu[2]; // [C, A] - pos[1] = pv[0]; // P - types[1] = (int) TOUCHFACE; - pos[2] = 3; // [A, B, C] - pos[3] = pv[1]; // Q - } else { - if (s4 == 0) { - // [P, Q] = [k, l] (-+00). - types[0] = (int) TOUCHEDGE; - pos[0] = pu[2]; // [C, A] - pos[1] = pv[0]; // P - types[1] = (int) TOUCHEDGE; - pos[2] = pu[1]; // [B, C] - pos[3] = pv[1]; // Q + 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 + + if (z1 == 0) { // (tritri-03) + if (s1 < 0) { + if (s3 > 0) { + if (s4 > 0) { + // [P, Q] overlaps [k, l] (-+++). + types[0] = (int)ACROSSEDGE; + pos[0] = pu[2]; // [C, A] + pos[1] = pv[0]; // [P, Q] + types[1] = (int)TOUCHFACE; + pos[2] = 3; // [A, B, C] + pos[3] = pv[1]; // Q + } else { + if (s4 == 0) { + // Q = l, [P, Q] contains [k, l] (-++0). + types[0] = (int)ACROSSEDGE; + pos[0] = pu[2]; // [C, A] + pos[1] = pv[0]; // [P, Q] + types[1] = (int)TOUCHEDGE; + pos[2] = pu[1]; // [B, C] + pos[3] = pv[1]; // Q + } else { // s4 < 0 + // [P, Q] contains [k, l] (-++-). + types[0] = (int)ACROSSEDGE; + pos[0] = pu[2]; // [C, A] + pos[1] = pv[0]; // [P, Q] + types[1] = (int)ACROSSEDGE; + pos[2] = pu[1]; // [B, C] + pos[3] = pv[0]; // [P, Q] + } + } } else { - // P = k, [P, Q] contains [k, l] (-+0-). - types[0] = (int) TOUCHEDGE; - pos[0] = pu[2]; // [C, A] - pos[1] = pv[0]; // P - types[1] = (int) ACROSSEDGE; - pos[2] = pu[1]; // [B, C] - pos[3] = pv[0]; // [P, Q] + if (s3 == 0) { + if (s4 > 0) { + // P = k, [P, Q] in [k, l] (-+0+). + types[0] = (int)TOUCHEDGE; + pos[0] = pu[2]; // [C, A] + pos[1] = pv[0]; // P + types[1] = (int)TOUCHFACE; + pos[2] = 3; // [A, B, C] + pos[3] = pv[1]; // Q + } else { + if (s4 == 0) { + // [P, Q] = [k, l] (-+00). + types[0] = (int)TOUCHEDGE; + pos[0] = pu[2]; // [C, A] + pos[1] = pv[0]; // P + types[1] = (int)TOUCHEDGE; + pos[2] = pu[1]; // [B, C] + pos[3] = pv[1]; // Q + } else { + // P = k, [P, Q] contains [k, l] (-+0-). + types[0] = (int)TOUCHEDGE; + pos[0] = pu[2]; // [C, A] + pos[1] = pv[0]; // P + types[1] = (int)ACROSSEDGE; + pos[2] = pu[1]; // [B, C] + pos[3] = pv[0]; // [P, Q] + } + } + } else { // s3 < 0 + if (s2 > 0) { + if (s4 > 0) { + // [P, Q] in [k, l] (-+-+). + types[0] = (int)TOUCHFACE; + pos[0] = 3; // [A, B, C] + pos[1] = pv[0]; // P + types[1] = (int)TOUCHFACE; + pos[2] = 3; // [A, B, C] + pos[3] = pv[1]; // Q + } else { + if (s4 == 0) { + // Q = l, [P, Q] in [k, l] (-+-0). + types[0] = (int)TOUCHFACE; + pos[0] = 3; // [A, B, C] + pos[1] = pv[0]; // P + types[1] = (int)TOUCHEDGE; + pos[2] = pu[1]; // [B, C] + pos[3] = pv[1]; // Q + } else { // s4 < 0 + // [P, Q] overlaps [k, l] (-+--). + types[0] = (int)TOUCHFACE; + pos[0] = 3; // [A, B, C] + pos[1] = pv[0]; // P + types[1] = (int)ACROSSEDGE; + pos[2] = pu[1]; // [B, C] + pos[3] = pv[0]; // [P, Q] + } + } + } else { // s2 == 0 + // P = l (#0##). + types[0] = (int)TOUCHEDGE; + pos[0] = pu[1]; // [B, C] + pos[1] = pv[0]; // P + types[1] = (int)DISJOINT; + } + } } - } - } else { // s3 < 0 - if (s2 > 0) { - if (s4 > 0) { - // [P, Q] in [k, l] (-+-+). - types[0] = (int) TOUCHFACE; - pos[0] = 3; // [A, B, C] - pos[1] = pv[0]; // P - types[1] = (int) TOUCHFACE; - pos[2] = 3; // [A, B, C] - pos[3] = pv[1]; // Q + } else { // s1 == 0 + // Q = k (0####) + types[0] = (int)TOUCHEDGE; + pos[0] = pu[2]; // [C, A] + pos[1] = pv[1]; // Q + types[1] = (int)DISJOINT; + } + } else if (z1 == 2) { // (tritri-23) + if (s1 < 0) { + if (s3 > 0) { + if (s4 > 0) { + // [P, Q] overlaps [A, l] (-+++). + types[0] = (int)ACROSSVERT; + pos[0] = pu[0]; // A + pos[1] = pv[0]; // [P, Q] + types[1] = (int)TOUCHFACE; + pos[2] = 3; // [A, B, C] + pos[3] = pv[1]; // Q + } else { + if (s4 == 0) { + // Q = l, [P, Q] contains [A, l] (-++0). + types[0] = (int)ACROSSVERT; + pos[0] = pu[0]; // A + pos[1] = pv[0]; // [P, Q] + types[1] = (int)TOUCHEDGE; + pos[2] = pu[1]; // [B, C] + pos[3] = pv[1]; // Q + } else { // s4 < 0 + // [P, Q] contains [A, l] (-++-). + types[0] = (int)ACROSSVERT; + pos[0] = pu[0]; // A + pos[1] = pv[0]; // [P, Q] + types[1] = (int)ACROSSEDGE; + pos[2] = pu[1]; // [B, C] + pos[3] = pv[0]; // [P, Q] + } + } } else { - if (s4 == 0) { - // Q = l, [P, Q] in [k, l] (-+-0). - types[0] = (int) TOUCHFACE; - pos[0] = 3; // [A, B, C] - pos[1] = pv[0]; // P - types[1] = (int) TOUCHEDGE; - pos[2] = pu[1]; // [B, C] - pos[3] = pv[1]; // Q - } else { // s4 < 0 - // [P, Q] overlaps [k, l] (-+--). - types[0] = (int) TOUCHFACE; - pos[0] = 3; // [A, B, C] - pos[1] = pv[0]; // P - types[1] = (int) ACROSSEDGE; - pos[2] = pu[1]; // [B, C] - pos[3] = pv[0]; // [P, Q] - } - } - } else { // s2 == 0 - // P = l (#0##). - types[0] = (int) TOUCHEDGE; - pos[0] = pu[1]; // [B, C] - pos[1] = pv[0]; // P - types[1] = (int) DISJOINT; - } - } - } - } else { // s1 == 0 - // Q = k (0####) - types[0] = (int) TOUCHEDGE; - pos[0] = pu[2]; // [C, A] - pos[1] = pv[1]; // Q - types[1] = (int) DISJOINT; - } - } else if (z1 == 2) { // (tritri-23) - if (s1 < 0) { - if (s3 > 0) { - if (s4 > 0) { - // [P, Q] overlaps [A, l] (-+++). - types[0] = (int) ACROSSVERT; - pos[0] = pu[0]; // A - pos[1] = pv[0]; // [P, Q] - types[1] = (int) TOUCHFACE; - pos[2] = 3; // [A, B, C] - pos[3] = pv[1]; // Q - } else { - if (s4 == 0) { - // Q = l, [P, Q] contains [A, l] (-++0). - types[0] = (int) ACROSSVERT; - pos[0] = pu[0]; // A - pos[1] = pv[0]; // [P, Q] - types[1] = (int) TOUCHEDGE; - pos[2] = pu[1]; // [B, C] - pos[3] = pv[1]; // Q - } else { // s4 < 0 - // [P, Q] contains [A, l] (-++-). - types[0] = (int) ACROSSVERT; - pos[0] = pu[0]; // A - pos[1] = pv[0]; // [P, Q] - types[1] = (int) ACROSSEDGE; - pos[2] = pu[1]; // [B, C] - pos[3] = pv[0]; // [P, Q] - } - } - } else { - if (s3 == 0) { - if (s4 > 0) { - // P = A, [P, Q] in [A, l] (-+0+). - types[0] = (int) SHAREVERT; - pos[0] = pu[0]; // A - pos[1] = pv[0]; // P - types[1] = (int) TOUCHFACE; - pos[2] = 3; // [A, B, C] - pos[3] = pv[1]; // Q - } else { - if (s4 == 0) { - // [P, Q] = [A, l] (-+00). - types[0] = (int) SHAREVERT; - pos[0] = pu[0]; // A - pos[1] = pv[0]; // P - types[1] = (int) TOUCHEDGE; - pos[2] = pu[1]; // [B, C] - pos[3] = pv[1]; // Q - } else { // s4 < 0 - // Q = l, [P, Q] in [A, l] (-+0-). - types[0] = (int) SHAREVERT; - pos[0] = pu[0]; // A - pos[1] = pv[0]; // P - types[1] = (int) ACROSSEDGE; - pos[2] = pu[1]; // [B, C] - pos[3] = pv[0]; // [P, Q] + if (s3 == 0) { + if (s4 > 0) { + // P = A, [P, Q] in [A, l] (-+0+). + types[0] = (int)SHAREVERT; + pos[0] = pu[0]; // A + pos[1] = pv[0]; // P + types[1] = (int)TOUCHFACE; + pos[2] = 3; // [A, B, C] + pos[3] = pv[1]; // Q + } else { + if (s4 == 0) { + // [P, Q] = [A, l] (-+00). + types[0] = (int)SHAREVERT; + pos[0] = pu[0]; // A + pos[1] = pv[0]; // P + types[1] = (int)TOUCHEDGE; + pos[2] = pu[1]; // [B, C] + pos[3] = pv[1]; // Q + } else { // s4 < 0 + // Q = l, [P, Q] in [A, l] (-+0-). + types[0] = (int)SHAREVERT; + pos[0] = pu[0]; // A + pos[1] = pv[0]; // P + types[1] = (int)ACROSSEDGE; + pos[2] = pu[1]; // [B, C] + pos[3] = pv[0]; // [P, Q] + } + } + } else { // s3 < 0 + if (s2 > 0) { + if (s4 > 0) { + // [P, Q] in [A, l] (-+-+). + types[0] = (int)TOUCHFACE; + pos[0] = 3; // [A, B, C] + pos[1] = pv[0]; // P + types[0] = (int)TOUCHFACE; + pos[0] = 3; // [A, B, C] + pos[1] = pv[1]; // Q + } else { + if (s4 == 0) { + // Q = l, [P, Q] in [A, l] (-+-0). + types[0] = (int)TOUCHFACE; + pos[0] = 3; // [A, B, C] + pos[1] = pv[0]; // P + types[0] = (int)TOUCHEDGE; + pos[0] = pu[1]; // [B, C] + pos[1] = pv[1]; // Q + } else { // s4 < 0 + // [P, Q] overlaps [A, l] (-+--). + types[0] = (int)TOUCHFACE; + pos[0] = 3; // [A, B, C] + pos[1] = pv[0]; // P + types[0] = (int)ACROSSEDGE; + pos[0] = pu[1]; // [B, C] + pos[1] = pv[0]; // [P, Q] + } + } + } else { // s2 == 0 + // P = l (#0##). + types[0] = (int)TOUCHEDGE; + pos[0] = pu[1]; // [B, C] + pos[1] = pv[0]; // P + types[1] = (int)DISJOINT; + } + } } - } - } else { // s3 < 0 - if (s2 > 0) { - if (s4 > 0) { - // [P, Q] in [A, l] (-+-+). - types[0] = (int) TOUCHFACE; - pos[0] = 3; // [A, B, C] - pos[1] = pv[0]; // P - types[0] = (int) TOUCHFACE; - pos[0] = 3; // [A, B, C] - pos[1] = pv[1]; // Q + } else { // s1 == 0 + // Q = A (0###). + types[0] = (int)SHAREVERT; + pos[0] = pu[0]; // A + pos[1] = pv[1]; // Q + types[1] = (int)DISJOINT; + } + } else if (z1 == 3) { // (tritri-33) + if (s1 < 0) { + if (s3 > 0) { + if (s4 > 0) { + // [P, Q] overlaps [A, B] (-+++). + types[0] = (int)ACROSSVERT; + pos[0] = pu[0]; // A + pos[1] = pv[0]; // [P, Q] + types[1] = (int)TOUCHEDGE; + pos[2] = pu[0]; // [A, B] + pos[3] = pv[1]; // Q + } else { + if (s4 == 0) { + // Q = B, [P, Q] contains [A, B] (-++0). + types[0] = (int)ACROSSVERT; + pos[0] = pu[0]; // A + pos[1] = pv[0]; // [P, Q] + 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)ACROSSVERT; + pos[0] = pu[0]; // A + pos[1] = pv[0]; // [P, Q] + types[1] = (int)ACROSSVERT; + pos[2] = pu[1]; // B + pos[3] = pv[0]; // [P, Q] + } + } } else { - if (s4 == 0) { - // Q = l, [P, Q] in [A, l] (-+-0). - types[0] = (int) TOUCHFACE; - pos[0] = 3; // [A, B, C] - pos[1] = pv[0]; // P - types[0] = (int) TOUCHEDGE; - pos[0] = pu[1]; // [B, C] - pos[1] = pv[1]; // Q - } else { // s4 < 0 - // [P, Q] overlaps [A, l] (-+--). - types[0] = (int) TOUCHFACE; - pos[0] = 3; // [A, B, C] - pos[1] = pv[0]; // P - types[0] = (int) ACROSSEDGE; - pos[0] = pu[1]; // [B, C] - pos[1] = pv[0]; // [P, Q] - } - } - } else { // s2 == 0 - // P = l (#0##). - types[0] = (int) TOUCHEDGE; - pos[0] = pu[1]; // [B, C] - pos[1] = pv[0]; // P - types[1] = (int) DISJOINT; - } + if (s3 == 0) { + if (s4 > 0) { + // P = A, [P, Q] in [A, B] (-+0+). + types[0] = (int)SHAREVERT; + pos[0] = pu[0]; // A + pos[1] = pv[0]; // P + types[1] = (int)TOUCHEDGE; + pos[2] = pu[0]; // [A, B] + pos[3] = pv[1]; // Q + } else { + if (s4 == 0) { + // [P, Q] = [A, B] (-+00). + types[0] = (int)SHAREEDGE; + pos[0] = pu[0]; // [A, B] + pos[1] = pv[0]; // [P, Q] + types[1] = (int)DISJOINT; + } else { // s4 < 0 + // P= A, [P, Q] in [A, B] (-+0-). + types[0] = (int)SHAREVERT; + pos[0] = pu[0]; // A + pos[1] = pv[0]; // P + types[1] = (int)ACROSSVERT; + pos[2] = pu[1]; // B + pos[3] = pv[0]; // [P, Q] + } + } + } else { // s3 < 0 + if (s2 > 0) { + if (s4 > 0) { + // [P, Q] in [A, B] (-+-+). + types[0] = (int)TOUCHEDGE; + pos[0] = pu[0]; // [A, B] + pos[1] = pv[0]; // P + types[1] = (int)TOUCHEDGE; + pos[2] = pu[0]; // [A, B] + pos[3] = pv[1]; // Q + } else { + if (s4 == 0) { + // Q = B, [P, Q] in [A, B] (-+-0). + types[0] = (int)TOUCHEDGE; + pos[0] = pu[0]; // [A, B] + pos[1] = pv[0]; // P + types[1] = (int)SHAREVERT; + pos[2] = pu[1]; // B + pos[3] = pv[1]; // Q + } else { // s4 < 0 + // [P, Q] overlaps [A, B] (-+--). + types[0] = (int)TOUCHEDGE; + pos[0] = pu[0]; // [A, B] + pos[1] = pv[0]; // P + types[1] = (int)ACROSSVERT; + pos[2] = pu[1]; // B + pos[3] = pv[0]; // [P, Q] + } + } + } else { // s2 == 0 + // P = B (#0##). + types[0] = (int)SHAREVERT; + pos[0] = pu[1]; // B + pos[1] = pv[0]; // P + types[1] = (int)DISJOINT; + } + } + } + } else { // s1 == 0 + // Q = A (0###). + types[0] = (int)SHAREVERT; + pos[0] = pu[0]; // A + pos[1] = pv[1]; // Q + types[1] = (int)DISJOINT; } - } - } else { // s1 == 0 - // Q = A (0###). - types[0] = (int) SHAREVERT; - pos[0] = pu[0]; // A - pos[1] = pv[1]; // Q - types[1] = (int) DISJOINT; } - } else if (z1 == 3) { // (tritri-33) - if (s1 < 0) { - if (s3 > 0) { - if (s4 > 0) { - // [P, Q] overlaps [A, B] (-+++). - types[0] = (int) ACROSSVERT; - pos[0] = pu[0]; // A - pos[1] = pv[0]; // [P, Q] - types[1] = (int) TOUCHEDGE; - pos[2] = pu[0]; // [A, B] - pos[3] = pv[1]; // Q + + return 4; +} + +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 s1, s2, s3; + int z1; + + if (sP < 0) { + if (sQ < 0) { // (--) disjoint + return 0; } else { - if (s4 == 0) { - // Q = B, [P, Q] contains [A, B] (-++0). - types[0] = (int) ACROSSVERT; - pos[0] = pu[0]; // A - pos[1] = pv[0]; // [P, Q] - 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) ACROSSVERT; - pos[0] = pu[0]; // A - pos[1] = pv[0]; // [P, Q] - types[1] = (int) ACROSSVERT; - pos[2] = pu[1]; // B - pos[3] = pv[0]; // [P, Q] - } + if (sQ > 0) { // (-+) + SETVECTOR3(U, A, B, C); + SETVECTOR3(V, P, Q, R); + SETVECTOR3(pu, 0, 1, 2); + SETVECTOR3(pv, 0, 1, 2); + z1 = 0; + } else { // (-0) + SETVECTOR3(U, A, B, C); + SETVECTOR3(V, P, Q, R); + SETVECTOR3(pu, 0, 1, 2); + SETVECTOR3(pv, 0, 1, 2); + z1 = 1; + } } - } else { - if (s3 == 0) { - if (s4 > 0) { - // P = A, [P, Q] in [A, B] (-+0+). - types[0] = (int) SHAREVERT; - pos[0] = pu[0]; // A - pos[1] = pv[0]; // P - types[1] = (int) TOUCHEDGE; - pos[2] = pu[0]; // [A, B] - pos[3] = pv[1]; // Q - } else { - if (s4 == 0) { - // [P, Q] = [A, B] (-+00). - types[0] = (int) SHAREEDGE; - pos[0] = pu[0]; // [A, B] - pos[1] = pv[0]; // [P, Q] - types[1] = (int) DISJOINT; - } else { // s4 < 0 - // P= A, [P, Q] in [A, B] (-+0-). - types[0] = (int) SHAREVERT; - pos[0] = pu[0]; // A - pos[1] = pv[0]; // P - types[1] = (int) ACROSSVERT; - pos[2] = pu[1]; // B - pos[3] = pv[0]; // [P, Q] + } else { + if (sP > 0) { // (+-) + if (sQ < 0) { + SETVECTOR3(U, A, B, C); + SETVECTOR3(V, Q, P, R); // P and Q are flipped. + SETVECTOR3(pu, 0, 1, 2); + SETVECTOR3(pv, 1, 0, 2); + z1 = 0; + } else { + if (sQ > 0) { // (++) disjoint + return 0; + } else { // (+0) + SETVECTOR3(U, B, A, C); // A and B are flipped. + SETVECTOR3(V, P, Q, R); + SETVECTOR3(pu, 1, 0, 2); + SETVECTOR3(pv, 0, 1, 2); + z1 = 1; + } } - } - } else { // s3 < 0 - if (s2 > 0) { - if (s4 > 0) { - // [P, Q] in [A, B] (-+-+). - types[0] = (int) TOUCHEDGE; - pos[0] = pu[0]; // [A, B] - pos[1] = pv[0]; // P - types[1] = (int) TOUCHEDGE; - pos[2] = pu[0]; // [A, B] - pos[3] = pv[1]; // Q + } else { // sP == 0 + if (sQ < 0) { // (0-) + SETVECTOR3(U, A, B, C); + SETVECTOR3(V, Q, P, R); // P and Q are flipped. + SETVECTOR3(pu, 0, 1, 2); + SETVECTOR3(pv, 1, 0, 2); + z1 = 1; } else { - if (s4 == 0) { - // Q = B, [P, Q] in [A, B] (-+-0). - types[0] = (int) TOUCHEDGE; - pos[0] = pu[0]; // [A, B] - pos[1] = pv[0]; // P - types[1] = (int) SHAREVERT; - pos[2] = pu[1]; // B - pos[3] = pv[1]; // Q - } else { // s4 < 0 - // [P, Q] overlaps [A, B] (-+--). - types[0] = (int) TOUCHEDGE; - pos[0] = pu[0]; // [A, B] - pos[1] = pv[0]; // P - types[1] = (int) ACROSSVERT; - pos[2] = pu[1]; // B - pos[3] = pv[0]; // [P, Q] - } - } - } else { // s2 == 0 - // P = B (#0##). - types[0] = (int) SHAREVERT; - pos[0] = pu[1]; // B - pos[1] = pv[0]; // P - types[1] = (int) DISJOINT; - } + if (sQ > 0) { // (0+) + SETVECTOR3(U, B, A, C); // A and B are flipped. + SETVECTOR3(V, Q, P, R); // P and Q are flipped. + SETVECTOR3(pu, 1, 0, 2); + SETVECTOR3(pv, 1, 0, 2); + z1 = 1; + } else { // (00) + // A, B, C, P, and Q are coplanar. + z1 = 2; + } + } } - } - } else { // s1 == 0 - // Q = A (0###). - types[0] = (int) SHAREVERT; - pos[0] = pu[0]; // A - pos[1] = pv[1]; // Q - types[1] = (int) DISJOINT; } - } - return 4; -} - -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 s1, s2, s3; - int z1; + if (z1 == 2) { + // The triangle and the edge are coplanar. + return tri_edge_2d(A, B, C, P, Q, R, level, types, pos); + } + s1 = orient3d(U[0], U[1], V[0], V[1]); + if (s1 < 0) { + return 0; + } - if (sP < 0) { - if (sQ < 0) { // (--) disjoint - return 0; - } else { - if (sQ > 0) { // (-+) - SETVECTOR3(U, A, B, C); - SETVECTOR3(V, P, Q, R); - SETVECTOR3(pu, 0, 1, 2); - SETVECTOR3(pv, 0, 1, 2); - z1 = 0; - } else { // (-0) - SETVECTOR3(U, A, B, C); - SETVECTOR3(V, P, Q, R); - SETVECTOR3(pu, 0, 1, 2); - SETVECTOR3(pv, 0, 1, 2); - z1 = 1; - } - } - } else { - if (sP > 0) { // (+-) - if (sQ < 0) { - SETVECTOR3(U, A, B, C); - SETVECTOR3(V, Q, P, R); // P and Q are flipped. - SETVECTOR3(pu, 0, 1, 2); - SETVECTOR3(pv, 1, 0, 2); - z1 = 0; - } else { - if (sQ > 0) { // (++) disjoint - return 0; - } else { // (+0) - SETVECTOR3(U, B, A, C); // A and B are flipped. - SETVECTOR3(V, P, Q, R); - SETVECTOR3(pu, 1, 0, 2); - SETVECTOR3(pv, 0, 1, 2); - z1 = 1; - } - } - } else { // sP == 0 - if (sQ < 0) { // (0-) - SETVECTOR3(U, A, B, C); - SETVECTOR3(V, Q, P, R); // P and Q are flipped. - SETVECTOR3(pu, 0, 1, 2); - SETVECTOR3(pv, 1, 0, 2); - z1 = 1; - } else { - if (sQ > 0) { // (0+) - SETVECTOR3(U, B, A, C); // A and B are flipped. - SETVECTOR3(V, Q, P, R); // P and Q are flipped. - SETVECTOR3(pu, 1, 0, 2); - SETVECTOR3(pv, 1, 0, 2); - z1 = 1; - } else { // (00) - // A, B, C, P, and Q are coplanar. - z1 = 2; - } - } - } - } - - if (z1 == 2) { - // The triangle and the edge are coplanar. - return tri_edge_2d(A, B, C, P, Q, R, level, types, pos); - } - - 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]); + if (s2 < 0) { + return 0; + } - 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]); + if (s3 < 0) { + return 0; + } - s3 = orient3d(U[2], U[0], V[0], V[1]); - if (s3 < 0) { - return 0; - } + if (level == 0) { + return 1; // The are intersected. + } - if (level == 0) { - return 1; // The are intersected. - } + types[1] = (int)DISJOINT; // No second intersection point. - types[1] = (int) DISJOINT; // No second intersection point. + if (z1 == 0) { + if (s1 > 0) { + if (s2 > 0) { + if (s3 > 0) { // (+++) + // [P, Q] passes interior of [A, B, C]. + types[0] = (int)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)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)ACROSSEDGE; + pos[0] = pu[1]; // [B, C] + pos[1] = 0; // [P, Q] + } else { // s3 == 0 (+00) + // [P, Q] passes C. + types[0] = (int)ACROSSVERT; + pos[0] = pu[2]; // C + pos[1] = 0; // [P, Q] + } + } + } else { // s1 == 0 + if (s2 > 0) { + if (s3 > 0) { // (0++) + // [P, Q] intersects [A, B]. + types[0] = (int)ACROSSEDGE; + pos[0] = pu[0]; // [A, B] + pos[1] = 0; // [P, Q] + } else { // s3 == 0 (0+0) + // [P, Q] passes A. + 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)ACROSSVERT; + pos[0] = pu[1]; // B + pos[1] = 0; // [P, Q] + } + } + } + } else { // z1 == 1 + if (s1 > 0) { + if (s2 > 0) { + if (s3 > 0) { // (+++) + // Q lies in [A, B, C]. + types[0] = (int)TOUCHFACE; + pos[0] = 0; // [A, B, C] + pos[1] = pv[1]; // Q + } else { // s3 == 0 (++0) + // Q lies on [C, A]. + types[0] = (int)TOUCHEDGE; + pos[0] = pu[2]; // [C, A] + pos[1] = pv[1]; // Q + } + } else { // s2 == 0 + if (s3 > 0) { // (+0+) + // Q lies on [B, C]. + types[0] = (int)TOUCHEDGE; + pos[0] = pu[1]; // [B, C] + pos[1] = pv[1]; // Q + } else { // s3 == 0 (+00) + // Q = C. + types[0] = (int)SHAREVERT; + pos[0] = pu[2]; // C + pos[1] = pv[1]; // Q + } + } + } else { // s1 == 0 + if (s2 > 0) { + if (s3 > 0) { // (0++) + // Q lies on [A, B]. + types[0] = (int)TOUCHEDGE; + pos[0] = pu[0]; // [A, B] + pos[1] = pv[1]; // Q + } else { // s3 == 0 (0+0) + // Q = A. + types[0] = (int)SHAREVERT; + pos[0] = pu[0]; // A + pos[1] = pv[1]; // Q + } + } else { // s2 == 0 + if (s3 > 0) { // (00+) + // Q = B. + types[0] = (int)SHAREVERT; + pos[0] = pu[1]; // B + pos[1] = pv[1]; // Q + } + } + } + } - if (z1 == 0) { - if (s1 > 0) { - if (s2 > 0) { - if (s3 > 0) { // (+++) - // [P, Q] passes interior of [A, B, C]. - 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) 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) ACROSSEDGE; - pos[0] = pu[1]; // [B, C] - pos[1] = 0; // [P, Q] - } else { // s3 == 0 (+00) - // [P, Q] passes C. - types[0] = (int) ACROSSVERT; - pos[0] = pu[2]; // C - pos[1] = 0; // [P, Q] - } - } - } else { // s1 == 0 - if (s2 > 0) { - if (s3 > 0) { // (0++) - // [P, Q] intersects [A, B]. - types[0] = (int) ACROSSEDGE; - pos[0] = pu[0]; // [A, B] - pos[1] = 0; // [P, Q] - } else { // s3 == 0 (0+0) - // [P, Q] passes A. - 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) ACROSSVERT; - pos[0] = pu[1]; // B - pos[1] = 0; // [P, Q] - } - } - } - } else { // z1 == 1 - if (s1 > 0) { - if (s2 > 0) { - if (s3 > 0) { // (+++) - // Q lies in [A, B, C]. - types[0] = (int) TOUCHFACE; - pos[0] = 0; // [A, B, C] - pos[1] = pv[1]; // Q - } else { // s3 == 0 (++0) - // Q lies on [C, A]. - types[0] = (int) TOUCHEDGE; - pos[0] = pu[2]; // [C, A] - pos[1] = pv[1]; // Q - } - } else { // s2 == 0 - if (s3 > 0) { // (+0+) - // Q lies on [B, C]. - types[0] = (int) TOUCHEDGE; - pos[0] = pu[1]; // [B, C] - pos[1] = pv[1]; // Q - } else { // s3 == 0 (+00) - // Q = C. - types[0] = (int) SHAREVERT; - pos[0] = pu[2]; // C - pos[1] = pv[1]; // Q - } - } - } else { // s1 == 0 - if (s2 > 0) { - if (s3 > 0) { // (0++) - // Q lies on [A, B]. - types[0] = (int) TOUCHEDGE; - pos[0] = pu[0]; // [A, B] - pos[1] = pv[1]; // Q - } else { // s3 == 0 (0+0) - // Q = A. - types[0] = (int) SHAREVERT; - pos[0] = pu[0]; // A - pos[1] = pv[1]; // Q - } - } else { // s2 == 0 - if (s3 > 0) { // (00+) - // Q = B. - types[0] = (int) SHAREVERT; - pos[0] = pu[1]; // B - pos[1] = pv[1]; // Q - } - } - } - } - - // T and E intersect in a single point. - return 2; + // T and E intersect in a single point. + return 2; } -int tetgenmesh::tri_edge_test(point A, point B, point C, point P, point Q, - point R, int level, int *types, int *pos) -{ - REAL sP, sQ; +int tetgenmesh::tri_edge_test(point A, point B, point C, point P, point Q, point R, int level, + int* types, int* pos) { + REAL sP, sQ; - // Test the locations of P and Q with respect to ABC. - sP = orient3d(A, B, C, P); - sQ = orient3d(A, B, C, Q); + // Test the locations of P and Q with respect to ABC. + sP = orient3d(A, B, C, P); + sQ = orient3d(A, B, C, Q); - return tri_edge_tail(A, B, C, P, Q, R, sP, sQ, level, types, pos); + return tri_edge_tail(A, B, C, P, Q, R, sP, sQ, level, types, pos); } /////////////////////////////////////////////////////////////////////////////// @@ -5883,132 +5803,130 @@ int tetgenmesh::tri_edge_test(point A, point B, point C, point P, point Q, // // /////////////////////////////////////////////////////////////////////////////// -int tetgenmesh::tri_edge_inter_tail(REAL* A, REAL* B, REAL* C, REAL* P, - REAL* Q, REAL s_p, REAL s_q) -{ - int types[2], pos[4]; - int ni; // =0, 2, 4 - - ni = tri_edge_tail(A, B, C, P, Q, NULL, s_p, s_q, 1, types, pos); - - 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 { - return (int) INTERSECT; - } - } else { - if (types[0] == (int) SHAREEDGE) { - return (int) SHAREEDGE; - } else { - return (int) INTERSECT; +int tetgenmesh::tri_edge_inter_tail(REAL* A, REAL* B, REAL* C, REAL* P, REAL* Q, REAL s_p, + REAL s_q) { + int types[2], pos[4]; + int ni; // =0, 2, 4 + + ni = tri_edge_tail(A, B, C, P, Q, NULL, s_p, s_q, 1, types, pos); + + 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 { + return (int)INTERSECT; + } + } else { + if (types[0] == (int)SHAREEDGE) { + return (int)SHAREEDGE; + } else { + return (int)INTERSECT; + } + } } - } } - } - return (int) DISJOINT; + return (int)DISJOINT; } -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; - - 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; - } - - 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; - } - - int abcop, abcpq, abcqo; - int shareedge = 0; - - 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; - } - - // Continue to detect whether opq and abc are intersecting or not. - int opqab, opqbc, opqca; - - 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; - } - - // 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) { - // op is coincident with an edge of abc. - return (int) SHAREEDGE; - } - if (abcpq == (int) SHAREEDGE) { - // pq is coincident with an edge of abc. - return (int) SHAREEDGE; - } - if (abcqo == (int) SHAREEDGE) { - // qo is coincident with an edge of abc. - return (int) SHAREEDGE; - } - - // They may share a vertex or disjoint. - if (abcop == (int) SHAREVERT) { - return (int) SHAREVERT; - } - if (abcpq == (int) SHAREVERT) { - // q is the coincident vertex. - return (int) SHAREVERT; - } - - // They are disjoint. - return (int) DISJOINT; +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; + + 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; + } + + 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; + } + + int abcop, abcpq, abcqo; + int shareedge = 0; + + 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; + } + + // Continue to detect whether opq and abc are intersecting or not. + int opqab, opqbc, opqca; + + 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; + } + + // 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) { + // op is coincident with an edge of abc. + return (int)SHAREEDGE; + } + if (abcpq == (int)SHAREEDGE) { + // pq is coincident with an edge of abc. + return (int)SHAREEDGE; + } + if (abcqo == (int)SHAREEDGE) { + // qo is coincident with an edge of abc. + return (int)SHAREEDGE; + } + + // They may share a vertex or disjoint. + if (abcop == (int)SHAREVERT) { + return (int)SHAREVERT; + } + if (abcpq == (int)SHAREVERT) { + // q is the coincident vertex. + return (int)SHAREVERT; + } + + // They are disjoint. + return (int)DISJOINT; } /////////////////////////////////////////////////////////////////////////////// @@ -6035,62 +5953,60 @@ int tetgenmesh::tri_tri_inter(REAL* A,REAL* B,REAL* C,REAL* O,REAL* P,REAL* Q) // // /////////////////////////////////////////////////////////////////////////////// -bool tetgenmesh::lu_decmp(REAL lu[4][4], int n, int* ps, REAL* d, int N) -{ - REAL scales[4]; - REAL pivot, biggest, mult, tempf; - int pivotindex = 0; - int i, j, k; - - *d = 1.0; // No row interchanges yet. - - for (i = N; i < n + N; i++) { // For each row. - // Find the largest element in each row for row equilibration - biggest = 0.0; - for (j = N; j < n + N; j++) - if (biggest < (tempf = fabs(lu[i][j]))) - biggest = tempf; - if (biggest != 0.0) - scales[i] = 1.0 / biggest; - else { - scales[i] = 0.0; - return false; // Zero row: singular matrix. - } - ps[i] = i; // Initialize pivot sequence. - } - - for (k = N; k < n + N - 1; k++) { // For each column. - // Find the largest element in each column to pivot around. - biggest = 0.0; - for (i = k; i < n + N; i++) { - if (biggest < (tempf = fabs(lu[ps[i]][k]) * scales[ps[i]])) { - biggest = tempf; - pivotindex = i; - } - } - if (biggest == 0.0) { - return false; // Zero column: singular matrix. - } - if (pivotindex != k) { // Update pivot sequence. - j = ps[k]; - ps[k] = ps[pivotindex]; - ps[pivotindex] = j; - *d = -(*d); // ...and change the parity of d. - } - - // Pivot, eliminating an extra variable each time - pivot = lu[ps[k]][k]; - for (i = k + 1; i < n + N; i++) { - lu[ps[i]][k] = mult = lu[ps[i]][k] / pivot; - if (mult != 0.0) { - for (j = k + 1; j < n + N; j++) - lu[ps[i]][j] -= mult * lu[ps[k]][j]; - } - } - } - - // (lu[ps[n + N - 1]][n + N - 1] == 0.0) ==> A is singular. - return lu[ps[n + N - 1]][n + N - 1] != 0.0; +bool tetgenmesh::lu_decmp(REAL lu[4][4], int n, int* ps, REAL* d, int N) { + REAL scales[4]; + REAL pivot, biggest, mult, tempf; + int pivotindex = 0; + int i, j, k; + + *d = 1.0; // No row interchanges yet. + + for (i = N; i < n + N; i++) { // For each row. + // Find the largest element in each row for row equilibration + biggest = 0.0; + for (j = N; j < n + N; j++) + if (biggest < (tempf = fabs(lu[i][j]))) biggest = tempf; + if (biggest != 0.0) + scales[i] = 1.0 / biggest; + else { + scales[i] = 0.0; + return false; // Zero row: singular matrix. + } + ps[i] = i; // Initialize pivot sequence. + } + + for (k = N; k < n + N - 1; k++) { // For each column. + // Find the largest element in each column to pivot around. + biggest = 0.0; + for (i = k; i < n + N; i++) { + if (biggest < (tempf = fabs(lu[ps[i]][k]) * scales[ps[i]])) { + biggest = tempf; + pivotindex = i; + } + } + if (biggest == 0.0) { + return false; // Zero column: singular matrix. + } + if (pivotindex != k) { // Update pivot sequence. + j = ps[k]; + ps[k] = ps[pivotindex]; + ps[pivotindex] = j; + *d = -(*d); // ...and change the parity of d. + } + + // Pivot, eliminating an extra variable each time + pivot = lu[ps[k]][k]; + for (i = k + 1; i < n + N; i++) { + lu[ps[i]][k] = mult = lu[ps[i]][k] / pivot; + if (mult != 0.0) { + for (j = k + 1; j < n + N; j++) + lu[ps[i]][j] -= mult * lu[ps[k]][j]; + } + } + } + + // (lu[ps[n + N - 1]][n + N - 1] == 0.0) ==> A is singular. + return lu[ps[n + N - 1]][n + N - 1] != 0.0; } /////////////////////////////////////////////////////////////////////////////// @@ -6108,30 +6024,31 @@ bool tetgenmesh::lu_decmp(REAL lu[4][4], int n, int* ps, REAL* d, int N) // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::lu_solve(REAL lu[4][4], int n, int* ps, REAL* b, int N) -{ - int i, j; - REAL X[4], dot; - - for (i = N; i < n + N; i++) X[i] = 0.0; - - // Vector reduction using U triangular matrix. - for (i = N; i < n + N; i++) { - dot = 0.0; - for (j = N; j < i + N; j++) - dot += lu[ps[i]][j] * X[j]; - X[i] = b[ps[i]] - dot; - } - - // Back substitution, in L triangular matrix. - for (i = n + N - 1; i >= N; i--) { - dot = 0.0; - for (j = i + 1; j < n + N; j++) - dot += lu[ps[i]][j] * X[j]; - X[i] = (X[i] - dot) / lu[ps[i]][i]; - } - - for (i = N; i < n + N; i++) b[i] = X[i]; +void tetgenmesh::lu_solve(REAL lu[4][4], int n, int* ps, REAL* b, int N) { + int i, j; + REAL X[4], dot; + + for (i = N; i < n + N; i++) + X[i] = 0.0; + + // Vector reduction using U triangular matrix. + for (i = N; i < n + N; i++) { + dot = 0.0; + for (j = N; j < i + N; j++) + dot += lu[ps[i]][j] * X[j]; + X[i] = b[ps[i]] - dot; + } + + // Back substitution, in L triangular matrix. + for (i = n + N - 1; i >= N; i--) { + dot = 0.0; + for (j = i + 1; j < n + N; j++) + dot += lu[ps[i]][j] * X[j]; + X[i] = (X[i] - dot) / lu[ps[i]][i]; + } + + for (i = N; i < n + N; i++) + b[i] = X[i]; } /////////////////////////////////////////////////////////////////////////////// @@ -6146,38 +6063,37 @@ void tetgenmesh::lu_solve(REAL lu[4][4], int n, int* ps, REAL* b, int N) // // /////////////////////////////////////////////////////////////////////////////// -REAL tetgenmesh::incircle3d(point pa, point pb, point pc, point pd) -{ - REAL area2[2], n1[3], n2[3], c[3]; - REAL sign, r, d; - - // Calculate the areas of the two triangles [a, b, c] and [b, a, d]. - facenormal(pa, pb, pc, n1, 1, NULL); - area2[0] = dot(n1, n1); - facenormal(pb, pa, pd, n2, 1, NULL); - area2[1] = dot(n2, n2); - - if (area2[0] > area2[1]) { - // Choose [a, b, c] as the base triangle. - circumsphere(pa, pb, pc, NULL, c, &r); - d = distance(c, pd); - } else { - // Choose [b, a, d] as the base triangle. - if (area2[1] > 0) { - circumsphere(pb, pa, pd, NULL, c, &r); - d = distance(c, pc); +REAL tetgenmesh::incircle3d(point pa, point pb, point pc, point pd) { + REAL area2[2], n1[3], n2[3], c[3]; + REAL sign, r, d; + + // Calculate the areas of the two triangles [a, b, c] and [b, a, d]. + facenormal(pa, pb, pc, n1, 1, NULL); + area2[0] = dot(n1, n1); + facenormal(pb, pa, pd, n2, 1, NULL); + area2[1] = dot(n2, n2); + + if (area2[0] > area2[1]) { + // Choose [a, b, c] as the base triangle. + circumsphere(pa, pb, pc, NULL, c, &r); + d = distance(c, pd); } else { - // The four points are collinear. This case only happens on the boundary. - return 0; // Return "not inside". + // 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". + } } - } - sign = d - r; - if (fabs(sign) / r < b->epsilon) { - sign = 0; - } + sign = d - r; + if (fabs(sign) / r < b->epsilon) { + sign = 0; + } - return sign; + return sign; } /////////////////////////////////////////////////////////////////////////////// @@ -6197,56 +6113,59 @@ REAL tetgenmesh::incircle3d(point pa, point pb, point pc, point pd) // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::facenormal(point pa, point pb, point pc, REAL *n, int pivot, - REAL* lav) -{ - REAL v1[3], v2[3], v3[3], *pv1, *pv2; - REAL L1, L2, L3; - - v1[0] = pb[0] - pa[0]; // edge vector v1: a->b - v1[1] = pb[1] - pa[1]; - v1[2] = pb[2] - pa[2]; - v2[0] = pa[0] - pc[0]; // edge vector v2: c->a - v2[1] = pa[1] - pc[1]; - v2[2] = pa[2] - pc[2]; - - // Default, normal is calculated by: v1 x (-v2) (see Fig. fnormal). - if (pivot > 0) { - // Choose edge vectors by Burdakov's algorithm. - v3[0] = pc[0] - pb[0]; // edge vector v3: b->c - v3[1] = pc[1] - pb[1]; - v3[2] = pc[2] - pb[2]; - L1 = dot(v1, v1); - L2 = dot(v2, v2); - L3 = dot(v3, v3); - // Sort the three edge lengths. - if (L1 < L2) { - if (L2 < L3) { - pv1 = v1; pv2 = v2; // n = v1 x (-v2). - } else { - pv1 = v3; pv2 = v1; // n = v3 x (-v1). - } +void tetgenmesh::facenormal(point pa, point pb, point pc, REAL* n, int pivot, REAL* lav) { + REAL v1[3], v2[3], v3[3], *pv1, *pv2; + REAL L1, L2, L3; + + v1[0] = pb[0] - pa[0]; // edge vector v1: a->b + v1[1] = pb[1] - pa[1]; + v1[2] = pb[2] - pa[2]; + v2[0] = pa[0] - pc[0]; // edge vector v2: c->a + v2[1] = pa[1] - pc[1]; + v2[2] = pa[2] - pc[2]; + + // Default, normal is calculated by: v1 x (-v2) (see Fig. fnormal). + if (pivot > 0) { + // Choose edge vectors by Burdakov's algorithm. + v3[0] = pc[0] - pb[0]; // edge vector v3: b->c + v3[1] = pc[1] - pb[1]; + v3[2] = pc[2] - pb[2]; + L1 = dot(v1, v1); + L2 = dot(v2, v2); + L3 = dot(v3, v3); + // Sort the three edge lengths. + if (L1 < L2) { + if (L2 < L3) { + pv1 = v1; + pv2 = v2; // n = v1 x (-v2). + } else { + pv1 = v3; + pv2 = v1; // n = v3 x (-v1). + } + } else { + if (L1 < L3) { + pv1 = v1; + pv2 = v2; // n = v1 x (-v2). + } else { + pv1 = v2; + pv2 = v3; // n = v2 x (-v3). + } + } + if (lav) { + // return the average edge length. + *lav = (sqrt(L1) + sqrt(L2) + sqrt(L3)) / 3.0; + } } else { - 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]; + 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]; } /////////////////////////////////////////////////////////////////////////////// @@ -6263,26 +6182,25 @@ void tetgenmesh::facenormal(point pa, point pb, point pc, REAL *n, int pivot, // // /////////////////////////////////////////////////////////////////////////////// -REAL tetgenmesh::shortdistance(REAL* p, REAL* e1, REAL* e2) -{ - REAL v1[3], v2[3]; - REAL len, l_p; +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]; + 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)); + len = sqrt(dot(v1, v1)); - v1[0] /= len; - v1[1] /= len; - v1[2] /= len; - l_p = dot(v1, v2); + v1[0] /= len; + v1[1] /= len; + v1[2] /= len; + l_p = dot(v1, v2); - return sqrt(dot(v2, v2) - l_p * l_p); + return sqrt(dot(v2, v2) - l_p * l_p); } /////////////////////////////////////////////////////////////////////////////// @@ -6291,42 +6209,39 @@ REAL tetgenmesh::shortdistance(REAL* p, REAL* e1, REAL* e2) // // /////////////////////////////////////////////////////////////////////////////// -REAL tetgenmesh::triarea(REAL* pa, REAL* pb, REAL* pc) -{ - REAL A[4][4]; +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) + // 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) + 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]. + 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); +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); } /////////////////////////////////////////////////////////////////////////////// @@ -6342,43 +6257,42 @@ REAL tetgenmesh::orient3dfast(REAL *pa, REAL *pb, REAL *pc, REAL *pd) // // /////////////////////////////////////////////////////////////////////////////// -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]; - v2[0] = p2[0] - o[0]; - v2[1] = p2[1] - o[1]; - v2[2] = p2[2] - o[2]; - len1 = sqrt(dot(v1, v1)); - len2 = sqrt(dot(v2, v2)); - lenlen = len1 * len2; - - costheta = dot(v1, v2) / lenlen; - if (costheta > 1.0) { - costheta = 1.0; // Roundoff. - } else if (costheta < -1.0) { - costheta = -1.0; // Roundoff. - } - theta = acos(costheta); - if (n != NULL) { - // Get a point above the face (o, p1, p2); - np[0] = o[0] + n[0]; - np[1] = o[1] + n[1]; - np[2] = o[2] + n[2]; - // Adjust theta (0 - 2 * PI). - ori = orient3d(p1, o, np, p2); - if (ori > 0.0) { - theta = 2 * PI - theta; +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]; + v2[0] = p2[0] - o[0]; + v2[1] = p2[1] - o[1]; + v2[2] = p2[2] - o[2]; + len1 = sqrt(dot(v1, v1)); + len2 = sqrt(dot(v2, v2)); + lenlen = len1 * len2; + + costheta = dot(v1, v2) / lenlen; + if (costheta > 1.0) { + costheta = 1.0; // Roundoff. + } else if (costheta < -1.0) { + costheta = -1.0; // Roundoff. + } + theta = acos(costheta); + if (n != NULL) { + // Get a point above the face (o, p1, p2); + np[0] = o[0] + n[0]; + np[1] = o[1] + n[1]; + np[2] = o[2] + n[2]; + // Adjust theta (0 - 2 * PI). + ori = orient3d(p1, o, np, p2); + if (ori > 0.0) { + theta = 2 * PI - theta; + } } - } - return theta; + return theta; } /////////////////////////////////////////////////////////////////////////////// @@ -6387,27 +6301,26 @@ REAL tetgenmesh::interiorangle(REAL* o, REAL* p1, REAL* p2, REAL* n) // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::projpt2edge(REAL* p, REAL* e1, REAL* e2, REAL* prj) -{ - REAL v1[3], v2[3]; - REAL len, l_p; - - v1[0] = e2[0] - e1[0]; - v1[1] = e2[1] - e1[1]; - v1[2] = e2[2] - e1[2]; - v2[0] = p[0] - e1[0]; - v2[1] = p[1] - e1[1]; - v2[2] = p[2] - e1[2]; - - len = sqrt(dot(v1, v1)); - v1[0] /= len; - v1[1] /= len; - v1[2] /= len; - l_p = dot(v1, v2); - - prj[0] = e1[0] + l_p * v1[0]; - prj[1] = e1[1] + l_p * v1[1]; - prj[2] = e1[2] + l_p * v1[2]; +void tetgenmesh::projpt2edge(REAL* p, REAL* e1, REAL* e2, REAL* prj) { + REAL v1[3], v2[3]; + REAL len, l_p; + + v1[0] = e2[0] - e1[0]; + v1[1] = e2[1] - e1[1]; + v1[2] = e2[2] - e1[2]; + v2[0] = p[0] - e1[0]; + v2[1] = p[1] - e1[1]; + v2[2] = p[2] - e1[2]; + + len = sqrt(dot(v1, v1)); + v1[0] /= len; + v1[1] /= len; + v1[2] /= len; + l_p = dot(v1, v2); + + prj[0] = e1[0] + l_p * v1[0]; + prj[1] = e1[1] + l_p * v1[1]; + prj[2] = e1[2] + l_p * v1[2]; } /////////////////////////////////////////////////////////////////////////////// @@ -6416,29 +6329,27 @@ void tetgenmesh::projpt2edge(REAL* p, REAL* e1, REAL* e2, REAL* prj) // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::projpt2face(REAL* p, REAL* f1, REAL* f2, REAL* f3, REAL* prj) -{ - REAL fnormal[3], v1[3]; - REAL len, dist; - - // Get the unit face normal. - facenormal(f1, f2, f3, fnormal, 1, NULL); - len = sqrt(fnormal[0]*fnormal[0] + fnormal[1]*fnormal[1] + - fnormal[2]*fnormal[2]); - fnormal[0] /= len; - fnormal[1] /= len; - fnormal[2] /= len; - // Get the vector v1 = |p - f1|. - v1[0] = p[0] - f1[0]; - v1[1] = p[1] - f1[1]; - v1[2] = p[2] - f1[2]; - // Get the project distance. - dist = dot(fnormal, v1); - - // Get the project point. - prj[0] = p[0] - dist * fnormal[0]; - prj[1] = p[1] - dist * fnormal[1]; - prj[2] = p[2] - dist * fnormal[2]; +void tetgenmesh::projpt2face(REAL* p, REAL* f1, REAL* f2, REAL* f3, REAL* prj) { + REAL fnormal[3], v1[3]; + REAL len, dist; + + // Get the unit face normal. + facenormal(f1, f2, f3, fnormal, 1, NULL); + len = sqrt(fnormal[0] * fnormal[0] + fnormal[1] * fnormal[1] + fnormal[2] * fnormal[2]); + fnormal[0] /= len; + fnormal[1] /= len; + fnormal[2] /= len; + // Get the vector v1 = |p - f1|. + v1[0] = p[0] - f1[0]; + v1[1] = p[1] - f1[1]; + v1[2] = p[2] - f1[2]; + // Get the project distance. + dist = dot(fnormal, v1); + + // Get the project point. + prj[0] = p[0] - dist * fnormal[0]; + prj[1] = p[1] - dist * fnormal[1]; + prj[2] = p[2] - dist * fnormal[2]; } /////////////////////////////////////////////////////////////////////////////// @@ -6452,92 +6363,111 @@ void tetgenmesh::projpt2face(REAL* p, REAL* f1, REAL* f2, REAL* f3, REAL* prj) // // /////////////////////////////////////////////////////////////////////////////// -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 = 0, f2 = 0, i, j; +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 = 0, f2 = 0, i, j; - vol = 0; // Check if the tet is valid or not. + 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); + // Get four normals of faces of the tet. + tetallnormal(pa, pb, pc, pd, N, &vol); - 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); - } - } - } - - 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 = 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 (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; + 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); + } + } + } + + 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 = 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 (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; } /////////////////////////////////////////////////////////////////////////////// @@ -6550,38 +6480,42 @@ bool tetgenmesh::tetalldihedral(point pa, point pb, point pc, point pd, // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::tetallnormal(point pa, point pb, point pc, point pd, - REAL N[4][3], REAL* volume) -{ - REAL A[4][4], rhs[4], D; - int indx[4]; - int i, j; +void tetgenmesh::tetallnormal(point pa, point pb, point pc, point pd, REAL N[4][3], REAL* volume) { + REAL A[4][4], rhs[4], D; + int indx[4]; + int i, j; - // get the entries of A[3][3]. - for (i = 0; i < 3; i++) A[0][i] = pa[i] - pd[i]; // d->a vec - for (i = 0; i < 3; i++) A[1][i] = pb[i] - pd[i]; // d->b vec - for (i = 0; i < 3; i++) A[2][i] = pc[i] - pd[i]; // d->c vec + // get the entries of A[3][3]. + for (i = 0; i < 3; i++) + A[0][i] = pa[i] - pd[i]; // d->a vec + for (i = 0; i < 3; i++) + A[1][i] = pb[i] - pd[i]; // d->b vec + for (i = 0; i < 3; i++) + A[2][i] = pc[i] - pd[i]; // d->c vec - // Compute the inverse of matrix A, to get 3 normals of the 4 faces. - 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; + // Compute the inverse of matrix A, to get 3 normals of the 4 faces. + 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; + } } - } } /////////////////////////////////////////////////////////////////////////////// @@ -6593,69 +6527,81 @@ void tetgenmesh::tetallnormal(point pa, point pb, point pc, point pd, // // /////////////////////////////////////////////////////////////////////////////// -REAL tetgenmesh::tetaspectratio(point pa, point pb, point pc, point pd) -{ - REAL V[6][3], edgelength[6], longlen; - REAL vda[3], vdb[3], vdc[3]; - REAL N[4][3], A[4][4], rhs[4], D; - REAL H[4], volume, minheightinv; - int indx[4]; - int i, j; - - // Set the edge vectors: V[0], ..., V[5] - for (i = 0; i < 3; i++) V[0][i] = pa[i] - pd[i]; - for (i = 0; i < 3; i++) V[1][i] = pb[i] - pd[i]; - for (i = 0; i < 3; i++) V[2][i] = pc[i] - pd[i]; - for (i = 0; i < 3; i++) V[3][i] = pb[i] - pa[i]; - for (i = 0; i < 3; i++) V[4][i] = pc[i] - pb[i]; - for (i = 0; i < 3; i++) V[5][i] = pa[i] - pc[i]; - - // Get the squares of the edge lengths. - for (i = 0; i < 6; i++) edgelength[i] = dot(V[i], V[i]); - - // Calculate the longest and shortest edge length. - longlen = edgelength[0]; - for (i = 1; i < 6; i++) { - longlen = edgelength[i] > longlen ? edgelength[i] : longlen; - } - - // Set the matrix A = [vda, vdb, vdc]^T. - for (i = 0; i < 3; i++) A[0][i] = vda[i] = pa[i] - pd[i]; - for (i = 0; i < 3; i++) A[1][i] = vdb[i] = pb[i] - pd[i]; - for (i = 0; i < 3; i++) A[2][i] = vdc[i] = pc[i] - pd[i]; - // Lu-decompose the matrix A. - lu_decmp(A, 3, indx, &D, 0); - // Get the volume of abcd. - volume = (A[indx[0]][0] * A[indx[1]][1] * A[indx[2]][2]) / 6.0; - // Check if it is zero. - if (volume == 0.0) return 1.0e+200; // A degenerate tet. - - // Compute the 4 face normals (N[0], ..., N[3]). - for (j = 0; j < 3; j++) { - for (i = 0; i < 3; i++) rhs[i] = 0.0; - rhs[j] = 1.0; // Positive means the inside direction - lu_solve(A, 3, indx, rhs, 0); - for (i = 0; i < 3; i++) N[j][i] = rhs[i]; - } - // Get the fourth normal by summing up the first three. - for (i = 0; i < 3; i++) N[3][i] = - N[0][i] - N[1][i] - N[2][i]; - // Normalized the normals. - for (i = 0; i < 4; i++) { - // H[i] is the inverse of the height of its corresponding face. - H[i] = sqrt(dot(N[i], N[i])); - // if (H[i] > 0.0) { - // for (j = 0; j < 3; j++) N[i][j] /= H[i]; - // } - } - // 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 < 4; i++) { - if (H[i] > minheightinv) minheightinv = H[i]; - } - - return sqrt(longlen) * minheightinv; +REAL tetgenmesh::tetaspectratio(point pa, point pb, point pc, point pd) { + REAL V[6][3], edgelength[6], longlen; + REAL vda[3], vdb[3], vdc[3]; + REAL N[4][3], A[4][4], rhs[4], D; + REAL H[4], volume, minheightinv; + int indx[4]; + int i, j; + + // Set the edge vectors: V[0], ..., V[5] + for (i = 0; i < 3; i++) + V[0][i] = pa[i] - pd[i]; + for (i = 0; i < 3; i++) + V[1][i] = pb[i] - pd[i]; + for (i = 0; i < 3; i++) + V[2][i] = pc[i] - pd[i]; + for (i = 0; i < 3; i++) + V[3][i] = pb[i] - pa[i]; + for (i = 0; i < 3; i++) + V[4][i] = pc[i] - pb[i]; + for (i = 0; i < 3; i++) + V[5][i] = pa[i] - pc[i]; + + // Get the squares of the edge lengths. + for (i = 0; i < 6; i++) + edgelength[i] = dot(V[i], V[i]); + + // Calculate the longest and shortest edge length. + longlen = edgelength[0]; + for (i = 1; i < 6; i++) { + longlen = edgelength[i] > longlen ? edgelength[i] : longlen; + } + + // Set the matrix A = [vda, vdb, vdc]^T. + for (i = 0; i < 3; i++) + A[0][i] = vda[i] = pa[i] - pd[i]; + for (i = 0; i < 3; i++) + A[1][i] = vdb[i] = pb[i] - pd[i]; + for (i = 0; i < 3; i++) + A[2][i] = vdc[i] = pc[i] - pd[i]; + // Lu-decompose the matrix A. + lu_decmp(A, 3, indx, &D, 0); + // Get the volume of abcd. + volume = (A[indx[0]][0] * A[indx[1]][1] * A[indx[2]][2]) / 6.0; + // Check if it is zero. + if (volume == 0.0) return 1.0e+200; // A degenerate tet. + + // Compute the 4 face normals (N[0], ..., N[3]). + for (j = 0; j < 3; j++) { + for (i = 0; i < 3; i++) + rhs[i] = 0.0; + rhs[j] = 1.0; // Positive means the inside direction + lu_solve(A, 3, indx, rhs, 0); + for (i = 0; i < 3; i++) + N[j][i] = rhs[i]; + } + // Get the fourth normal by summing up the first three. + for (i = 0; i < 3; i++) + N[3][i] = -N[0][i] - N[1][i] - N[2][i]; + // Normalized the normals. + for (i = 0; i < 4; i++) { + // H[i] is the inverse of the height of its corresponding face. + H[i] = sqrt(dot(N[i], N[i])); + // if (H[i] > 0.0) { + // for (j = 0; j < 3; j++) N[i][j] /= H[i]; + // } + } + // 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 < 4; i++) { + if (H[i] > minheightinv) minheightinv = H[i]; + } + + return sqrt(longlen) * minheightinv; } /////////////////////////////////////////////////////////////////////////////// @@ -6673,52 +6619,50 @@ REAL tetgenmesh::tetaspectratio(point pa, point pb, point pc, point pd) // // /////////////////////////////////////////////////////////////////////////////// -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]; - - // Compute the coefficient matrix A (3x3). - A[0][0] = pb[0] - pa[0]; - A[0][1] = pb[1] - pa[1]; - A[0][2] = pb[2] - pa[2]; - A[1][0] = pc[0] - pa[0]; - A[1][1] = pc[1] - pa[1]; - A[1][2] = pc[2] - pa[2]; - if (pd != NULL) { - A[2][0] = pd[0] - pa[0]; - A[2][1] = pd[1] - pa[1]; - A[2][2] = pd[2] - pa[2]; - } else { - cross(A[0], A[1], A[2]); - } - - // Compute the right hand side vector b (3x1). - rhs[0] = 0.5 * dot(A[0], A[0]); - rhs[1] = 0.5 * dot(A[1], A[1]); - if (pd != NULL) { - rhs[2] = 0.5 * dot(A[2], A[2]); - } else { - rhs[2] = 0.0; - } - - // Solve the 3 by 3 equations use LU decomposition with partial pivoting - // and backward and forward substitute.. - if (!lu_decmp(A, 3, indx, &D, 0)) { - if (radius != (REAL *) NULL) *radius = 0.0; - return false; - } - lu_solve(A, 3, indx, rhs, 0); - if (cent != (REAL *) NULL) { - cent[0] = pa[0] + rhs[0]; - cent[1] = pa[1] + rhs[1]; - cent[2] = pa[2] + rhs[2]; - } - if (radius != (REAL *) NULL) { - *radius = sqrt(rhs[0] * rhs[0] + rhs[1] * rhs[1] + rhs[2] * rhs[2]); - } - return true; +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]; + + // Compute the coefficient matrix A (3x3). + A[0][0] = pb[0] - pa[0]; + A[0][1] = pb[1] - pa[1]; + A[0][2] = pb[2] - pa[2]; + A[1][0] = pc[0] - pa[0]; + A[1][1] = pc[1] - pa[1]; + A[1][2] = pc[2] - pa[2]; + if (pd != NULL) { + A[2][0] = pd[0] - pa[0]; + A[2][1] = pd[1] - pa[1]; + A[2][2] = pd[2] - pa[2]; + } else { + cross(A[0], A[1], A[2]); + } + + // Compute the right hand side vector b (3x1). + rhs[0] = 0.5 * dot(A[0], A[0]); + rhs[1] = 0.5 * dot(A[1], A[1]); + if (pd != NULL) { + rhs[2] = 0.5 * dot(A[2], A[2]); + } else { + rhs[2] = 0.0; + } + + // Solve the 3 by 3 equations use LU decomposition with partial pivoting + // and backward and forward substitute.. + if (!lu_decmp(A, 3, indx, &D, 0)) { + if (radius != (REAL*)NULL) *radius = 0.0; + return false; + } + lu_solve(A, 3, indx, rhs, 0); + if (cent != (REAL*)NULL) { + cent[0] = pa[0] + rhs[0]; + cent[1] = pa[1] + rhs[1]; + cent[2] = pa[2] + rhs[2]; + } + if (radius != (REAL*)NULL) { + *radius = sqrt(rhs[0] * rhs[0] + rhs[1] * rhs[1] + rhs[2] * rhs[2]); + } + return true; } /////////////////////////////////////////////////////////////////////////////// @@ -6731,115 +6675,120 @@ bool tetgenmesh::circumsphere(REAL* pa, REAL* pb, REAL* pc, REAL* pd, // // /////////////////////////////////////////////////////////////////////////////// -bool tetgenmesh::orthosphere(REAL* pa, REAL* pb, REAL* pc, REAL* pd, - REAL aheight, REAL bheight, REAL cheight, - REAL dheight, REAL* orthocent, REAL* radius) -{ - REAL A[4][4], rhs[4], D; - int indx[4]; - - // Set the coefficient matrix A (4 x 4). - A[0][0] = 1.0; A[0][1] = pa[0]; A[0][2] = pa[1]; A[0][3] = pa[2]; - A[1][0] = 1.0; A[1][1] = pb[0]; A[1][2] = pb[1]; A[1][3] = pb[2]; - A[2][0] = 1.0; A[2][1] = pc[0]; A[2][2] = pc[1]; A[2][3] = pc[2]; - A[3][0] = 1.0; A[3][1] = pd[0]; A[3][2] = pd[1]; A[3][3] = pd[2]; - - // Set the right hand side vector (4 x 1). - rhs[0] = 0.5 * aheight; - rhs[1] = 0.5 * bheight; - rhs[2] = 0.5 * cheight; - rhs[3] = 0.5 * dheight; - - // Solve the 4 by 4 equations use LU decomposition with partial pivoting - // and backward and forward substitute.. - if (!lu_decmp(A, 4, indx, &D, 0)) { - if (radius != (REAL *) NULL) *radius = 0.0; - return false; - } - lu_solve(A, 4, indx, rhs, 0); - - if (orthocent != (REAL *) NULL) { - orthocent[0] = rhs[1]; - orthocent[1] = rhs[2]; - orthocent[2] = rhs[3]; - } - if (radius != (REAL *) NULL) { - // rhs[0] = - rheight / 2; - // rheight = - 2 * rhs[0]; - // = r[0]^2 + r[1]^2 + r[2]^2 - radius^2 - // radius^2 = r[0]^2 + r[1]^2 + r[2]^2 -rheight - // = r[0]^2 + r[1]^2 + r[2]^2 + 2 * rhs[0] - *radius = sqrt(rhs[1] * rhs[1] + rhs[2] * rhs[2] + rhs[3] * rhs[3] - + 2.0 * rhs[0]); - } - return true; +bool tetgenmesh::orthosphere(REAL* pa, REAL* pb, REAL* pc, REAL* pd, REAL aheight, REAL bheight, + REAL cheight, REAL dheight, REAL* orthocent, REAL* radius) { + REAL A[4][4], rhs[4], D; + int indx[4]; + + // Set the coefficient matrix A (4 x 4). + A[0][0] = 1.0; + A[0][1] = pa[0]; + A[0][2] = pa[1]; + A[0][3] = pa[2]; + A[1][0] = 1.0; + A[1][1] = pb[0]; + A[1][2] = pb[1]; + A[1][3] = pb[2]; + A[2][0] = 1.0; + A[2][1] = pc[0]; + A[2][2] = pc[1]; + A[2][3] = pc[2]; + A[3][0] = 1.0; + A[3][1] = pd[0]; + A[3][2] = pd[1]; + A[3][3] = pd[2]; + + // Set the right hand side vector (4 x 1). + rhs[0] = 0.5 * aheight; + rhs[1] = 0.5 * bheight; + rhs[2] = 0.5 * cheight; + rhs[3] = 0.5 * dheight; + + // Solve the 4 by 4 equations use LU decomposition with partial pivoting + // and backward and forward substitute.. + if (!lu_decmp(A, 4, indx, &D, 0)) { + if (radius != (REAL*)NULL) *radius = 0.0; + return false; + } + lu_solve(A, 4, indx, rhs, 0); + + if (orthocent != (REAL*)NULL) { + orthocent[0] = rhs[1]; + orthocent[1] = rhs[2]; + orthocent[2] = rhs[3]; + } + if (radius != (REAL*)NULL) { + // rhs[0] = - rheight / 2; + // rheight = - 2 * rhs[0]; + // = r[0]^2 + r[1]^2 + r[2]^2 - radius^2 + // radius^2 = r[0]^2 + r[1]^2 + r[2]^2 -rheight + // = r[0]^2 + r[1]^2 + r[2]^2 + 2 * rhs[0] + *radius = sqrt(rhs[1] * rhs[1] + rhs[2] * rhs[2] + rhs[3] * rhs[3] + 2.0 * rhs[0]); + } + return true; } -void tetgenmesh::tetcircumcenter(point tetorg, point tetdest, point tetfapex, - point tettapex, REAL *circumcenter, REAL *radius) -{ - REAL xot, yot, zot, xdt, ydt, zdt, xft, yft, zft; - REAL otlength, dtlength, ftlength; - REAL xcrossdf, ycrossdf, zcrossdf; - REAL xcrossfo, ycrossfo, zcrossfo; - REAL xcrossod, ycrossod, zcrossod; - REAL denominator; - REAL xct, yct, zct; - - //tetcircumcentercount++; - - /* Use coordinates relative to the apex of the tetrahedron. */ - xot = tetorg[0] - tettapex[0]; - yot = tetorg[1] - tettapex[1]; - zot = tetorg[2] - tettapex[2]; - xdt = tetdest[0] - tettapex[0]; - ydt = tetdest[1] - tettapex[1]; - zdt = tetdest[2] - tettapex[2]; - xft = tetfapex[0] - tettapex[0]; - yft = tetfapex[1] - tettapex[1]; - zft = tetfapex[2] - tettapex[2]; - /* Squares of lengths of the origin, destination, and face apex edges. */ - otlength = xot * xot + yot * yot + zot * zot; - dtlength = xdt * xdt + ydt * ydt + zdt * zdt; - ftlength = xft * xft + yft * yft + zft * zft; - /* Cross products of the origin, destination, and face apex vectors. */ - xcrossdf = ydt * zft - yft * zdt; - ycrossdf = zdt * xft - zft * xdt; - zcrossdf = xdt * yft - xft * ydt; - xcrossfo = yft * zot - yot * zft; - ycrossfo = zft * xot - zot * xft; - zcrossfo = xft * yot - xot * yft; - xcrossod = yot * zdt - ydt * zot; - ycrossod = zot * xdt - zdt * xot; - zcrossod = xot * ydt - xdt * yot; - - /* Calculate the denominator of all the formulae. */ - //if (noexact) { - // denominator = 0.5 / (xot * xcrossdf + yot * ycrossdf + zot * zcrossdf); - //} else { +void tetgenmesh::tetcircumcenter(point tetorg, point tetdest, point tetfapex, point tettapex, + REAL* circumcenter, REAL* radius) { + REAL xot, yot, zot, xdt, ydt, zdt, xft, yft, zft; + REAL otlength, dtlength, ftlength; + REAL xcrossdf, ycrossdf, zcrossdf; + REAL xcrossfo, ycrossfo, zcrossfo; + REAL xcrossod, ycrossod, zcrossod; + REAL denominator; + REAL xct, yct, zct; + + // tetcircumcentercount++; + + /* Use coordinates relative to the apex of the tetrahedron. */ + xot = tetorg[0] - tettapex[0]; + yot = tetorg[1] - tettapex[1]; + zot = tetorg[2] - tettapex[2]; + xdt = tetdest[0] - tettapex[0]; + ydt = tetdest[1] - tettapex[1]; + zdt = tetdest[2] - tettapex[2]; + xft = tetfapex[0] - tettapex[0]; + yft = tetfapex[1] - tettapex[1]; + zft = tetfapex[2] - tettapex[2]; + /* Squares of lengths of the origin, destination, and face apex edges. */ + otlength = xot * xot + yot * yot + zot * zot; + dtlength = xdt * xdt + ydt * ydt + zdt * zdt; + ftlength = xft * xft + yft * yft + zft * zft; + /* Cross products of the origin, destination, and face apex vectors. */ + xcrossdf = ydt * zft - yft * zdt; + ycrossdf = zdt * xft - zft * xdt; + zcrossdf = xdt * yft - xft * ydt; + xcrossfo = yft * zot - yot * zft; + ycrossfo = zft * xot - zot * xft; + zcrossfo = xft * yot - xot * yft; + xcrossod = yot * zdt - ydt * zot; + ycrossod = zot * xdt - zdt * xot; + zcrossod = xot * ydt - xdt * yot; + + /* Calculate the denominator of all the formulae. */ + // if (noexact) { + // denominator = 0.5 / (xot * xcrossdf + yot * ycrossdf + zot * zcrossdf); + //} else { /* Use the orient3d() routine to ensure a positive (and */ /* reasonably accurate) result, avoiding any possibility */ /* of division by zero. */ denominator = 0.5 / orient3d(tetorg, tetdest, tetfapex, tettapex); /* Don't count the above as an orientation test. */ - //orientcount--; - //} - - /* Calculate offset (from apex) of circumcenter. */ - xct = (otlength * xcrossdf + dtlength * xcrossfo + ftlength * xcrossod) * - denominator; - yct = (otlength * ycrossdf + dtlength * ycrossfo + ftlength * ycrossod) * - denominator; - zct = (otlength * zcrossdf + dtlength * zcrossfo + ftlength * zcrossod) * - denominator; - - circumcenter[0] = xct + tettapex[0]; - circumcenter[1] = yct + tettapex[1]; - circumcenter[2] = zct + tettapex[2]; - - if (radius != NULL) { - *radius = sqrt(xct * xct + yct * yct + zct * zct); - } + // orientcount--; + //} + + /* Calculate offset (from apex) of circumcenter. */ + xct = (otlength * xcrossdf + dtlength * xcrossfo + ftlength * xcrossod) * denominator; + yct = (otlength * ycrossdf + dtlength * ycrossfo + ftlength * ycrossod) * denominator; + zct = (otlength * zcrossdf + dtlength * zcrossfo + ftlength * zcrossod) * denominator; + + circumcenter[0] = xct + tettapex[0]; + circumcenter[1] = yct + tettapex[1]; + circumcenter[2] = zct + tettapex[2]; + + if (radius != NULL) { + *radius = sqrt(xct * xct + yct * yct + zct * zct); + } } /////////////////////////////////////////////////////////////////////////////// @@ -6865,27 +6814,23 @@ void tetgenmesh::tetcircumcenter(point tetorg, point tetdest, point tetfapex, // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::planelineint(REAL* pa, REAL* pb, REAL* pc, REAL* e1, REAL* e2, - REAL* ip, REAL* u) -{ - REAL n[3], det, det1; - - // Calculate N. - facenormal(pa, pb, pc, n, 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]); - if (det != 0.0) { - // Calculate N dot (pa - e1) - det1 = n[0] * (pa[0] - e1[0]) + n[1] * (pa[1] - e1[1]) - + n[2] * (pa[2] - e1[2]); - *u = det1 / det; - ip[0] = e1[0] + *u * (e2[0] - e1[0]); - ip[1] = e1[1] + *u * (e2[1] - e1[1]); - ip[2] = e1[2] + *u * (e2[2] - e1[2]); - } else { - *u = 0.0; - } +void tetgenmesh::planelineint(REAL* pa, REAL* pb, REAL* pc, REAL* e1, REAL* e2, REAL* ip, REAL* u) { + REAL n[3], det, det1; + + // Calculate N. + facenormal(pa, pb, pc, n, 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]); + if (det != 0.0) { + // Calculate N dot (pa - e1) + det1 = n[0] * (pa[0] - e1[0]) + n[1] * (pa[1] - e1[1]) + n[2] * (pa[2] - e1[2]); + *u = det1 / det; + ip[0] = e1[0] + *u * (e2[0] - e1[0]); + ip[1] = e1[1] + *u * (e2[1] - e1[1]); + ip[2] = e1[2] + *u * (e2[2] - e1[2]); + } else { + *u = 0.0; + } } /////////////////////////////////////////////////////////////////////////////// @@ -6900,42 +6845,43 @@ void tetgenmesh::planelineint(REAL* pa, REAL* pb, REAL* pc, REAL* e1, REAL* e2, // // /////////////////////////////////////////////////////////////////////////////// -int tetgenmesh::linelineint(REAL* A, REAL* B, REAL* C, REAL* D, REAL* P, - REAL* Q, REAL* tp, REAL* tq) -{ - REAL vab[3], vcd[3], vca[3]; - REAL vab_vab, vcd_vcd, vab_vcd; - REAL vca_vab, vca_vcd; - REAL det, eps; - int i; - - for (i = 0; i < 3; i++) { - vab[i] = B[i] - A[i]; - vcd[i] = D[i] - C[i]; - vca[i] = A[i] - C[i]; - } - - vab_vab = dot(vab, vab); - vcd_vcd = dot(vcd, vcd); - vab_vcd = dot(vab, vcd); - - 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; - } +int tetgenmesh::linelineint(REAL* A, REAL* B, REAL* C, REAL* D, REAL* P, REAL* Q, REAL* tp, + REAL* tq) { + REAL vab[3], vcd[3], vca[3]; + REAL vab_vab, vcd_vcd, vab_vcd; + REAL vca_vab, vca_vcd; + REAL det, eps; + int i; + + for (i = 0; i < 3; i++) { + vab[i] = B[i] - A[i]; + vcd[i] = D[i] - C[i]; + vca[i] = A[i] - C[i]; + } + + vab_vab = dot(vab, vab); + vcd_vcd = dot(vcd, vcd); + vab_vcd = dot(vab, vcd); + + 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; + } - vca_vab = dot(vca, vab); - vca_vcd = dot(vca, vcd); + vca_vab = dot(vca, vab); + vca_vcd = dot(vca, vcd); - *tp = (vcd_vcd * (- vca_vab) + vab_vcd * vca_vcd) / det; - *tq = (vab_vcd * (- vca_vab) + vab_vab * vca_vcd) / det; + *tp = (vcd_vcd * (-vca_vab) + vab_vcd * vca_vcd) / det; + *tq = (vab_vcd * (-vca_vab) + vab_vab * vca_vcd) / det; - 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]; + 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; + return 1; } /////////////////////////////////////////////////////////////////////////////// @@ -6959,30 +6905,29 @@ int tetgenmesh::linelineint(REAL* A, REAL* B, REAL* C, REAL* D, REAL* P, // // /////////////////////////////////////////////////////////////////////////////// -REAL tetgenmesh::tetprismvol(REAL* p0, REAL* p1, REAL* p2, REAL* p3) -{ - REAL *p4, *p5, *p6, *p7; - REAL w4, w5, w6, w7; - REAL vol[4]; - - 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]); +REAL tetgenmesh::tetprismvol(REAL* p0, REAL* p1, REAL* p2, REAL* p3) { + REAL *p4, *p5, *p6, *p7; + REAL w4, w5, w6, w7; + REAL vol[4]; + + 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]); } /////////////////////////////////////////////////////////////////////////////// @@ -6991,86 +6936,83 @@ REAL tetgenmesh::tetprismvol(REAL* p0, REAL* p1, REAL* p2, REAL* p3) // // /////////////////////////////////////////////////////////////////////////////// -bool tetgenmesh::calculateabovepoint(arraypool *facpoints, point *ppa, - point *ppb, point *ppc) -{ - point *ppt, pa, pb, pc; - REAL v1[3], v2[3], n[3]; - REAL lab, len, A, area; - REAL x, y, z; - int i; - - ppt = (point *) fastlookup(facpoints, 0); - pa = *ppt; // a is the first point. - pb = pc = NULL; // Avoid compiler warnings. - - // Get a point b s.t. the length of [a, b] is maximal. - lab = 0; - for (i = 1; i < facpoints->objects; i++) { - ppt = (point *) fastlookup(facpoints, i); - x = (*ppt)[0] - pa[0]; - y = (*ppt)[1] - pa[1]; - z = (*ppt)[2] - pa[2]; - len = x * x + y * y + z * z; - if (len > lab) { - lab = len; - pb = *ppt; - } - } - lab = sqrt(lab); - if (lab == 0) { - if (!b->quiet) { - printf("Warning: All points of a facet are coincident with %d.\n", - pointmark(pa)); - } - return false; - } - - // Get a point c s.t. the area of [a, b, c] is maximal. - v1[0] = pb[0] - pa[0]; - v1[1] = pb[1] - pa[1]; - v1[2] = pb[2] - pa[2]; - A = 0; - for (i = 1; i < facpoints->objects; i++) { - ppt = (point *) fastlookup(facpoints, i); - v2[0] = (*ppt)[0] - pa[0]; - v2[1] = (*ppt)[1] - pa[1]; - v2[2] = (*ppt)[2] - pa[2]; - cross(v1, v2, n); - area = dot(n, n); - if (area > A) { - A = area; - pc = *ppt; - } - } - if (A == 0) { - // All points are collinear. No above point. - if (!b->quiet) { - printf("Warning: All points of a facet are collinaer with [%d, %d].\n", - pointmark(pa), pointmark(pb)); - } - return false; - } - - // Calculate an above point of this facet. - facenormal(pa, pb, pc, n, 1, NULL); - len = sqrt(dot(n, n)); - n[0] /= len; - n[1] /= len; - n[2] /= len; - lab /= 2.0; // Half the maximal length. - dummypoint[0] = pa[0] + lab * n[0]; - dummypoint[1] = pa[1] + lab * n[1]; - dummypoint[2] = pa[2] + lab * n[2]; - - if (ppa != NULL) { - // Return the three points. - *ppa = pa; - *ppb = pb; - *ppc = pc; - } - - return true; +bool tetgenmesh::calculateabovepoint(arraypool* facpoints, point* ppa, point* ppb, point* ppc) { + point *ppt, pa, pb, pc; + REAL v1[3], v2[3], n[3]; + REAL lab, len, A, area; + REAL x, y, z; + int i; + + ppt = (point*)fastlookup(facpoints, 0); + pa = *ppt; // a is the first point. + pb = pc = NULL; // Avoid compiler warnings. + + // Get a point b s.t. the length of [a, b] is maximal. + lab = 0; + for (i = 1; i < facpoints->objects; i++) { + ppt = (point*)fastlookup(facpoints, i); + x = (*ppt)[0] - pa[0]; + y = (*ppt)[1] - pa[1]; + z = (*ppt)[2] - pa[2]; + len = x * x + y * y + z * z; + if (len > lab) { + lab = len; + pb = *ppt; + } + } + lab = sqrt(lab); + if (lab == 0) { + if (!b->quiet) { + printf("Warning: All points of a facet are coincident with %d.\n", pointmark(pa)); + } + return false; + } + + // Get a point c s.t. the area of [a, b, c] is maximal. + v1[0] = pb[0] - pa[0]; + v1[1] = pb[1] - pa[1]; + v1[2] = pb[2] - pa[2]; + A = 0; + for (i = 1; i < facpoints->objects; i++) { + ppt = (point*)fastlookup(facpoints, i); + v2[0] = (*ppt)[0] - pa[0]; + v2[1] = (*ppt)[1] - pa[1]; + v2[2] = (*ppt)[2] - pa[2]; + cross(v1, v2, n); + area = dot(n, n); + if (area > A) { + A = area; + pc = *ppt; + } + } + if (A == 0) { + // All points are collinear. No above point. + if (!b->quiet) { + printf("Warning: All points of a facet are collinaer with [%d, %d].\n", pointmark(pa), + pointmark(pb)); + } + return false; + } + + // Calculate an above point of this facet. + facenormal(pa, pb, pc, n, 1, NULL); + len = sqrt(dot(n, n)); + n[0] /= len; + n[1] /= len; + n[2] /= len; + lab /= 2.0; // Half the maximal length. + dummypoint[0] = pa[0] + lab * n[0]; + dummypoint[1] = pa[1] + lab * n[1]; + dummypoint[2] = pa[2] + lab * n[2]; + + if (ppa != NULL) { + // Return the three points. + *ppa = pa; + *ppb = pb; + *ppc = pc; + } + + return true; } /////////////////////////////////////////////////////////////////////////////// @@ -7081,30 +7023,29 @@ bool tetgenmesh::calculateabovepoint(arraypool *facpoints, point *ppa, // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::calculateabovepoint4(point pa, point pb, point pc, point pd) -{ - REAL n1[3], n2[3], *norm; - REAL len, len1, len2; - - // Select a base. - facenormal(pa, pb, pc, n1, 1, NULL); - len1 = sqrt(dot(n1, n1)); - facenormal(pa, pb, pd, n2, 1, NULL); - len2 = sqrt(dot(n2, n2)); - if (len1 > len2) { - norm = n1; - len = len1; - } else { - norm = n2; - len = len2; - } - 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]; +void tetgenmesh::calculateabovepoint4(point pa, point pb, point pc, point pd) { + REAL n1[3], n2[3], *norm; + REAL len, len1, len2; + + // Select a base. + facenormal(pa, pb, pc, n1, 1, NULL); + len1 = sqrt(dot(n1, n1)); + facenormal(pa, pb, pd, n2, 1, NULL); + len2 = sqrt(dot(n2, n2)); + if (len1 > len2) { + norm = n1; + len = len1; + } else { + norm = n2; + len = len2; + } + 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]; } /////////////////////////////////////////////////////////////////////////////// @@ -7117,53 +7058,50 @@ void tetgenmesh::calculateabovepoint4(point pa, point pb, point pc, point pd) // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::report_overlapping_facets(face *f1, face *f2, REAL dihedang) -{ - point pa, pb, pc, pd; - - pa = sorg(*f1); - pb = sdest(*f1); - pc = sapex(*f1); - pd = sapex(*f2); - - if (pc != pd) { - printf("Found two %s self-intersecting facets.\n", - dihedang > 0 ? "nearly" : "exactly"); - 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)); - if (dihedang > 0) { - printf("The dihedral angle between them is %g degree.\n", - dihedang / PI * 180.0); - printf("Hint: You may use -p/# to decrease the dihedral angle"); - printf(" tolerance %g (degree).\n", b->facet_overlap_ang_tol); - } - } else { - if (shellmark(*f1) != shellmark(*f2)) { - // Two identical faces from two different facet. - printf("Found two overlapping facets.\n"); +void tetgenmesh::report_overlapping_facets(face* f1, face* f2, REAL dihedang) { + point pa, pb, pc, pd; + + pa = sorg(*f1); + pb = sdest(*f1); + pc = sapex(*f1); + pd = sapex(*f2); + + if (pc != pd) { + printf("Found two %s self-intersecting facets.\n", dihedang > 0 ? "nearly" : "exactly"); + 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)); + if (dihedang > 0) { + printf("The dihedral angle between them is %g degree.\n", dihedang / PI * 180.0); + printf("Hint: You may use -p/# to decrease the dihedral angle"); + printf(" tolerance %g (degree).\n", b->facet_overlap_ang_tol); + } } else { - 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)); - } - - // Return the information - sevent.e_type = 6; - sevent.f_marker1 = shellmark(*f1); - sevent.f_vertices1[0] = pointmark(pa); - sevent.f_vertices1[1] = pointmark(pb); - sevent.f_vertices1[2] = pointmark(pc); - sevent.f_marker2 = shellmark(*f2); - sevent.f_vertices2[0] = pointmark(pa); - sevent.f_vertices2[1] = pointmark(pb); - sevent.f_vertices2[2] = pointmark(pd); - - terminatetetgen(this, 3); + if (shellmark(*f1) != shellmark(*f2)) { + // Two identical faces from two different facet. + printf("Found two overlapping facets.\n"); + } else { + 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)); + } + + // Return the information + sevent.e_type = 6; + sevent.f_marker1 = shellmark(*f1); + sevent.f_vertices1[0] = pointmark(pa); + sevent.f_vertices1[1] = pointmark(pb); + sevent.f_vertices1[2] = pointmark(pc); + sevent.f_marker2 = shellmark(*f2); + sevent.f_vertices2[0] = pointmark(pa); + sevent.f_vertices2[1] = pointmark(pb); + sevent.f_vertices2[2] = pointmark(pd); + + terminatetetgen(this, 3); } /////////////////////////////////////////////////////////////////////////////// @@ -7182,387 +7120,381 @@ void tetgenmesh::report_overlapping_facets(face *f1, face *f2, REAL dihedang) // // /////////////////////////////////////////////////////////////////////////////// -int tetgenmesh::report_selfint_edge(point e1, point e2, face *iedge, - triface* itet, enum interresult dir) -{ - point forg = NULL, fdest = NULL, fapex = NULL; - int etype = 0, geomtag = 0, facemark = 0; - - if (iedge != NULL) { - if (iedge->sh[5] != NULL) { - etype = 2; // A subface - forg = e1; - fdest = e2; - fapex = sapex(*iedge); - facemark = shellmark(*iedge); - } else { - etype = 1; // A segment - forg = farsorg(*iedge); - fdest = farsdest(*iedge); - // Get a facet containing this segment. - face parentsh; - spivot(*iedge, parentsh); - if (parentsh.sh != NULL) { - facemark = shellmark(parentsh); - } +int tetgenmesh::report_selfint_edge(point e1, point e2, face* iedge, triface* itet, + enum interresult dir) { + point forg = NULL, fdest = NULL, fapex = NULL; + int etype = 0, geomtag = 0, facemark = 0; + + if (iedge != NULL) { + if (iedge->sh[5] != NULL) { + etype = 2; // A subface + forg = e1; + fdest = e2; + fapex = sapex(*iedge); + facemark = shellmark(*iedge); + } else { + etype = 1; // A segment + forg = farsorg(*iedge); + fdest = farsdest(*iedge); + // Get a facet containing this segment. + face parentsh; + spivot(*iedge, parentsh); + if (parentsh.sh != NULL) { + facemark = shellmark(parentsh); + } + } + geomtag = shellmark(*iedge); } - geomtag = shellmark(*iedge); - } - if (dir == SHAREEDGE) { - // Two edges (segments) are coincide. - face colseg; - tsspivot1(*itet, colseg); - if (etype == 1) { - if (colseg.sh != iedge->sh) { - face parentsh; - spivot(colseg, parentsh); - printf("PLC Error: Two segments are overlapping.\n"); - printf(" Segment 1: [%d, %d] #%d (%d)\n", pointmark(sorg(colseg)), - pointmark(sdest(colseg)), shellmark(colseg), - parentsh.sh ? shellmark(parentsh) : 0); - printf(" Segment 2: [%d, %d] #%d (%d)\n", pointmark(forg), - pointmark(fdest), geomtag, facemark); - sevent.e_type = 4; - sevent.f_marker1 = (parentsh.sh ? shellmark(parentsh) : 0); - sevent.s_marker1 = shellmark(colseg); - sevent.f_vertices1[0] = pointmark( sorg(colseg)); - sevent.f_vertices1[1] = pointmark(sdest(colseg)); - sevent.f_vertices1[2] = 0; - sevent.f_marker2 = facemark; - sevent.s_marker2 = geomtag; - sevent.f_vertices2[0] = pointmark(forg); - sevent.f_vertices2[1] = pointmark(fdest); - sevent.f_vertices2[2] = 0; - } else { - // Two identical segments. Why report it? - terminatetetgen(this, 2); - } - } else if (etype == 2) { - printf("PLC Error: A segment lies in a facet.\n"); - printf(" Segment: [%d, %d] #%d\n", pointmark(sorg(colseg)), - pointmark(sdest(colseg)), shellmark(colseg)); - printf(" Facet: [%d,%d,%d] #%d\n", pointmark(forg), - pointmark(fdest), pointmark(fapex), geomtag); - sevent.e_type = 5; - sevent.f_marker1 = 0; - sevent.s_marker1 = shellmark(colseg); - sevent.f_vertices1[0] = pointmark( sorg(colseg)); - sevent.f_vertices1[1] = pointmark(sdest(colseg)); - sevent.f_vertices1[2] = 0; - sevent.f_marker2 = geomtag; - sevent.s_marker2 = 0; - sevent.f_vertices2[0] = pointmark(forg); - sevent.f_vertices2[1] = pointmark(fdest); - sevent.f_vertices2[2] = pointmark(fapex); - } - } else if (dir == SHAREFACE) { - // Two triangles (subfaces) are coincide. - face colface; - tspivot(*itet, colface); - if (etype == 2) { - if (colface.sh != iedge->sh) { - printf("PLC Error: Two facets are overlapping.\n"); - printf(" Facet 1: [%d,%d,%d] #%d\n", pointmark(forg), - pointmark(fdest), pointmark(fapex), geomtag); - printf(" Facet 2: [%d,%d,%d] #%d\n", pointmark(sorg(colface)), - pointmark(sdest(colface)), pointmark(sapex(colface)), - shellmark(colface)); - sevent.e_type = 6; - sevent.f_marker1 = geomtag; - sevent.s_marker1 = 0; - sevent.f_vertices1[0] = pointmark(forg); - sevent.f_vertices1[1] = pointmark(fdest); - sevent.f_vertices1[2] = pointmark(fapex); - sevent.f_marker2 = shellmark(colface); - sevent.s_marker2 = 0; - sevent.f_vertices2[0] = pointmark(sorg(colface)); - sevent.f_vertices2[1] = pointmark(sdest(colface)); - sevent.f_vertices2[2] = pointmark(sapex(colface)); - } else { - // Two identical subfaces. Why report it? - terminatetetgen(this, 2); - } - } else { - terminatetetgen(this, 2); - } - } else if (dir == ACROSSVERT) { - point pp = dest(*itet); - if ((pointtype(pp) == RIDGEVERTEX) || (pointtype(pp) == FACETVERTEX) - || (pointtype(pp) == VOLVERTEX)) { - if (etype == 1) { - printf("PLC Error: A vertex lies in a segment.\n"); - printf(" Vertex: [%d] (%g,%g,%g).\n",pointmark(pp),pp[0],pp[1],pp[2]); - printf(" Segment: [%d, %d] #%d (%d)\n", pointmark(forg), - pointmark(fdest), geomtag, facemark); - sevent.e_type = 7; - sevent.f_marker1 = 0; - sevent.s_marker1 = 0; - sevent.f_vertices1[0] = pointmark(pp); - sevent.f_vertices1[1] = 0; - sevent.f_vertices1[2] = 0; - sevent.f_marker2 = facemark; - sevent.s_marker2 = geomtag; - sevent.f_vertices2[0] = pointmark(forg); - sevent.f_vertices2[1] = pointmark(fdest); - sevent.f_vertices2[2] = 0; - sevent.int_point[0] = pp[0]; - sevent.int_point[1] = pp[1]; - sevent.int_point[2] = pp[2]; - } else if (etype == 2) { - printf("PLC Error: A vertex lies in a facet.\n"); - printf(" Vertex: [%d] (%g,%g,%g).\n",pointmark(pp),pp[0],pp[1],pp[2]); - printf(" Facet: [%d,%d,%d] #%d\n", pointmark(forg), pointmark(fdest), - pointmark(fapex), geomtag); - sevent.e_type = 8; - sevent.f_marker1 = 0; - sevent.s_marker1 = 0; - sevent.f_vertices1[0] = pointmark(pp); - sevent.f_vertices1[1] = 0; - sevent.f_vertices1[2] = 0; - sevent.f_marker2 = geomtag; - sevent.s_marker2 = 0; - sevent.f_vertices2[0] = pointmark(forg); - sevent.f_vertices2[1] = pointmark(fdest); - sevent.f_vertices2[2] = pointmark(fapex); - sevent.int_point[0] = pp[0]; - sevent.int_point[1] = pp[1]; - sevent.int_point[2] = pp[2]; - } - } else if (pointtype(pp) == FREESEGVERTEX) { - face parentseg, parentsh; - sdecode(point2sh(pp), parentseg); - spivot(parentseg, parentsh); - if (parentseg.sh != NULL) { - point p1 = farsorg(parentseg); - point p2 = farsdest(parentseg); - if (etype == 1) { - printf("PLC Error: Two segments intersect at point (%g,%g,%g).\n", - pp[0], pp[1], pp[2]); - printf(" Segment 1: [%d, %d], #%d (%d)\n", pointmark(forg), - pointmark(fdest), geomtag, facemark); - printf(" Segment 2: [%d, %d], #%d (%d)\n", pointmark(p1), - pointmark(p2), shellmark(parentseg), - parentsh.sh ? shellmark(parentsh) : 0); - sevent.e_type = 1; - sevent.f_marker1 = facemark; - sevent.s_marker1 = geomtag; - sevent.f_vertices1[0] = pointmark(forg); - sevent.f_vertices1[1] = pointmark(fdest); - sevent.f_vertices1[2] = 0; - sevent.f_marker2 = (parentsh.sh ? shellmark(parentsh) : 0); - sevent.s_marker2 = shellmark(parentseg); - sevent.f_vertices2[0] = pointmark(p1); - sevent.f_vertices2[1] = pointmark(p2); - sevent.f_vertices2[2] = 0; - sevent.int_point[0] = pp[0]; - sevent.int_point[1] = pp[1]; - sevent.int_point[2] = pp[2]; - } else if (etype == 2) { - printf("PLC Error: A segment and a facet intersect at point"); - printf(" (%g,%g,%g).\n", pp[0], pp[1], pp[2]); - printf(" Segment: [%d, %d], #%d (%d)\n", pointmark(p1), - pointmark(p2), shellmark(parentseg), - parentsh.sh ? shellmark(parentsh) : 0); - printf(" Facet: [%d,%d,%d] #%d\n", pointmark(forg), - pointmark(fdest), pointmark(fapex), geomtag); - sevent.e_type = 2; - sevent.f_marker1 = (parentsh.sh ? shellmark(parentsh) : 0); - sevent.s_marker1 = shellmark(parentseg); - sevent.f_vertices1[0] = pointmark(p1); - sevent.f_vertices1[1] = pointmark(p2); - sevent.f_vertices1[2] = 0; - sevent.f_marker2 = geomtag; - sevent.s_marker2 = 0; - sevent.f_vertices2[0] = pointmark(forg); - sevent.f_vertices2[1] = pointmark(fdest); - sevent.f_vertices2[2] = pointmark(fapex); - sevent.int_point[0] = pp[0]; - sevent.int_point[1] = pp[1]; - sevent.int_point[2] = pp[2]; - } - } else { - terminatetetgen(this, 2); // Report a bug. - } - } else if (pointtype(pp) == FREEFACETVERTEX) { - face parentsh; - sdecode(point2sh(pp), parentsh); - if (parentsh.sh != NULL) { - point p1 = sorg(parentsh); - point p2 = sdest(parentsh); - point p3 = sapex(parentsh); + if (dir == SHAREEDGE) { + // Two edges (segments) are coincide. + face colseg; + tsspivot1(*itet, colseg); if (etype == 1) { - printf("PLC Error: A segment and a facet intersect at point"); - printf(" (%g,%g,%g).\n", pp[0], pp[1], pp[2]); - printf(" Segment : [%d, %d], #%d (%d)\n", pointmark(forg), - pointmark(fdest), geomtag, facemark); - printf(" Facet : [%d, %d, %d] #%d.\n", pointmark(p1), - pointmark(p2), pointmark(p3), shellmark(parentsh)); - sevent.e_type = 2; - sevent.f_marker1 = facemark; - sevent.s_marker1 = geomtag; - sevent.f_vertices1[0] = pointmark(forg); - sevent.f_vertices1[1] = pointmark(fdest); - sevent.f_vertices1[2] = 0; - sevent.f_marker2 = shellmark(parentsh); - sevent.s_marker2 = 0; - sevent.f_vertices2[0] = pointmark(p1); - sevent.f_vertices2[1] = pointmark(p2); - sevent.f_vertices2[2] = pointmark(p3); - sevent.int_point[0] = pp[0]; - sevent.int_point[1] = pp[1]; - sevent.int_point[2] = pp[2]; + if (colseg.sh != iedge->sh) { + face parentsh; + spivot(colseg, parentsh); + printf("PLC Error: Two segments are overlapping.\n"); + printf(" Segment 1: [%d, %d] #%d (%d)\n", pointmark(sorg(colseg)), + pointmark(sdest(colseg)), shellmark(colseg), + parentsh.sh ? shellmark(parentsh) : 0); + printf(" Segment 2: [%d, %d] #%d (%d)\n", pointmark(forg), pointmark(fdest), + geomtag, facemark); + sevent.e_type = 4; + sevent.f_marker1 = (parentsh.sh ? shellmark(parentsh) : 0); + sevent.s_marker1 = shellmark(colseg); + sevent.f_vertices1[0] = pointmark(sorg(colseg)); + sevent.f_vertices1[1] = pointmark(sdest(colseg)); + sevent.f_vertices1[2] = 0; + sevent.f_marker2 = facemark; + sevent.s_marker2 = geomtag; + sevent.f_vertices2[0] = pointmark(forg); + sevent.f_vertices2[1] = pointmark(fdest); + sevent.f_vertices2[2] = 0; + } else { + // Two identical segments. Why report it? + terminatetetgen(this, 2); + } } else if (etype == 2) { - printf("PLC Error: Two facets intersect at point (%g,%g,%g).\n", - pp[0], pp[1], pp[2]); - printf(" Facet 1: [%d, %d, %d] #%d.\n", pointmark(forg), - pointmark(fdest), pointmark(fapex), geomtag); - printf(" Facet 2: [%d, %d, %d] #%d.\n", pointmark(p1), - pointmark(p2), pointmark(p3), shellmark(parentsh)); - sevent.e_type = 3; - sevent.f_marker1 = geomtag; - sevent.s_marker1 = 0; - sevent.f_vertices1[0] = pointmark(forg); - sevent.f_vertices1[1] = pointmark(fdest); - sevent.f_vertices1[2] = pointmark(fapex); - sevent.f_marker2 = shellmark(parentsh); - sevent.s_marker2 = 0; - sevent.f_vertices2[0] = pointmark(p1); - sevent.f_vertices2[1] = pointmark(p2); - sevent.f_vertices2[2] = pointmark(p3); - sevent.int_point[0] = pp[0]; - sevent.int_point[1] = pp[1]; - sevent.int_point[2] = pp[2]; - } - } else { - terminatetetgen(this, 2); // Report a bug. - } - } else if (pointtype(pp) == FREEVOLVERTEX) { - // This is not a PLC error. - // We should shift the vertex. - // not down yet. - terminatetetgen(this, 2); // Report a bug. + printf("PLC Error: A segment lies in a facet.\n"); + printf(" Segment: [%d, %d] #%d\n", pointmark(sorg(colseg)), pointmark(sdest(colseg)), + shellmark(colseg)); + printf(" Facet: [%d,%d,%d] #%d\n", pointmark(forg), pointmark(fdest), + pointmark(fapex), geomtag); + sevent.e_type = 5; + sevent.f_marker1 = 0; + sevent.s_marker1 = shellmark(colseg); + sevent.f_vertices1[0] = pointmark(sorg(colseg)); + sevent.f_vertices1[1] = pointmark(sdest(colseg)); + sevent.f_vertices1[2] = 0; + sevent.f_marker2 = geomtag; + sevent.s_marker2 = 0; + sevent.f_vertices2[0] = pointmark(forg); + sevent.f_vertices2[1] = pointmark(fdest); + sevent.f_vertices2[2] = pointmark(fapex); + } + } else if (dir == SHAREFACE) { + // Two triangles (subfaces) are coincide. + face colface; + tspivot(*itet, colface); + if (etype == 2) { + if (colface.sh != iedge->sh) { + printf("PLC Error: Two facets are overlapping.\n"); + printf(" Facet 1: [%d,%d,%d] #%d\n", pointmark(forg), pointmark(fdest), + pointmark(fapex), geomtag); + printf(" Facet 2: [%d,%d,%d] #%d\n", pointmark(sorg(colface)), + pointmark(sdest(colface)), pointmark(sapex(colface)), shellmark(colface)); + sevent.e_type = 6; + sevent.f_marker1 = geomtag; + sevent.s_marker1 = 0; + sevent.f_vertices1[0] = pointmark(forg); + sevent.f_vertices1[1] = pointmark(fdest); + sevent.f_vertices1[2] = pointmark(fapex); + sevent.f_marker2 = shellmark(colface); + sevent.s_marker2 = 0; + sevent.f_vertices2[0] = pointmark(sorg(colface)); + sevent.f_vertices2[1] = pointmark(sdest(colface)); + sevent.f_vertices2[2] = pointmark(sapex(colface)); + } else { + // Two identical subfaces. Why report it? + terminatetetgen(this, 2); + } + } else { + terminatetetgen(this, 2); + } + } else if (dir == ACROSSVERT) { + point pp = dest(*itet); + if ((pointtype(pp) == RIDGEVERTEX) || (pointtype(pp) == FACETVERTEX) || + (pointtype(pp) == VOLVERTEX)) { + if (etype == 1) { + printf("PLC Error: A vertex lies in a segment.\n"); + printf(" Vertex: [%d] (%g,%g,%g).\n", pointmark(pp), pp[0], pp[1], pp[2]); + printf(" Segment: [%d, %d] #%d (%d)\n", pointmark(forg), pointmark(fdest), geomtag, + facemark); + sevent.e_type = 7; + sevent.f_marker1 = 0; + sevent.s_marker1 = 0; + sevent.f_vertices1[0] = pointmark(pp); + sevent.f_vertices1[1] = 0; + sevent.f_vertices1[2] = 0; + sevent.f_marker2 = facemark; + sevent.s_marker2 = geomtag; + sevent.f_vertices2[0] = pointmark(forg); + sevent.f_vertices2[1] = pointmark(fdest); + sevent.f_vertices2[2] = 0; + sevent.int_point[0] = pp[0]; + sevent.int_point[1] = pp[1]; + sevent.int_point[2] = pp[2]; + } else if (etype == 2) { + printf("PLC Error: A vertex lies in a facet.\n"); + printf(" Vertex: [%d] (%g,%g,%g).\n", pointmark(pp), pp[0], pp[1], pp[2]); + printf(" Facet: [%d,%d,%d] #%d\n", pointmark(forg), pointmark(fdest), + pointmark(fapex), geomtag); + sevent.e_type = 8; + sevent.f_marker1 = 0; + sevent.s_marker1 = 0; + sevent.f_vertices1[0] = pointmark(pp); + sevent.f_vertices1[1] = 0; + sevent.f_vertices1[2] = 0; + sevent.f_marker2 = geomtag; + sevent.s_marker2 = 0; + sevent.f_vertices2[0] = pointmark(forg); + sevent.f_vertices2[1] = pointmark(fdest); + sevent.f_vertices2[2] = pointmark(fapex); + sevent.int_point[0] = pp[0]; + sevent.int_point[1] = pp[1]; + sevent.int_point[2] = pp[2]; + } + } else if (pointtype(pp) == FREESEGVERTEX) { + face parentseg, parentsh; + sdecode(point2sh(pp), parentseg); + spivot(parentseg, parentsh); + if (parentseg.sh != NULL) { + point p1 = farsorg(parentseg); + point p2 = farsdest(parentseg); + if (etype == 1) { + printf("PLC Error: Two segments intersect at point (%g,%g,%g).\n", pp[0], + pp[1], pp[2]); + printf(" Segment 1: [%d, %d], #%d (%d)\n", pointmark(forg), pointmark(fdest), + geomtag, facemark); + printf(" Segment 2: [%d, %d], #%d (%d)\n", pointmark(p1), pointmark(p2), + shellmark(parentseg), parentsh.sh ? shellmark(parentsh) : 0); + sevent.e_type = 1; + sevent.f_marker1 = facemark; + sevent.s_marker1 = geomtag; + sevent.f_vertices1[0] = pointmark(forg); + sevent.f_vertices1[1] = pointmark(fdest); + sevent.f_vertices1[2] = 0; + sevent.f_marker2 = (parentsh.sh ? shellmark(parentsh) : 0); + sevent.s_marker2 = shellmark(parentseg); + sevent.f_vertices2[0] = pointmark(p1); + sevent.f_vertices2[1] = pointmark(p2); + sevent.f_vertices2[2] = 0; + sevent.int_point[0] = pp[0]; + sevent.int_point[1] = pp[1]; + sevent.int_point[2] = pp[2]; + } else if (etype == 2) { + printf("PLC Error: A segment and a facet intersect at point"); + printf(" (%g,%g,%g).\n", pp[0], pp[1], pp[2]); + printf(" Segment: [%d, %d], #%d (%d)\n", pointmark(p1), pointmark(p2), + shellmark(parentseg), parentsh.sh ? shellmark(parentsh) : 0); + printf(" Facet: [%d,%d,%d] #%d\n", pointmark(forg), pointmark(fdest), + pointmark(fapex), geomtag); + sevent.e_type = 2; + sevent.f_marker1 = (parentsh.sh ? shellmark(parentsh) : 0); + sevent.s_marker1 = shellmark(parentseg); + sevent.f_vertices1[0] = pointmark(p1); + sevent.f_vertices1[1] = pointmark(p2); + sevent.f_vertices1[2] = 0; + sevent.f_marker2 = geomtag; + sevent.s_marker2 = 0; + sevent.f_vertices2[0] = pointmark(forg); + sevent.f_vertices2[1] = pointmark(fdest); + sevent.f_vertices2[2] = pointmark(fapex); + sevent.int_point[0] = pp[0]; + sevent.int_point[1] = pp[1]; + sevent.int_point[2] = pp[2]; + } + } else { + terminatetetgen(this, 2); // Report a bug. + } + } else if (pointtype(pp) == FREEFACETVERTEX) { + face parentsh; + sdecode(point2sh(pp), parentsh); + if (parentsh.sh != NULL) { + point p1 = sorg(parentsh); + point p2 = sdest(parentsh); + point p3 = sapex(parentsh); + if (etype == 1) { + printf("PLC Error: A segment and a facet intersect at point"); + printf(" (%g,%g,%g).\n", pp[0], pp[1], pp[2]); + printf(" Segment : [%d, %d], #%d (%d)\n", pointmark(forg), pointmark(fdest), + geomtag, facemark); + printf(" Facet : [%d, %d, %d] #%d.\n", pointmark(p1), pointmark(p2), + pointmark(p3), shellmark(parentsh)); + sevent.e_type = 2; + sevent.f_marker1 = facemark; + sevent.s_marker1 = geomtag; + sevent.f_vertices1[0] = pointmark(forg); + sevent.f_vertices1[1] = pointmark(fdest); + sevent.f_vertices1[2] = 0; + sevent.f_marker2 = shellmark(parentsh); + sevent.s_marker2 = 0; + sevent.f_vertices2[0] = pointmark(p1); + sevent.f_vertices2[1] = pointmark(p2); + sevent.f_vertices2[2] = pointmark(p3); + sevent.int_point[0] = pp[0]; + sevent.int_point[1] = pp[1]; + sevent.int_point[2] = pp[2]; + } else if (etype == 2) { + printf("PLC Error: Two facets intersect at point (%g,%g,%g).\n", pp[0], pp[1], + pp[2]); + printf(" Facet 1: [%d, %d, %d] #%d.\n", pointmark(forg), pointmark(fdest), + pointmark(fapex), geomtag); + printf(" Facet 2: [%d, %d, %d] #%d.\n", pointmark(p1), pointmark(p2), + pointmark(p3), shellmark(parentsh)); + sevent.e_type = 3; + sevent.f_marker1 = geomtag; + sevent.s_marker1 = 0; + sevent.f_vertices1[0] = pointmark(forg); + sevent.f_vertices1[1] = pointmark(fdest); + sevent.f_vertices1[2] = pointmark(fapex); + sevent.f_marker2 = shellmark(parentsh); + sevent.s_marker2 = 0; + sevent.f_vertices2[0] = pointmark(p1); + sevent.f_vertices2[1] = pointmark(p2); + sevent.f_vertices2[2] = pointmark(p3); + sevent.int_point[0] = pp[0]; + sevent.int_point[1] = pp[1]; + sevent.int_point[2] = pp[2]; + } + } else { + terminatetetgen(this, 2); // Report a bug. + } + } else if (pointtype(pp) == FREEVOLVERTEX) { + // This is not a PLC error. + // We should shift the vertex. + // not down yet. + terminatetetgen(this, 2); // Report a bug. + } else { + terminatetetgen(this, 2); // Report a bug. + } + terminatetetgen(this, 3); + } else if (dir == ACROSSEDGE) { + if (issubseg(*itet)) { + face checkseg; + tsspivot1(*itet, checkseg); + face parentsh; + spivot(checkseg, parentsh); + // Calulcate the intersecting point. + point p1 = sorg(checkseg); + point p2 = sdest(checkseg); + REAL P[3], Q[3], tp = 0, tq = 0; + linelineint(e1, e2, p1, p2, P, Q, &tp, &tq); + if (etype == 1) { + printf("PLC Error: Two segments intersect at point (%g,%g,%g).\n", P[0], P[1], + P[2]); + printf(" Segment 1: [%d, %d] #%d (%d)\n", pointmark(forg), pointmark(fdest), + geomtag, facemark); + printf(" Segment 2: [%d, %d] #%d (%d)\n", pointmark(p1), pointmark(p2), + shellmark(checkseg), parentsh.sh ? shellmark(parentsh) : 0); + sevent.e_type = 1; + sevent.f_marker1 = facemark; + sevent.s_marker1 = geomtag; + sevent.f_vertices1[0] = pointmark(forg); + sevent.f_vertices1[1] = pointmark(fdest); + sevent.f_vertices1[2] = 0; + sevent.f_marker2 = (parentsh.sh ? shellmark(parentsh) : 0); + sevent.s_marker2 = shellmark(checkseg); + sevent.f_vertices2[0] = pointmark(p1); + sevent.f_vertices2[1] = pointmark(p2); + sevent.f_vertices2[2] = 0; + sevent.int_point[0] = P[0]; + sevent.int_point[1] = P[1]; + sevent.int_point[2] = P[2]; + } else if (etype == 2) { + printf("PLC Error: A segment and a facet intersect at point"); + printf(" (%g,%g,%g).\n", P[0], P[1], P[2]); + printf(" Segment: [%d, %d] #%d (%d)\n", pointmark(p1), pointmark(p2), + shellmark(checkseg), parentsh.sh ? shellmark(parentsh) : 0); + printf(" Facet: [%d, %d, %d] #%d.\n", pointmark(forg), pointmark(fdest), + pointmark(fapex), geomtag); + sevent.e_type = 2; + sevent.f_marker1 = (parentsh.sh ? shellmark(parentsh) : 0); + sevent.s_marker1 = shellmark(checkseg); + sevent.f_vertices1[0] = pointmark(p1); + sevent.f_vertices1[1] = pointmark(p2); + sevent.f_vertices1[2] = 0; + sevent.f_marker2 = geomtag; + sevent.s_marker2 = 0; + sevent.f_vertices2[0] = pointmark(forg); + sevent.f_vertices2[1] = pointmark(fdest); + sevent.f_vertices2[2] = pointmark(fapex); + sevent.int_point[0] = P[0]; + sevent.int_point[1] = P[1]; + sevent.int_point[2] = P[2]; + } + terminatetetgen(this, 3); + } + } else if (dir == ACROSSFACE) { + if (issubface(*itet)) { + face checksh; + tspivot(*itet, checksh); + point p1 = sorg(checksh); + point p2 = sdest(checksh); + point p3 = sapex(checksh); + REAL ip[3], u = 0; + planelineint(p1, p2, p3, e1, e2, ip, &u); + if (etype == 1) { + printf("PLC Error: A segment and a facet intersect at point"); + printf(" (%g,%g,%g).\n", ip[0], ip[1], ip[2]); + printf(" Segment: [%d, %d] #%d (%d)\n", pointmark(forg), pointmark(fdest), geomtag, + facemark); + printf(" Facet: [%d, %d, %d] #%d.\n", pointmark(p1), pointmark(p2), + pointmark(p3), shellmark(checksh)); + sevent.e_type = 2; + sevent.f_marker1 = facemark; + sevent.s_marker1 = geomtag; + sevent.f_vertices1[0] = pointmark(forg); + sevent.f_vertices1[1] = pointmark(fdest); + sevent.f_vertices1[2] = 0; + sevent.f_marker2 = shellmark(checksh); + sevent.s_marker2 = 0; + sevent.f_vertices2[0] = pointmark(p1); + sevent.f_vertices2[1] = pointmark(p2); + sevent.f_vertices2[2] = pointmark(p3); + sevent.int_point[0] = ip[0]; + sevent.int_point[1] = ip[1]; + sevent.int_point[2] = ip[2]; + } else if (etype == 2) { + printf("PLC Error: Two facets intersect at point (%g,%g,%g).\n", ip[0], ip[1], + ip[2]); + printf(" Facet 1: [%d, %d, %d] #%d.\n", pointmark(forg), pointmark(fdest), + pointmark(fapex), geomtag); + printf(" Facet 2: [%d, %d, %d] #%d.\n", pointmark(p1), pointmark(p2), + pointmark(p3), shellmark(checksh)); + sevent.e_type = 3; + sevent.f_marker1 = geomtag; + sevent.s_marker1 = 0; + sevent.f_vertices1[0] = pointmark(forg); + sevent.f_vertices1[1] = pointmark(fdest); + sevent.f_vertices1[2] = pointmark(fapex); + sevent.f_marker2 = shellmark(checksh); + sevent.s_marker2 = 0; + sevent.f_vertices2[0] = pointmark(p1); + sevent.f_vertices2[1] = pointmark(p2); + sevent.f_vertices2[2] = pointmark(p3); + sevent.int_point[0] = ip[0]; + sevent.int_point[1] = ip[1]; + sevent.int_point[2] = ip[2]; + } + terminatetetgen(this, 3); + } } else { - terminatetetgen(this, 2); // Report a bug. + // An unknown 'dir'. + terminatetetgen(this, 2); } - terminatetetgen(this, 3); - } else if (dir == ACROSSEDGE) { - if (issubseg(*itet)) { - face checkseg; - tsspivot1(*itet, checkseg); - face parentsh; - spivot(checkseg, parentsh); - // Calulcate the intersecting point. - point p1 = sorg(checkseg); - point p2 = sdest(checkseg); - REAL P[3], Q[3], tp = 0, tq = 0; - linelineint(e1, e2, p1, p2, P, Q, &tp, &tq); - if (etype == 1) { - printf("PLC Error: Two segments intersect at point (%g,%g,%g).\n", - P[0], P[1], P[2]); - printf(" Segment 1: [%d, %d] #%d (%d)\n", pointmark(forg), - pointmark(fdest), geomtag, facemark); - printf(" Segment 2: [%d, %d] #%d (%d)\n", pointmark(p1), - pointmark(p2), shellmark(checkseg), - parentsh.sh ? shellmark(parentsh) : 0); - sevent.e_type = 1; - sevent.f_marker1 = facemark; - sevent.s_marker1 = geomtag; - sevent.f_vertices1[0] = pointmark(forg); - sevent.f_vertices1[1] = pointmark(fdest); - sevent.f_vertices1[2] = 0; - sevent.f_marker2 = (parentsh.sh ? shellmark(parentsh) : 0); - sevent.s_marker2 = shellmark(checkseg); - sevent.f_vertices2[0] = pointmark(p1); - sevent.f_vertices2[1] = pointmark(p2); - sevent.f_vertices2[2] = 0; - sevent.int_point[0] = P[0]; - sevent.int_point[1] = P[1]; - sevent.int_point[2] = P[2]; - } else if (etype == 2) { - printf("PLC Error: A segment and a facet intersect at point"); - printf(" (%g,%g,%g).\n", P[0], P[1], P[2]); - printf(" Segment: [%d, %d] #%d (%d)\n", pointmark(p1), - pointmark(p2), shellmark(checkseg), - parentsh.sh ? shellmark(parentsh) : 0); - printf(" Facet: [%d, %d, %d] #%d.\n", pointmark(forg), - pointmark(fdest), pointmark(fapex), geomtag); - sevent.e_type = 2; - sevent.f_marker1 = (parentsh.sh ? shellmark(parentsh) : 0); - sevent.s_marker1 = shellmark(checkseg); - sevent.f_vertices1[0] = pointmark(p1); - sevent.f_vertices1[1] = pointmark(p2); - sevent.f_vertices1[2] = 0; - sevent.f_marker2 = geomtag; - sevent.s_marker2 = 0; - sevent.f_vertices2[0] = pointmark(forg); - sevent.f_vertices2[1] = pointmark(fdest); - sevent.f_vertices2[2] = pointmark(fapex); - sevent.int_point[0] = P[0]; - sevent.int_point[1] = P[1]; - sevent.int_point[2] = P[2]; - } - terminatetetgen(this, 3); - } - } else if (dir == ACROSSFACE) { - if (issubface(*itet)) { - face checksh; - tspivot(*itet, checksh); - point p1 = sorg(checksh); - point p2 = sdest(checksh); - point p3 = sapex(checksh); - REAL ip[3], u = 0; - planelineint(p1, p2, p3, e1, e2, ip, &u); - if (etype == 1) { - printf("PLC Error: A segment and a facet intersect at point"); - printf(" (%g,%g,%g).\n", ip[0], ip[1], ip[2]); - printf(" Segment: [%d, %d] #%d (%d)\n", pointmark(forg), - pointmark(fdest), geomtag, facemark); - printf(" Facet: [%d, %d, %d] #%d.\n", pointmark(p1), - pointmark(p2), pointmark(p3), shellmark(checksh)); - sevent.e_type = 2; - sevent.f_marker1 = facemark; - sevent.s_marker1 = geomtag; - sevent.f_vertices1[0] = pointmark(forg); - sevent.f_vertices1[1] = pointmark(fdest); - sevent.f_vertices1[2] = 0; - sevent.f_marker2 = shellmark(checksh); - sevent.s_marker2 = 0; - sevent.f_vertices2[0] = pointmark(p1); - sevent.f_vertices2[1] = pointmark(p2); - sevent.f_vertices2[2] = pointmark(p3); - sevent.int_point[0] = ip[0]; - sevent.int_point[1] = ip[1]; - sevent.int_point[2] = ip[2]; - } else if (etype == 2) { - printf("PLC Error: Two facets intersect at point (%g,%g,%g).\n", - ip[0], ip[1], ip[2]); - printf(" Facet 1: [%d, %d, %d] #%d.\n", pointmark(forg), - pointmark(fdest), pointmark(fapex), geomtag); - printf(" Facet 2: [%d, %d, %d] #%d.\n", pointmark(p1), - pointmark(p2), pointmark(p3), shellmark(checksh)); - sevent.e_type = 3; - sevent.f_marker1 = geomtag; - sevent.s_marker1 = 0; - sevent.f_vertices1[0] = pointmark(forg); - sevent.f_vertices1[1] = pointmark(fdest); - sevent.f_vertices1[2] = pointmark(fapex); - sevent.f_marker2 = shellmark(checksh); - sevent.s_marker2 = 0; - sevent.f_vertices2[0] = pointmark(p1); - sevent.f_vertices2[1] = pointmark(p2); - sevent.f_vertices2[2] = pointmark(p3); - sevent.int_point[0] = ip[0]; - sevent.int_point[1] = ip[1]; - sevent.int_point[2] = ip[2]; - } - terminatetetgen(this, 3); - } - } else { - // An unknown 'dir'. - terminatetetgen(this, 2); - } - return 0; + return 0; } /////////////////////////////////////////////////////////////////////////////// @@ -7579,224 +7511,221 @@ int tetgenmesh::report_selfint_edge(point e1, point e2, face *iedge, // // /////////////////////////////////////////////////////////////////////////////// -int tetgenmesh::report_selfint_face(point p1, point p2, point p3, face* sface, - triface* iedge, int intflag, int* types, int* poss) -{ - face iface; - point e1 = NULL, e2 = NULL, e3 = NULL; - int etype = 0, geomtag = 0, facemark = 0; - - geomtag = shellmark(*sface); - - if (issubface(*iedge)) { - tspivot(*iedge, iface); - e1 = sorg(iface); - e2 = sdest(iface); - e3 = sapex(iface); - etype = 2; - facemark = geomtag; - } else if (issubseg(*iedge)) { - tsspivot1(*iedge, iface); - e1 = farsorg(iface); - e2 = farsdest(iface); - etype = 1; - face parentsh; - spivot(iface, parentsh); - facemark = shellmark(parentsh); - } else { - terminatetetgen(this, 2); - } - - if (intflag == 2) { - // The triangle and the edge intersect only at one point. - REAL ip[3], u = 0; - planelineint(p1, p2, p3, e1, e2, ip, &u); - if ((types[0] == (int) ACROSSFACE) || - (types[0] == (int) ACROSSEDGE)) { - // The triangle and the edge intersect in their interiors. - if (etype == 1) { - printf("PLC Error: A segment and a facet intersect at point"); - printf(" (%g,%g,%g).\n", ip[0], ip[1], ip[2]); - printf(" Segment: [%d,%d] #%d (%d)\n", pointmark(e1), pointmark(e2), - shellmark(iface), facemark); - printf(" Facet: [%d,%d,%d] #%d\n", pointmark(p1), - pointmark(p2), pointmark(p3), geomtag); - sevent.e_type = 2; - sevent.f_marker1 = facemark; - sevent.s_marker1 = shellmark(iface); - sevent.f_vertices1[0] = pointmark(e1); - sevent.f_vertices1[1] = pointmark(e2); - sevent.f_vertices1[2] = 0; - sevent.f_marker2 = geomtag; - sevent.s_marker2 = 0; - sevent.f_vertices2[0] = pointmark(p1); - sevent.f_vertices2[1] = pointmark(p2); - sevent.f_vertices2[2] = pointmark(p3); - sevent.int_point[0] = ip[0]; - sevent.int_point[1] = ip[1]; - sevent.int_point[2] = ip[2]; - } else { - printf("PLC Error: Two facets intersect at point"); - printf(" (%g,%g,%g).\n", ip[0], ip[1], ip[2]); - printf(" Facet 1: [%d,%d,%d] #%d\n", pointmark(e1), pointmark(e2), - pointmark(sorg(iface)), shellmark(iface)); - printf(" Facet 2: [%d,%d,%d] #%d\n", pointmark(p1), - pointmark(p2), pointmark(p3), geomtag); - sevent.e_type = 3; - sevent.f_marker1 = shellmark(iface); - sevent.s_marker1 = 0; - sevent.f_vertices1[0] = pointmark(e1); - sevent.f_vertices1[1] = pointmark(e2); - sevent.f_vertices1[2] = pointmark(sorg(iface)); - sevent.f_marker2 = geomtag; - sevent.s_marker2 = 0; - sevent.f_vertices2[0] = pointmark(p1); - sevent.f_vertices2[1] = pointmark(p2); - sevent.f_vertices2[2] = pointmark(p3); - sevent.int_point[0] = ip[0]; - sevent.int_point[1] = ip[1]; - sevent.int_point[2] = ip[2]; - } - } else if (types[0] == (int) ACROSSVERT) { - // A vertex of the triangle and the edge intersect. - point crosspt = NULL; - if (poss[0] == 0) { - crosspt = p1; - } else if (poss[0] == 1) { - crosspt = p2; - } else if (poss[0] == 2) { - crosspt = p3; - } else { - terminatetetgen(this, 2); - } - if (!issteinerpoint(crosspt)) { - if (etype == 1) { - printf("PLC Error: A vertex and a segment intersect at (%g,%g,%g)\n", - crosspt[0], crosspt[1], crosspt[2]); - printf(" Vertex: #%d\n", pointmark(crosspt)); - printf(" Segment: [%d,%d] #%d (%d)\n", pointmark(e1), pointmark(e2), - shellmark(iface), facemark); - sevent.e_type = 7; - sevent.f_marker1 = 0; - sevent.s_marker1 = 0; - sevent.f_vertices1[0] = pointmark(crosspt); - sevent.f_vertices1[1] = 0; - sevent.f_vertices1[2] = 0; - sevent.f_marker2 = facemark; - sevent.s_marker2 = shellmark(iface); - sevent.f_vertices2[0] = pointmark(e1); - sevent.f_vertices2[1] = pointmark(e2); - sevent.f_vertices2[2] = 0; - sevent.int_point[0] = crosspt[0]; - sevent.int_point[1] = crosspt[1]; - sevent.int_point[2] = crosspt[2]; - } else { - printf("PLC Error: A vertex and a facet intersect at (%g,%g,%g)\n", - crosspt[0], crosspt[1], crosspt[2]); - printf(" Vertex: #%d\n", pointmark(crosspt)); - printf(" Facet: [%d,%d,%d] #%d\n", pointmark(p1), - pointmark(p2), pointmark(p3), geomtag); - sevent.e_type = 8; - sevent.f_marker1 = 0; - sevent.s_marker1 = 0; - sevent.f_vertices1[0] = pointmark(crosspt); - sevent.f_vertices1[1] = 0; - sevent.f_vertices1[2] = 0; - sevent.f_marker2 = geomtag; - sevent.s_marker2 = 0; - sevent.f_vertices2[0] = pointmark(p1); - sevent.f_vertices2[1] = pointmark(p2); - sevent.f_vertices2[2] = pointmark(p3); - sevent.int_point[0] = crosspt[0]; - sevent.int_point[1] = crosspt[1]; - sevent.int_point[2] = crosspt[2]; - } - } else { - // It is a Steiner point. To be processed. - terminatetetgen(this, 2); - } - } else if ((types[0] == (int) TOUCHFACE) || - (types[0] == (int) TOUCHEDGE)) { - // The triangle and a vertex of the edge intersect. - point touchpt = NULL; - if (poss[1] == 0) { - touchpt = org(*iedge); - } else if (poss[1] == 1) { - touchpt = dest(*iedge); - } else { - terminatetetgen(this, 2); - } - if (!issteinerpoint(touchpt)) { - printf("PLC Error: A vertex and a facet intersect at (%g,%g,%g)\n", - touchpt[0], touchpt[1], touchpt[2]); - printf(" Vertex: #%d\n", pointmark(touchpt)); - printf(" Facet: [%d,%d,%d] #%d\n", pointmark(p1), - pointmark(p2), pointmark(p3), geomtag); - sevent.e_type = 8; - sevent.f_marker1 = 0; - sevent.s_marker1 = 0; - sevent.f_vertices1[0] = pointmark(touchpt); - sevent.f_vertices1[1] = 0; - sevent.f_vertices1[2] = 0; - sevent.f_marker2 = geomtag; - sevent.s_marker2 = 0; - sevent.f_vertices2[0] = pointmark(p1); - sevent.f_vertices2[1] = pointmark(p2); - sevent.f_vertices2[2] = pointmark(p3); - sevent.int_point[0] = touchpt[0]; - sevent.int_point[1] = touchpt[1]; - sevent.int_point[2] = touchpt[2]; - } else { - // It is a Steiner point. To be processed. - terminatetetgen(this, 2); - } - } else if (types[0] == (int) SHAREVERT) { - terminatetetgen(this, 2); - } else { - terminatetetgen(this, 2); - } - } else if (intflag == 4) { - if (types[0] == (int) SHAREFACE) { - printf("PLC Error: Two facets are overlapping.\n"); - printf(" Facet 1: [%d,%d,%d] #%d\n", pointmark(e1), - pointmark(e2), pointmark(e3), facemark); - printf(" Facet 2: [%d,%d,%d] #%d\n", pointmark(p1), - pointmark(p2), pointmark(p3), geomtag); - sevent.e_type = 6; - sevent.f_marker1 = facemark; - sevent.s_marker1 = 0; - sevent.f_vertices1[0] = pointmark(e1); - sevent.f_vertices1[1] = pointmark(e2); - sevent.f_vertices1[2] = pointmark(e3); - sevent.f_marker2 = geomtag; - sevent.s_marker2 = 0; - sevent.f_vertices2[0] = pointmark(p1); - sevent.f_vertices2[1] = pointmark(p2); - sevent.f_vertices2[2] = pointmark(p3); - } else { - terminatetetgen(this, 2); - } - } else { - terminatetetgen(this, 2); - } - - terminatetetgen(this, 3); - return 0; -} +int tetgenmesh::report_selfint_face(point p1, point p2, point p3, face* sface, triface* iedge, + int intflag, int* types, int* poss) { + face iface; + point e1 = NULL, e2 = NULL, e3 = NULL; + int etype = 0, geomtag = 0, facemark = 0; -//// //// -//// //// -//// geom_cxx ///////////////////////////////////////////////////////////////// + geomtag = shellmark(*sface); -//// flip_cxx ///////////////////////////////////////////////////////////////// -//// //// -//// //// + if (issubface(*iedge)) { + tspivot(*iedge, iface); + e1 = sorg(iface); + e2 = sdest(iface); + e3 = sapex(iface); + etype = 2; + facemark = geomtag; + } else if (issubseg(*iedge)) { + tsspivot1(*iedge, iface); + e1 = farsorg(iface); + e2 = farsdest(iface); + etype = 1; + face parentsh; + spivot(iface, parentsh); + facemark = shellmark(parentsh); + } else { + terminatetetgen(this, 2); + } -/////////////////////////////////////////////////////////////////////////////// -// // -// flip23() Perform a 2-to-3 flip (face-to-edge flip). // -// // -// 'fliptets' is an array of three tets (handles), where the [0] and [1] are // + if (intflag == 2) { + // The triangle and the edge intersect only at one point. + REAL ip[3], u = 0; + planelineint(p1, p2, p3, e1, e2, ip, &u); + if ((types[0] == (int)ACROSSFACE) || (types[0] == (int)ACROSSEDGE)) { + // The triangle and the edge intersect in their interiors. + if (etype == 1) { + printf("PLC Error: A segment and a facet intersect at point"); + printf(" (%g,%g,%g).\n", ip[0], ip[1], ip[2]); + printf(" Segment: [%d,%d] #%d (%d)\n", pointmark(e1), pointmark(e2), + shellmark(iface), facemark); + printf(" Facet: [%d,%d,%d] #%d\n", pointmark(p1), pointmark(p2), pointmark(p3), + geomtag); + sevent.e_type = 2; + sevent.f_marker1 = facemark; + sevent.s_marker1 = shellmark(iface); + sevent.f_vertices1[0] = pointmark(e1); + sevent.f_vertices1[1] = pointmark(e2); + sevent.f_vertices1[2] = 0; + sevent.f_marker2 = geomtag; + sevent.s_marker2 = 0; + sevent.f_vertices2[0] = pointmark(p1); + sevent.f_vertices2[1] = pointmark(p2); + sevent.f_vertices2[2] = pointmark(p3); + sevent.int_point[0] = ip[0]; + sevent.int_point[1] = ip[1]; + sevent.int_point[2] = ip[2]; + } else { + printf("PLC Error: Two facets intersect at point"); + printf(" (%g,%g,%g).\n", ip[0], ip[1], ip[2]); + printf(" Facet 1: [%d,%d,%d] #%d\n", pointmark(e1), pointmark(e2), + pointmark(sorg(iface)), shellmark(iface)); + printf(" Facet 2: [%d,%d,%d] #%d\n", pointmark(p1), pointmark(p2), pointmark(p3), + geomtag); + sevent.e_type = 3; + sevent.f_marker1 = shellmark(iface); + sevent.s_marker1 = 0; + sevent.f_vertices1[0] = pointmark(e1); + sevent.f_vertices1[1] = pointmark(e2); + sevent.f_vertices1[2] = pointmark(sorg(iface)); + sevent.f_marker2 = geomtag; + sevent.s_marker2 = 0; + sevent.f_vertices2[0] = pointmark(p1); + sevent.f_vertices2[1] = pointmark(p2); + sevent.f_vertices2[2] = pointmark(p3); + sevent.int_point[0] = ip[0]; + sevent.int_point[1] = ip[1]; + sevent.int_point[2] = ip[2]; + } + } else if (types[0] == (int)ACROSSVERT) { + // A vertex of the triangle and the edge intersect. + point crosspt = NULL; + if (poss[0] == 0) { + crosspt = p1; + } else if (poss[0] == 1) { + crosspt = p2; + } else if (poss[0] == 2) { + crosspt = p3; + } else { + terminatetetgen(this, 2); + } + if (!issteinerpoint(crosspt)) { + if (etype == 1) { + printf("PLC Error: A vertex and a segment intersect at (%g,%g,%g)\n", + crosspt[0], crosspt[1], crosspt[2]); + printf(" Vertex: #%d\n", pointmark(crosspt)); + printf(" Segment: [%d,%d] #%d (%d)\n", pointmark(e1), pointmark(e2), + shellmark(iface), facemark); + sevent.e_type = 7; + sevent.f_marker1 = 0; + sevent.s_marker1 = 0; + sevent.f_vertices1[0] = pointmark(crosspt); + sevent.f_vertices1[1] = 0; + sevent.f_vertices1[2] = 0; + sevent.f_marker2 = facemark; + sevent.s_marker2 = shellmark(iface); + sevent.f_vertices2[0] = pointmark(e1); + sevent.f_vertices2[1] = pointmark(e2); + sevent.f_vertices2[2] = 0; + sevent.int_point[0] = crosspt[0]; + sevent.int_point[1] = crosspt[1]; + sevent.int_point[2] = crosspt[2]; + } else { + printf("PLC Error: A vertex and a facet intersect at (%g,%g,%g)\n", crosspt[0], + crosspt[1], crosspt[2]); + printf(" Vertex: #%d\n", pointmark(crosspt)); + printf(" Facet: [%d,%d,%d] #%d\n", pointmark(p1), pointmark(p2), + pointmark(p3), geomtag); + sevent.e_type = 8; + sevent.f_marker1 = 0; + sevent.s_marker1 = 0; + sevent.f_vertices1[0] = pointmark(crosspt); + sevent.f_vertices1[1] = 0; + sevent.f_vertices1[2] = 0; + sevent.f_marker2 = geomtag; + sevent.s_marker2 = 0; + sevent.f_vertices2[0] = pointmark(p1); + sevent.f_vertices2[1] = pointmark(p2); + sevent.f_vertices2[2] = pointmark(p3); + sevent.int_point[0] = crosspt[0]; + sevent.int_point[1] = crosspt[1]; + sevent.int_point[2] = crosspt[2]; + } + } else { + // It is a Steiner point. To be processed. + terminatetetgen(this, 2); + } + } else if ((types[0] == (int)TOUCHFACE) || (types[0] == (int)TOUCHEDGE)) { + // The triangle and a vertex of the edge intersect. + point touchpt = NULL; + if (poss[1] == 0) { + touchpt = org(*iedge); + } else if (poss[1] == 1) { + touchpt = dest(*iedge); + } else { + terminatetetgen(this, 2); + } + if (!issteinerpoint(touchpt)) { + printf("PLC Error: A vertex and a facet intersect at (%g,%g,%g)\n", touchpt[0], + touchpt[1], touchpt[2]); + printf(" Vertex: #%d\n", pointmark(touchpt)); + printf(" Facet: [%d,%d,%d] #%d\n", pointmark(p1), pointmark(p2), pointmark(p3), + geomtag); + sevent.e_type = 8; + sevent.f_marker1 = 0; + sevent.s_marker1 = 0; + sevent.f_vertices1[0] = pointmark(touchpt); + sevent.f_vertices1[1] = 0; + sevent.f_vertices1[2] = 0; + sevent.f_marker2 = geomtag; + sevent.s_marker2 = 0; + sevent.f_vertices2[0] = pointmark(p1); + sevent.f_vertices2[1] = pointmark(p2); + sevent.f_vertices2[2] = pointmark(p3); + sevent.int_point[0] = touchpt[0]; + sevent.int_point[1] = touchpt[1]; + sevent.int_point[2] = touchpt[2]; + } else { + // It is a Steiner point. To be processed. + terminatetetgen(this, 2); + } + } else if (types[0] == (int)SHAREVERT) { + terminatetetgen(this, 2); + } else { + terminatetetgen(this, 2); + } + } else if (intflag == 4) { + if (types[0] == (int)SHAREFACE) { + printf("PLC Error: Two facets are overlapping.\n"); + printf(" Facet 1: [%d,%d,%d] #%d\n", pointmark(e1), pointmark(e2), pointmark(e3), + facemark); + printf(" Facet 2: [%d,%d,%d] #%d\n", pointmark(p1), pointmark(p2), pointmark(p3), + geomtag); + sevent.e_type = 6; + sevent.f_marker1 = facemark; + sevent.s_marker1 = 0; + sevent.f_vertices1[0] = pointmark(e1); + sevent.f_vertices1[1] = pointmark(e2); + sevent.f_vertices1[2] = pointmark(e3); + sevent.f_marker2 = geomtag; + sevent.s_marker2 = 0; + sevent.f_vertices2[0] = pointmark(p1); + sevent.f_vertices2[1] = pointmark(p2); + sevent.f_vertices2[2] = pointmark(p3); + } else { + terminatetetgen(this, 2); + } + } else { + terminatetetgen(this, 2); + } + + terminatetetgen(this, 3); + return 0; +} + +//// //// +//// //// +//// geom_cxx ///////////////////////////////////////////////////////////////// + +//// flip_cxx ///////////////////////////////////////////////////////////////// +//// //// +//// //// + +/////////////////////////////////////////////////////////////////////////////// +// // +// flip23() Perform a 2-to-3 flip (face-to-edge flip). // +// // +// 'fliptets' is an array of three tets (handles), where the [0] and [1] are // // [a,b,c,d] and [b,a,c,e]. The three new tets: [e,d,a,b], [e,d,b,c], and // // [e,d,c,a] are returned in [0], [1], and [2] of 'fliptets'. As a result, // // The face [a,b,c] is removed, and the edge [d,e] is created. // @@ -7817,311 +7746,310 @@ int tetgenmesh::report_selfint_face(point p1, point p2, point p3, face* sface, // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::flip23(triface* fliptets, int hullflag, flipconstraints *fc) -{ - 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 (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. +void tetgenmesh::flip23(triface* fliptets, int hullflag, flipconstraints* fc) { + 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 (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. + } + } + } + + pa = org(fliptets[0]); + pb = dest(fliptets[0]); + pc = apex(fliptets[0]); + pd = oppo(fliptets[0]); + pe = oppo(fliptets[1]); + + flip23count++; + + // Get the outer boundary faces. + for (i = 0; i < 3; i++) { + fnext(fliptets[0], topcastets[i]); enextself(fliptets[0]); + } + for (i = 0; i < 3; i++) { + fnext(fliptets[1], botcastets[i]); 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. - } - } - } - - pa = org(fliptets[0]); - pb = dest(fliptets[0]); - pc = apex(fliptets[0]); - pd = oppo(fliptets[0]); - pe = oppo(fliptets[1]); - - flip23count++; - - // Get the outer boundary faces. - for (i = 0; i < 3; i++) { - fnext(fliptets[0], topcastets[i]); - enextself(fliptets[0]); - } - for (i = 0; i < 3; i++) { - fnext(fliptets[1], botcastets[i]); - eprevself(fliptets[1]); - } - - // 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. + } + + // 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 { - // 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]); - } - - 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. + 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++) { - 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. + 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++) { - 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) - - 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); - } - } + 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++) { - if (issubface(botcastets[i])) { - tspivot(botcastets[i], checksh); - edestoppo(fliptets[i], newface); - sesymself(checksh); - tsbond(newface, checksh); - if (fc->chkencflag & 2) { - enqueuesubface(badsubfacs, &checksh); - } - } + edestoppo(fliptets[i], newface); // At edges [a,b], [b,c], [c,a]. + bond(newface, botcastets[i]); } - } // if (checksubfaceflag) - if (fc->chkencflag & 4) { - // Put three new tets into check list. - for (i = 0; i < 3; i++) { - enqueuetetrahedron(&(fliptets[i])); - } - } - - // Update the point-to-tet map. - setpoint2tet(pa, (tetrahedron) fliptets[0].tet); - setpoint2tet(pb, (tetrahedron) fliptets[0].tet); - setpoint2tet(pc, (tetrahedron) fliptets[1].tet); - setpoint2tet(pd, (tetrahedron) fliptets[0].tet); - setpoint2tet(pe, (tetrahedron) fliptets[0].tet); - - if (hullflag > 0) { - if (dummyflag != 0) { - // Restore the original position of the points (for flipnm()). - if (dummyflag == -1) { - // Reverse the edge. + 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++) { - 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; - } - } - } - } - - 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 (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) + + 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) + + if (fc->chkencflag & 4) { + // Put three new tets into check list. + for (i = 0; i < 3; i++) { + enqueuetetrahedron(&(fliptets[i])); + } } - if (fc->enqflag > 1) { - for (i = 0; i < 3; i++) { - enextesym(fliptets[i], newface); - flippush(flipstack, &newface); - } + + // 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; + } + } + } + } + + 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); + } + } } - } - recenttet = fliptets[0]; + recenttet = fliptets[0]; } /////////////////////////////////////////////////////////////////////////////// @@ -8156,384 +8084,383 @@ void tetgenmesh::flip23(triface* fliptets, int hullflag, flipconstraints *fc) // // /////////////////////////////////////////////////////////////////////////////// -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 { - // 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 { - dummyflag = 0; // either c or d may be dummypoint. - } - } - } - - 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]. +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 { + // 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 { + dummyflag = 0; // either c or d may be dummypoint. + } + } + } + + 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++) { - 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; - } - } - } - - // 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); + 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; + } + } + } + + // 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 (hullflag > 0) { + // Check if c is dummypointc. + if (pc != dummypoint) { + // Check if d is dummypoint. + if (pd != dummypoint) { + // No hull tet is involved. + } else { + // 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. } - if (b->varvolume) { - volume = volumebound(fliptets[spivot].tet); - setvolumebound(fliptets[j].tet, volume); - } - } - } - } - // Delete an old tet. - tetrahedrondealloc(fliptets[2].tet); - - 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 { - // 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) - - if (fc->chkencflag & 4) { - // Put two new tets into check list. - for (i = 0; i < 2; i++) { - enqueuetetrahedron(&(fliptets[i])); - } - } - - setpoint2tet(pa, (tetrahedron) fliptets[0].tet); - setpoint2tet(pb, (tetrahedron) fliptets[0].tet); - setpoint2tet(pc, (tetrahedron) fliptets[0].tet); - setpoint2tet(pd, (tetrahedron) fliptets[0].tet); - setpoint2tet(pe, (tetrahedron) fliptets[1].tet); - - 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); - } - } - - recenttet = fliptets[0]; + 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) + + if (fc->chkencflag & 4) { + // Put two new tets into check list. + for (i = 0; i < 2; i++) { + enqueuetetrahedron(&(fliptets[i])); + } + } + + setpoint2tet(pa, (tetrahedron)fliptets[0].tet); + setpoint2tet(pb, (tetrahedron)fliptets[0].tet); + setpoint2tet(pc, (tetrahedron)fliptets[0].tet); + setpoint2tet(pd, (tetrahedron)fliptets[0].tet); + setpoint2tet(pe, (tetrahedron)fliptets[1].tet); + + 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); + } + } + + recenttet = fliptets[0]; } /////////////////////////////////////////////////////////////////////////////// @@ -8558,294 +8485,292 @@ void tetgenmesh::flip32(triface* fliptets, int hullflag, flipconstraints *fc) // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::flip41(triface* fliptets, int hullflag, flipconstraints *fc) -{ - 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; - - 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,#] - } - 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 (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]}. - // 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]); - eprevself(neightet); - } - } else { - spivot = 3; // The new subface is [a,b,c]. - } - } - } // if (checksubfaceflag) - - - // Re-use fliptets[0] for [a,b,c,d]. - fliptets[0].ver = 11; - setelemmarker(fliptets[0].tet, 0); // Clean all flags. - // NOTE: the element attributes and volume constraint remain unchanged. - if (checksubsegflag) { - // Dealloc the space to subsegments. - if (fliptets[0].tet[8] != NULL) { - tet2segpool->dealloc((shellface *) fliptets[0].tet[8]); - fliptets[0].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; - } - } - // Delete the other three tets. - for (i = 1; i < 4; i++) { - tetrahedrondealloc(fliptets[i].tet); - } - - if (pp != dummypoint) { - // Mark the point pp as unused. - setpointtype(pp, UNUSEDVERTEX); - unuverts++; - } - - // 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; - } - } - 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. - } - } 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); - } - 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]); - } - 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); - } - } - enextself(fliptets[0]); - } - for (i = 0; i < 3; i++) { - if (issubseg(topcastets[i])) { - tsspivot1(topcastets[i], checkseg); // At edges [a,b],[b,c],[c,a]. - tssbond1(fliptets[0], checkseg); - sstbond1(checkseg, fliptets[0]); - if (fc->chkencflag & 1) { - enqueuesubface(badsubsegs, &checkseg); - } - } - enextself(fliptets[0]); - } - } +void tetgenmesh::flip41(triface* fliptets, int hullflag, flipconstraints* fc) { + 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; - if (checksubfaceflag) { - face checksh; - // Bond 4 subfaces (at faces of [a,b,c,d]) if there are. + 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++) { - 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); - } - } - - 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) - - if (fc->chkencflag & 4) { - enqueuetetrahedron(&(fliptets[0])); - } - - // Update the point-to-tet map. - setpoint2tet(pa, (tetrahedron) fliptets[0].tet); - setpoint2tet(pb, (tetrahedron) fliptets[0].tet); - setpoint2tet(pc, (tetrahedron) fliptets[0].tet); - setpoint2tet(pd, (tetrahedron) fliptets[0].tet); - - 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]); - } + 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,#] } - } + fsym(fliptets[3], botcastet); // [b,a,c,#] - recenttet = fliptets[0]; + 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 (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]}. + // 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]); + eprevself(neightet); + } + } else { + spivot = 3; // The new subface is [a,b,c]. + } + } + } // if (checksubfaceflag) + + // Re-use fliptets[0] for [a,b,c,d]. + fliptets[0].ver = 11; + setelemmarker(fliptets[0].tet, 0); // Clean all flags. + // NOTE: the element attributes and volume constraint remain unchanged. + if (checksubsegflag) { + // Dealloc the space to subsegments. + if (fliptets[0].tet[8] != NULL) { + tet2segpool->dealloc((shellface*)fliptets[0].tet[8]); + fliptets[0].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; + } + } + // Delete the other three tets. + for (i = 1; i < 4; i++) { + tetrahedrondealloc(fliptets[i].tet); + } + + if (pp != dummypoint) { + // Mark the point pp as unused. + setpointtype(pp, UNUSEDVERTEX); + unuverts++; + } + + // 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; + } + } + 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. + } + } 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); + } + 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]); + } + 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); + } + } + enextself(fliptets[0]); + } + for (i = 0; i < 3; i++) { + if (issubseg(topcastets[i])) { + tsspivot1(topcastets[i], checkseg); // At edges [a,b],[b,c],[c,a]. + tssbond1(fliptets[0], checkseg); + sstbond1(checkseg, fliptets[0]); + if (fc->chkencflag & 1) { + enqueuesubface(badsubsegs, &checkseg); + } + } + enextself(fliptets[0]); + } + } + + 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); + } + } + + 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) + + if (fc->chkencflag & 4) { + enqueuetetrahedron(&(fliptets[0])); + } + + // Update the point-to-tet map. + setpoint2tet(pa, (tetrahedron)fliptets[0].tet); + setpoint2tet(pb, (tetrahedron)fliptets[0].tet); + setpoint2tet(pc, (tetrahedron)fliptets[0].tet); + setpoint2tet(pd, (tetrahedron)fliptets[0].tet); + + 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]); + } + } + } + + recenttet = fliptets[0]; } /////////////////////////////////////////////////////////////////////////////// @@ -8877,648 +8802,642 @@ void tetgenmesh::flip41(triface* fliptets, int hullflag, flipconstraints *fc) // // /////////////////////////////////////////////////////////////////////////////// -int tetgenmesh::flipnm(triface* abtets, int n, int level, int abedgepivot, - flipconstraints* fc) -{ - 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. - } - } - // 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; - } - - 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. - } - - - // 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); - } - } - } - } - if (!reducflag) { - reflexlinkedgecount++; - } - } 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]); - 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. - } - } - } - } // 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; - } - } - } +int tetgenmesh::flipnm(triface* abtets, int n, int level, int abedgepivot, flipconstraints* fc) { + 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; - 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 information let us to either undo this flip or recover - // the original edge link (for collecting new created tets). - 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]. - } - } + pa = org(abtets[0]); + pb = dest(abtets[0]); - // 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] - // 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) - - 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 + if (n > 3) { + // Try to reduce the size of the Star(ab) by flipping a face in it. + reflexlinkedgecount = 0; - // 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. + // Let the face of 'abtets[i]' be [a,b,c]. + if (checksubfaceflag) { + if (issubface(abtets[i])) { + continue; // Skip a subface. + } + } + // 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; + } - // 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; + 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. } - } - if (!edgepivot) continue; + // Decide whether [a,b,c] is flippable or not. + reducflag = 0; - // 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; + 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); + } + } + } + } + if (!reducflag) { + reflexlinkedgecount++; + } + } 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]); + 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. + } + } + } + } // 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; + } } - } - 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; - } - if (n1 < 3) { - // This is only possible when the mesh contains inverted - // elements. Reprot a bug. - terminatetetgen(this, 2); - } - if (j > 2) { - // The Star(flipedge) overlaps other Stars. - continue; // Do not flip this edge. - } + 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 information let us to either undo this flip or recover + // the original edge link (for collecting new created tets). + 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]. + } + } - if ((b->flipstarsize > 0) && (n1 > b->flipstarsize)) { - // The star size exceeds the given limit. - continue; // Do not flip it. - } + // 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] + // 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) + + 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 + + // 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. + } - // 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; - } + edgepivot = 0; // No edge is selected yet. - // 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 - 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]). - // 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) + // 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; + } + } - // Recover the flipped edge ([c,b] or [a,c]). - flipnm_post(tmpabtets, n1, 2, edgepivot, fc); + 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; + } + } - // 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) { - // 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++) { - decreaseelemcounter(tmpabtets[j]); - } - // Release the allocated spaces. - delete [] tmpabtets; - } - } // i - } // if (level...) - } // if (reflexlinkedgecount > 0) - } else { - // Check if a 3-to-2 flip is possible. - // Let the three apexes be c, d,and e. Hull tets may be involved. If so, - // we rearrange them such that the vertex e is dummypoint. - hullflag = 0; - - 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 { - pc = apex(abtets[0]); - pd = apex(abtets[1]); - pe = apex(abtets[2]); - hullflag = (pe == dummypoint) ? 3 : 0; - } + // 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; + } + if (n1 < 3) { + // This is only possible when the mesh contains inverted + // elements. Reprot a bug. + terminatetetgen(this, 2); + } + if (j > 2) { + // The Star(flipedge) overlaps other Stars. + continue; // Do not flip this edge. + } - reducflag = 0; - rejflag = 0; + 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; + } - 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; - } - } + // 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 + 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]). + // 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) { + // 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++) { + decreaseelemcounter(tmpabtets[j]); + } + // Release the allocated spaces. + delete[] tmpabtets; + } + } // i + } // if (level...) + } // if (reflexlinkedgecount > 0) } else { - // [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); - if (ori > bigvol) { - bigvol = ori; - searchpt = chkpt; - } - } - } - 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. - } - } + // 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 { - // No valid searchpt is found. - reducflag = 0; // Do not flip it. + pc = apex(abtets[0]); + pd = apex(abtets[1]); + pe = apex(abtets[2]); + hullflag = (pe == dummypoint) ? 3 : 0; } - } // 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; - } - } - 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. + reducflag = 0; + rejflag = 0; + + 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 (nn == 3) { - // Report a bug. - terminatetetgen(this, 2); - } - } - 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 { - *parytet = abtets[0]; + } else { + // [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 (fc->collectnewtets) - return 2; - } - } // if (reducflag) - } // if (n == 3) - - // The current (reduced) Star size. - return n; + 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); + if (ori > bigvol) { + bigvol = ori; + searchpt = chkpt; + } + } + } + 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 { + // 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; + } + } + 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 if (nn == 3) { + // Report a bug. + terminatetetgen(this, 2); + } + } + 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 { + *parytet = abtets[0]; + } + } + } // if (fc->collectnewtets) + return 2; + } + } // if (reducflag) + } // if (n == 3) + + // The current (reduced) Star size. + return n; } /////////////////////////////////////////////////////////////////////////////// @@ -9551,1609 +9470,1594 @@ int tetgenmesh::flipnm(triface* abtets, int n, int level, int abedgepivot, // // /////////////////////////////////////////////////////////////////////////////// -int tetgenmesh::flipnm_post(triface* abtets, int n, int nn, int abedgepivot, - flipconstraints* fc) -{ - triface fliptets[3], flipface; - triface *tmpabtets; - int fliptype; - int edgepivot; - int t, n1; - int i, j; - - - 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++; - } - - // 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); - 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); - 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; - } - } // i - - return 1; -} +int tetgenmesh::flipnm_post(triface* abtets, int n, int nn, int abedgepivot, flipconstraints* fc) { + triface fliptets[3], flipface; + triface* tmpabtets; + int fliptype; + int edgepivot; + int t, n1; + int i, j; + + 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++; + } + + // 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); + 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); + 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); -/////////////////////////////////////////////////////////////////////////////// -// // -// insertpoint() Insert a point into current tetrahedralization. // -// // -// 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 // + // 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; + } + } // i + + return 1; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// insertpoint() Insert a point into current tetrahedralization. // +// // +// 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. // // // /////////////////////////////////////////////////////////////////////////////// -int tetgenmesh::insertpoint(point insertpt, triface *searchtet, face *splitsh, - face *splitseg, insertvertexflags *ivf) -{ - 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; - - if (b->verbose > 2) { - printf(" Insert point %d\n", pointmark(insertpt)); - } - - // Locate the point. - if (searchtet->tet != NULL) { - loc = (enum locateresult) ivf->iloc; - } - - if (loc == OUTSIDE) { - if (searchtet->tet == NULL) { - if (!b->weighted) { - randomsample(insertpt, searchtet); - } else { - // Weighted DT. There may exist dangling vertex. - *searchtet = recenttet; - } - } - // Locate the point. - loc = locate(insertpt, searchtet); - } - - ivf->iloc = (int) loc; // The return value. +int tetgenmesh::insertpoint(point insertpt, triface* searchtet, face* splitsh, face* splitseg, + insertvertexflags* ivf) { + 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; - if (b->weighted) { - if (loc != OUTSIDE) { - // Check if this vertex is regular. - pts = (point *) searchtet->tet; - 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 lies above the lower hull. Do not insert it. - ivf->iloc = (int) NONREGULAR; - return 0; - } + if (b->verbose > 2) { + printf(" Insert point %d\n", pointmark(insertpt)); } - } - - // 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; + // Locate the point. + if (searchtet->tet != NULL) { + loc = (enum locateresult)ivf->iloc; } - 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; + + 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); } - infect(*searchtet); - caveoldtetlist->newindex((void **) &parytet); - *parytet = *searchtet; - } else if (loc == ONFACE) { - flip26count++; - // Add six adjacent boundary tets into list. - j = (searchtet->ver & 3); // The current face number. - for (i = 1; i < 4; i++) { - decode(searchtet->tet[(j + i) % 4], neightet); - neightet.ver = epivot[neightet.ver]; - cavebdrylist->newindex((void **) &parytet); - *parytet = neightet; - } - decode(searchtet->tet[j], spintet); - j = (spintet.ver & 3); // The current face number. - for (i = 1; i < 4; i++) { - decode(spintet.tet[(j + i) % 4], neightet); - neightet.ver = epivot[neightet.ver]; - cavebdrylist->newindex((void **) &parytet); - *parytet = neightet; - } - infect(spintet); - caveoldtetlist->newindex((void **) &parytet); - *parytet = spintet; - infect(*searchtet); - caveoldtetlist->newindex((void **) &parytet); - *parytet = *searchtet; - if (ivf->splitbdflag) { - if ((splitsh != NULL) && (splitsh->sh != NULL)) { - // Create the initial sub-cavity sC(p). - smarktest(*splitsh); - caveshlist->newindex((void **) &parysh); - *parysh = *splitsh; - } - } // if (splitbdflag) - } else if (loc == ONEDGE) { - flipn2ncount++; - // Add all adjacent boundary tets into list. - spintet = *searchtet; - while (1) { - eorgoppo(spintet, neightet); - decode(neightet.tet[neightet.ver & 3], neightet); - neightet.ver = epivot[neightet.ver]; - cavebdrylist->newindex((void **) &parytet); - *parytet = neightet; - edestoppo(spintet, neightet); - decode(neightet.tet[neightet.ver & 3], neightet); - neightet.ver = epivot[neightet.ver]; - cavebdrylist->newindex((void **) &parytet); - *parytet = neightet; - infect(spintet); - caveoldtetlist->newindex((void **) &parytet); - *parytet = spintet; - fnextself(spintet); - if (spintet.tet == searchtet->tet) break; - } // while (1) + ivf->iloc = (int)loc; // The return value. - 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. - // 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; + if (b->weighted) { + if (loc != OUTSIDE) { + // Check if this vertex is regular. + pts = (point*)searchtet->tet; + 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 lies above the lower hull. Do not insert it. + ivf->iloc = (int)NONREGULAR; + return 0; + } } - } } - } else if (loc == ONVERTEX) { - // The point already exist. Do nothing and return. - return 0; - } + // 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 (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. - // Include it in the cavity. The convex hull will be enlarged. - enqflag = true; - } 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; - 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; - 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); + if (loc == OUTSIDE) { + flip14count++; + // The current hull will be enlarged. + // Add four adjacent boundary tets into list. + for (i = 0; i < 4; i++) { + decode(searchtet->tet[i], neightet); + neightet.ver = epivot[neightet.ver]; + cavebdrylist->newindex((void**)&parytet); *parytet = neightet; - } - infect(*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 + infect(*searchtet); + caveoldtetlist->newindex((void**)&parytet); + *parytet = *searchtet; + } else if (loc == INTETRAHEDRON) { + flip14count++; + // Add four adjacent boundary tets into list. + for (i = 0; i < 4; i++) { + decode(searchtet->tet[i], neightet); + neightet.ver = epivot[neightet.ver]; + cavebdrylist->newindex((void**)&parytet); + *parytet = neightet; + } + infect(*searchtet); + caveoldtetlist->newindex((void**)&parytet); + *parytet = *searchtet; + } else if (loc == ONFACE) { + flip26count++; + // Add six adjacent boundary tets into list. + j = (searchtet->ver & 3); // The current face number. + for (i = 1; i < 4; i++) { + decode(searchtet->tet[(j + i) % 4], neightet); + neightet.ver = epivot[neightet.ver]; + cavebdrylist->newindex((void**)&parytet); + *parytet = neightet; + } + decode(searchtet->tet[j], spintet); + j = (spintet.ver & 3); // The current face number. + for (i = 1; i < 4; i++) { + decode(spintet.tet[(j + i) % 4], neightet); + neightet.ver = epivot[neightet.ver]; + cavebdrylist->newindex((void**)&parytet); + *parytet = neightet; + } + infect(spintet); + caveoldtetlist->newindex((void**)&parytet); + *parytet = spintet; + infect(*searchtet); + caveoldtetlist->newindex((void**)&parytet); + *parytet = *searchtet; - cavetetlist->restart(); // Clear the working list. - } // if (ivf->bowywat) + if (ivf->splitbdflag) { + if ((splitsh != NULL) && (splitsh->sh != NULL)) { + // Create the initial sub-cavity sC(p). + smarktest(*splitsh); + caveshlist->newindex((void**)&parysh); + *parysh = *splitsh; + } + } // if (splitbdflag) + } else if (loc == ONEDGE) { + flipn2ncount++; + // Add all adjacent boundary tets into list. + spintet = *searchtet; + while (1) { + eorgoppo(spintet, neightet); + decode(neightet.tet[neightet.ver & 3], neightet); + neightet.ver = epivot[neightet.ver]; + cavebdrylist->newindex((void**)&parytet); + *parytet = neightet; + edestoppo(spintet, neightet); + decode(neightet.tet[neightet.ver & 3], neightet); + neightet.ver = epivot[neightet.ver]; + cavebdrylist->newindex((void**)&parytet); + *parytet = neightet; + infect(spintet); + caveoldtetlist->newindex((void**)&parytet); + *parytet = spintet; + fnextself(spintet); + if (spintet.tet == searchtet->tet) break; + } // while (1) - 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; + if (ivf->splitbdflag) { + // Create the initial sub-cavity sC(p). + if ((splitseg != NULL) && (splitseg->sh != NULL)) { + smarktest(*splitseg); + splitseg->shver = 0; + spivot(*splitseg, *splitsh); } - } - } // j - } - } // i - // Uninfect collected segments. - for (i = 0; i < cavetetseglist->objects; i++) { - paryseg = (face *) fastlookup(cavetetseglist, i); - suninfect(*paryseg); - } - - if (ivf->rejflag & 1) { - // Reject this point if it encroaches upon any segment. - face *paryseg1; - for (i = 0; i < cavetetseglist->objects; i++) { - paryseg1 = (face *) fastlookup(cavetetseglist, i); - if (checkseg4encroach((point) paryseg1->sh[3], (point) paryseg1->sh[4], - insertpt)) { - encseglist->newindex((void **) &paryseg); - *paryseg = *paryseg1; - } - } // i - if ((ivf->rejflag & 1) && (encseglist->objects > 0)) { - insertpoint_abort(splitseg, ivf); - ivf->iloc = (int) ENCSEGMENT; + 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. + // 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; + } + } + } + } else if (loc == ONVERTEX) { + // The point already exist. Do nothing and return. return 0; - } } - } // if (checksubsegflag) - if (checksubfaceflag) { - // Collect all subfaces of C(p). - shellface *sptr; - for (i = 0; i < caveoldtetlist->objects; i++) { - cavetet = (triface *) fastlookup(caveoldtetlist, i); - 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; + 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)); } - } - } // 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) + } 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. + // Include it in the cavity. The convex hull will be enlarged. + enqflag = true; + } 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; + 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; + 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 - 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; - } + cavetetlist->restart(); // Clear the working list. + } // if (ivf->bowywat) - 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); - checksh = *parysh; - for (j = 0; j < 3; j++) { - if (!isshsubseg(checksh)) { - spivot(checksh, neighsh); - 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; - } - } + 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 } - } - } - 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); - } - 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 { - // A hull tet. It needs to be cut. - ori = 1; - } - // 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); + } // 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; } - } // if (ori >= 0) + } // i + if ((ivf->rejflag & 1) && (encseglist->objects > 0)) { + insertpoint_abort(splitseg, ivf); + ivf->iloc = (int)ENCSEGMENT; + return 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; + } // if (checksubsegflag) + + if (checksubfaceflag) { + // Collect all subfaces of C(p). + shellface* sptr; + for (i = 0; i < caveoldtetlist->objects; i++) { + cavetet = (triface*)fastlookup(caveoldtetlist, i); + if ((sptr = (shellface*)cavetet->tet[9]) != NULL) { + for (j = 0; j < 4; j++) { + if (sptr[j]) { + sdecode(sptr[j], checksh); + if (!sinfected(checksh)) { + sinfect(checksh); + cavetetshlist->newindex((void**)&parysh); + *parysh = checksh; + } } - } - } - } - fnextself(spintet); - if (spintet.tet == neightet.tet) break; + } // j } - if (j == 0) { - // Not found such a face. - terminatetetgen(this, 2); + } // 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; + } } - 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); + if (encshlist->objects > 0) { + insertpoint_abort(splitseg, ivf); + ivf->iloc = (int)ENCSUBFACE; + return 0; } - } - } - } // i - } // if (ivf->respectbdflag) - - // Update the cavity by removing invisible faces until it is star-shaped. - for (i = 0; i < cavebdrylist->objects; i++) { - cavetet = (triface *) fastlookup(cavebdrylist, i); - // 'cavetet' is an exterior tet adjacent to the cavity. - // 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) { - // Check if this face is visible by the new point. - if (issubface(neightet)) { - // We should only create a new tet that has a reasonable volume. - // Re-use 'volume' and 'attrib'. - pa = org(*cavetet); - pb = dest(*cavetet); - pc = apex(*cavetet); - volume = orient3dfast(pa, pb, pc, insertpt); - attrib = distance(pa, pb) * distance(pb, pc) * distance(pc, pa); - if ((fabs(volume) / attrib) < b->epsilon) { - ori = 0.0; - } else { - ori = orient3d(pa, pb, pc, insertpt); - } - } else { - ori = orient3d(org(*cavetet), dest(*cavetet), apex(*cavetet), - insertpt); - } - enqflag = (ori > 0); - // Comment: if ori == 0 (coplanar case), we also cut the tet. - } else { - // 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 (checksubfaceflag) - 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) { + 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); - ivf->iloc = (int) BADELEMENT; return 0; - } + } - if (ivf->splitbdflag) { - int cutshcount = 0; - // Update the sub-cavity sC(p). + if (ivf->splitbdflag) { + // The new point locates in surface mesh. Update the sC(p). + // We have already 'smarktested' the subfaces which directly intersect + // with p in 'caveshlist'. From them, we 'smarktest' their neighboring + // subfaces which are included in C(p). Do not across a segment. for (i = 0; i < caveshlist->objects; i++) { - parysh = (face *) fastlookup(caveshlist, i); - if (smarktested(*parysh)) { - enqflag = false; - stpivot(*parysh, neightet); + parysh = (face*)fastlookup(caveshlist, i); + checksh = *parysh; + for (j = 0; j < 3; j++) { + if (!isshsubseg(checksh)) { + spivot(checksh, neighsh); + 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; + } + } + } + } + } + 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); + } + 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 { + // A hull tet. It needs to be cut. + ori = 1; + } + // 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); + } + } // 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; + } + if (j == 0) { + // Not found such a face. + terminatetetgen(this, 2); + } + 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) + + // 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)) { - 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 (apex(*cavetet) != dummypoint) { + // It is a cavity boundary face. Check its visibility. + if (oppo(neightet) != dummypoint) { + // Check if this face is visible by the new point. + if (issubface(neightet)) { + // We should only create a new tet that has a reasonable volume. + // Re-use 'volume' and 'attrib'. + pa = org(*cavetet); + pb = dest(*cavetet); + pc = apex(*cavetet); + volume = orient3dfast(pa, pb, pc, insertpt); + attrib = distance(pa, pb) * distance(pb, pc) * distance(pc, pa); + if ((fabs(volume) / attrib) < b->epsilon) { + ori = 0.0; + } else { + ori = orient3d(pa, pb, pc, insertpt); + } + } else { + ori = orient3d(org(*cavetet), dest(*cavetet), apex(*cavetet), insertpt); + } + enqflag = (ori > 0); + // Comment: if ori == 0 (coplanar case), we also cut the tet. + } else { + // 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 (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 (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); + } } - 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); + + // 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; } - // 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) } - } + // 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 (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) + } + } - if (i > 0) { - // The updated sC(p) is invalid. Do not insert this vertex. + 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) + + } // if (ivf->validflag) + + if (ivf->refineflag) { + // The new point is inserted by Delaunay refinement, i.e., it is the + // circumcenter of a tetrahedron, or a subface, or a segment. + // Do not insert this point if the tetrahedron, or subface, or segment + // is not inside the final cavity. + if (((ivf->refineflag == 1) && !infected(ivf->refinetet)) || + ((ivf->refineflag == 2) && !smarktested(ivf->refinesh))) { insertpoint_abort(splitseg, ivf); - ivf->iloc = (int) BADELEMENT; + ivf->iloc = (int)BADELEMENT; return 0; - } - } // if (cutshcount > 0) - } // if (ivf->splitbdflag) - } // if (cutcount > 0) - - } // if (ivf->validflag) - - if (ivf->refineflag) { - // The new point is inserted by Delaunay refinement, i.e., it is the - // circumcenter of a tetrahedron, or a subface, or a segment. - // Do not insert this point if the tetrahedron, or subface, or segment - // is not inside the final cavity. - if (((ivf->refineflag == 1) && !infected(ivf->refinetet)) || - ((ivf->refineflag == 2) && !smarktested(ivf->refinesh))) { - insertpoint_abort(splitseg, ivf); - ivf->iloc = (int) BADELEMENT; - return 0; - } - } // if (ivf->refineflag) - - if (b->plc && (loc != INSTAR)) { - // Reject the new point if it lies too close to an existing point (b->plc), - // or it lies inside a protecting ball of near vertex (ivf->rejflag & 4). - // Collect the list of vertices of the initial cavity. - if (loc == OUTSIDE) { - pts = (point *) &(searchtet->tet[4]); - for (i = 0; i < 3; i++) { - cavetetvertlist->newindex((void **) &parypt); - *parypt = pts[i]; - } - } else if (loc == INTETRAHEDRON) { - pts = (point *) &(searchtet->tet[4]); - for (i = 0; i < 4; i++) { - cavetetvertlist->newindex((void **) &parypt); - *parypt = pts[i]; - } - } else if (loc == ONFACE) { - pts = (point *) &(searchtet->tet[4]); - for (i = 0; i < 3; i++) { - cavetetvertlist->newindex((void **) &parypt); - *parypt = pts[i]; - } - 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; - } - } - - int rejptflag = (ivf->rejflag & 4); - REAL rd; - pts = NULL; - - for (i = 0; i < cavetetvertlist->objects; i++) { - parypt = (point *) fastlookup(cavetetvertlist, i); - rd = distance(*parypt, insertpt); - // Is the point very close to an existing point? - if (rd < 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; - } - } - } - cavetetvertlist->restart(); // Clear the work list. - - 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 (!issteinerpoint(insertpt) && b->nomergevertex) { // -M0/1 option. - // 'insertpt' is an input vertex. - // In this case, we still insert this vertex. Issue a warning. - 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, minedgelength); - printf(" You may try a smaller tolerance (-T) (current is %g)\n", - b->epsilon); - printf(" to avoid this warning.\n"); - } - } else { - point2tetorg(*pts, *searchtet); - insertpoint_abort(splitseg, ivf); - ivf->iloc = (int) loc; - return 0; - } - } else { // loc == ENCVERTEX - // The point lies inside the protection ball. - point2tetorg(*pts, *searchtet); - insertpoint_abort(splitseg, ivf); - ivf->iloc = (int) loc; - return 0; - } - } - } // if (b->plc && (loc != INSTAR)) + } // if (ivf->refineflag) - 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]; - } + 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; + } + } + + int rejptflag = (ivf->rejflag & 4); + REAL rd; + pts = NULL; + + for (i = 0; i < cavetetvertlist->objects; i++) { + parypt = (point*)fastlookup(cavetetvertlist, i); + rd = distance(*parypt, insertpt); + // Is the point very close to an existing point? + if (rd < 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; + } + } + } + cavetetvertlist->restart(); // Clear the work list. + + 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 (!issteinerpoint(insertpt) && b->nomergevertex) { // -M0/1 option. + // 'insertpt' is an input vertex. + // In this case, we still insert this vertex. Issue a warning. + 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, + minedgelength); + printf(" You may try a smaller tolerance (-T) (current is %g)\n", + b->epsilon); + printf(" to avoid this warning.\n"); + } + } else { + point2tetorg(*pts, *searchtet); + insertpoint_abort(splitseg, ivf); + ivf->iloc = (int)loc; + return 0; + } + } else { // loc == ENCVERTEX + // The point lies inside the protection ball. + point2tetorg(*pts, *searchtet); + insertpoint_abort(splitseg, ivf); + ivf->iloc = (int)loc; + return 0; + } + } + } // if (b->plc && (loc != INSTAR)) + + 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; + } + } } - } // 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; + + if (ivf->cdtflag) { + // Unmark tets. + for (i = 0; i < caveoldtetlist->objects; i++) { + cavetet = (triface*)fastlookup(caveoldtetlist, i); + unmarktest(*cavetet); + } + for (i = 0; i < cavebdrylist->objects; i++) { + cavetet = (triface*)fastlookup(cavebdrylist, i); + unmarktest(*cavetet); + } + // Clean up arrays which are not needed. + cavetetlist->restart(); + if (checksubsegflag) { + cavetetseglist->restart(); } - } + if (checksubfaceflag) { + cavetetshlist->restart(); + } + return 1; } - } + // Before re-mesh C(p). Process the segments and subfaces which are on the + // 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 (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(); - } + 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 { + 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. + terminatetetgen(this, 2); + } + } + } 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) { - cavetetshlist->restart(); - } - return 1; - } + 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. + terminatetetgen(this, 2); + } + } + } 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); + } + } // i + } // if (checksubfaceflag) - // 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. + // Create new tetrahedra to fill the cavity. - 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 { - 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. - terminatetetgen(this, 2); - } + 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); } - } 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 (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 - 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. - terminatetetgen(this, 2); - } + // Set a handle for speeding point location. + recenttet = newtet; + // setpoint2tet(insertpt, encode(newtet)); + setpoint2tet(insertpt, (tetrahedron)(newtet.tet)); + + // Re-use this list to save new interior cavity faces. + cavetetlist->restart(); + + // 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); + bond(neightet, newneitet); + if (ivf->lawson > 1) { + cavetetlist->newindex((void**)&parytet); + *parytet = neightet; + } + } + // 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); + } } - } 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); - } - } // 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); - } - 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 - - // Set a handle for speeding point location. - recenttet = newtet; - //setpoint2tet(insertpt, encode(newtet)); - setpoint2tet(insertpt, (tetrahedron) (newtet.tet)); - - // Re-use this list to save new interior cavity faces. - cavetetlist->restart(); - - // 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); - bond(neightet, newneitet); - if (ivf->lawson > 1) { - cavetetlist->newindex((void **) &parytet); - *parytet = neightet; - } - } - //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); - } } - } - 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 (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 (((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); - } + 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); + } - 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); - // Find the adjacent tet containing the edge [a,b] outside C(p). - spintet = neightet; - while (1) { - fnextself(spintet); - if (!infected(spintet)) break; - } - // The adjacent tet connects to a new tet in C(p). - fsym(spintet, neightet); - // Find the tet containing the face [a, b, p]. - spintet = neightet; - while (1) { - fnextself(spintet); - if (apex(spintet) == insertpt) break; - } - // Adjust the edge direction in spintet and checksh. - if (sorg(checksh) != org(spintet)) { - sesymself(checksh); - } - // Connect the subface to two adjacent tets. - tsbond(spintet, checksh); - fsymself(spintet); - sesymself(checksh); - tsbond(spintet, checksh); - } // if (checksh.sh[3] != NULL) - } - } 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); - // 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) - - 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)); - } - sstbond1(checkseg, neightet); - spintet = neightet; - while (1) { - tssbond1(spintet, checkseg); - fnextself(spintet); - if (spintet.tet == neightet.tet) break; - } + 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); + // Find the adjacent tet containing the edge [a,b] outside C(p). + spintet = neightet; + while (1) { + fnextself(spintet); + if (!infected(spintet)) break; + } + // The adjacent tet connects to a new tet in C(p). + fsym(spintet, neightet); + // Find the tet containing the face [a, b, p]. + spintet = neightet; + while (1) { + fnextself(spintet); + if (apex(spintet) == insertpt) break; + } + // Adjust the edge direction in spintet and checksh. + if (sorg(checksh) != org(spintet)) { + sesymself(checksh); + } + // Connect the subface to two adjacent tets. + tsbond(spintet, checksh); + fsymself(spintet); + sesymself(checksh); + tsbond(spintet, checksh); + } // if (checksh.sh[3] != NULL) + } + } 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); + // 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 (splitseg != NULL) - } 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); - 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); - 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++; + } // if (checksubfaceflag) + + 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)); + } + sstbond1(checkseg, neightet); + spintet = neightet; + while (1) { + tssbond1(spintet, checkseg); + fnextself(spintet); + if (spintet.tet == neightet.tet) break; + } + } + } // if (splitseg != NULL) + } 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); + 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 (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) - - 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) - - 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); + 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); + 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++; + } + } + } } - } - // C(p) is re-meshed successfully. + 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) - // 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 (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) - 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). - tsdissolve(neightet); - fsymself(neightet); - tsdissolve(neightet); - } + 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); } - } - shellfacedealloc(subfaces, parysh->sh); } - if ((splitseg != NULL) && (splitseg->sh != NULL)) { - // Delete the old segment in sC(p). - shellfacedealloc(subsegs, splitseg->sh); + + // 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 (ivf->lawson) { - for (i = 0; i < cavebdrylist->objects; i++) { - searchtet = (triface *) fastlookup(cavebdrylist, i); - flippush(flipstack, searchtet); + 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). + tsdissolve(neightet); + fsymself(neightet); + tsdissolve(neightet); + } + } + } + shellfacedealloc(subfaces, parysh->sh); + } + if ((splitseg != NULL) && (splitseg->sh != NULL)) { + // Delete the old segment in sC(p). + shellfacedealloc(subsegs, splitseg->sh); + } } - if (ivf->lawson > 1) { - for (i = 0; i < cavetetlist->objects; i++) { - searchtet = (triface *) fastlookup(cavetetlist, i); - flippush(flipstack, searchtet); - } + + 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); + } + } } - } + // Clean the working lists. - // Clean the working lists. + caveoldtetlist->restart(); + cavebdrylist->restart(); + cavetetlist->restart(); - caveoldtetlist->restart(); - cavebdrylist->restart(); - cavetetlist->restart(); + if (checksubsegflag) { + cavetetseglist->restart(); + caveencseglist->restart(); + } - if (checksubsegflag) { - cavetetseglist->restart(); - caveencseglist->restart(); - } + if (checksubfaceflag) { + cavetetshlist->restart(); + caveencshlist->restart(); + } - if (checksubfaceflag) { - cavetetshlist->restart(); - caveencshlist->restart(); - } - - if (b->weighted || ivf->smlenflag - ) { - cavetetvertlist->restart(); - } - - if (((splitsh != NULL) && (splitsh->sh != NULL)) || - ((splitseg != NULL) && (splitseg->sh != NULL))) { - caveshlist->restart(); - caveshbdlist->restart(); - cavesegshlist->restart(); - } + if (b->weighted || ivf->smlenflag) { + cavetetvertlist->restart(); + } - return 1; // Point is inserted. + if (((splitsh != NULL) && (splitsh->sh != NULL)) || + ((splitseg != NULL) && (splitseg->sh != NULL))) { + caveshlist->restart(); + caveshbdlist->restart(); + cavesegshlist->restart(); + } + + return 1; // Point is inserted. } /////////////////////////////////////////////////////////////////////////////// @@ -11164,37 +11068,36 @@ int tetgenmesh::insertpoint(point insertpt, triface *searchtet, face *splitsh, // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::insertpoint_abort(face *splitseg, insertvertexflags *ivf) -{ - triface *cavetet; - face *parysh; - int i; - - for (i = 0; i < caveoldtetlist->objects; i++) { - cavetet = (triface *) fastlookup(caveoldtetlist, i); - uninfect(*cavetet); - unmarktest(*cavetet); - } - for (i = 0; i < cavebdrylist->objects; i++) { - cavetet = (triface *) fastlookup(cavebdrylist, i); - unmarktest(*cavetet); - } - cavetetlist->restart(); - cavebdrylist->restart(); - caveoldtetlist->restart(); - cavetetseglist->restart(); - cavetetshlist->restart(); - if (ivf->splitbdflag) { - if ((splitseg != NULL) && (splitseg->sh != NULL)) { - sunmarktest(*splitseg); +void tetgenmesh::insertpoint_abort(face* splitseg, insertvertexflags* ivf) { + triface* cavetet; + face* parysh; + int i; + + for (i = 0; i < caveoldtetlist->objects; i++) { + cavetet = (triface*)fastlookup(caveoldtetlist, i); + uninfect(*cavetet); + unmarktest(*cavetet); } - for (i = 0; i < caveshlist->objects; i++) { - parysh = (face *) fastlookup(caveshlist, i); - sunmarktest(*parysh); + 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); + sunmarktest(*parysh); + } + caveshlist->restart(); + cavesegshlist->restart(); } - caveshlist->restart(); - cavesegshlist->restart(); - } } //// //// @@ -11216,92 +11119,91 @@ void tetgenmesh::insertpoint_abort(face *splitseg, insertvertexflags *ivf) // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::transfernodes() -{ - point pointloop; - REAL x, y, z, w; - int coordindex; - int attribindex; - int mtrindex; - int i, j; - - // Read the points. - coordindex = 0; - attribindex = 0; - mtrindex = 0; - for (i = 0; i < in->numberofpoints; i++) { - makepoint(&pointloop, 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); - } - } - } - - // '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, 10); - } - // Two identical points are distinguished by 'minedgelength'. - minedgelength = longest * b->epsilon; +void tetgenmesh::transfernodes() { + point pointloop; + REAL x, y, z, w; + int coordindex; + int attribindex; + int mtrindex; + int i, j; + + // Read the points. + coordindex = 0; + attribindex = 0; + mtrindex = 0; + for (i = 0; i < in->numberofpoints; i++) { + makepoint(&pointloop, 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); + } + } + } + + // '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, 10); + } + // Two identical points are distinguished by 'minedgelength'. + minedgelength = longest * b->epsilon; } /////////////////////////////////////////////////////////////////////////////// @@ -11320,47 +11222,46 @@ void tetgenmesh::transfernodes() // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::hilbert_init(int n) -{ - int gc[8], N, mask, travel_bit; - int e, d, f, k, g; - int v, c; - int i; - - N = (n == 2) ? 4 : 8; - mask = (n == 2) ? 3 : 7; - - // Generate the Gray code sequence. - for (i = 0; i < N; i++) { - gc[i] = i ^ (i >> 1); - } - - for (e = 0; e < N; e++) { - for (d = 0; d < n; d++) { - // Calculate the end point (f). - f = e ^ (1 << d); // Toggle the d-th bit of 'e'. - // travel_bit = 2**p, the bit we want to travel. - travel_bit = e ^ f; - for (i = 0; i < N; i++) { - // // Rotate gc[i] left by (p + 1) % n bits. - k = gc[i] * (travel_bit * 2); - g = ((k | (k / N)) & mask); - // Calculate the permuted Gray code by xor with the start point (e). - transgc[e][d][i] = (g ^ e); - } - } // 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; - } +void tetgenmesh::hilbert_init(int n) { + int gc[8], N, mask, travel_bit; + int e, d, f, k, g; + int v, c; + int i; + + N = (n == 2) ? 4 : 8; + mask = (n == 2) ? 3 : 7; + + // Generate the Gray code sequence. + for (i = 0; i < N; i++) { + gc[i] = i ^ (i >> 1); + } + + for (e = 0; e < N; e++) { + for (d = 0; d < n; d++) { + // Calculate the end point (f). + f = e ^ (1 << d); // Toggle the d-th bit of 'e'. + // travel_bit = 2**p, the bit we want to travel. + travel_bit = e ^ f; + for (i = 0; i < N; i++) { + // // Rotate gc[i] left by (p + 1) % n bits. + k = gc[i] * (travel_bit * 2); + g = ((k | (k / N)) & mask); + // Calculate the permuted Gray code by xor with the start point (e). + transgc[e][d][i] = (g ^ e); + } + } // 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; + } } /////////////////////////////////////////////////////////////////////////////// @@ -11369,169 +11270,164 @@ void tetgenmesh::hilbert_init(int n) // // /////////////////////////////////////////////////////////////////////////////// -int tetgenmesh::hilbert_split(point* vertexarray,int arraysize,int gc0,int gc1, - REAL bxmin, REAL bxmax, REAL bymin, REAL bymax, - REAL bzmin, REAL bzmax) -{ - point swapvert; - int axis, d; - REAL split; - int i, j; - +int tetgenmesh::hilbert_split(point* vertexarray, int arraysize, int gc0, int gc1, REAL bxmin, + REAL bxmax, REAL bymin, REAL bymax, REAL bzmin, REAL bzmax) { + point swapvert; + int axis, d; + REAL split; + int i, j; - // 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; + // Find the current splitting axis. 'axis' is a value 0, or 1, or 2, which + // correspoding to x-, or y- or z-axis. + axis = (gc0 ^ gc1) >> 1; - // Calulate the split position along the axis. - if (axis == 0) { - split = 0.5 * (bxmin + bxmax); - } else if (axis == 1) { - split = 0.5 * (bymin + bymax); - } else { // == 2 - split = 0.5 * (bzmin + bzmax); - } + // 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); + } - // 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< 0) { + do { + for (; i < arraysize; i++) { + if (vertexarray[i][axis] >= split) break; + } + for (; j >= 0; j--) { + if (vertexarray[j][axis] < split) break; + } + // Is the partition finished? + if (i == (j + 1)) break; + // Swap i-th and j-th vertices. + swapvert = vertexarray[i]; + vertexarray[i] = vertexarray[j]; + vertexarray[j] = swapvert; + // Continue patitioning the array; + } while (true); + } else { + do { + for (; i < arraysize; i++) { + if (vertexarray[i][axis] <= split) break; + } + 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); + } - // 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 { - 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); - } - - return i; + 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; - } - } - - // 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]; - } - di = (d + d_w + 1) % n; - // Calculate the bounding box of the sub-box. - if (transgc[e][d][w] & 1) { // x-axis - x1 = 0.5 * (bxmin + bxmax); - x2 = bxmax; - } else { - x1 = bxmin; - x2 = 0.5 * (bxmin + bxmax); - } - if (transgc[e][d][w] & 2) { // y-axis - y1 = 0.5 * (bymin + bymax); - y2 = bymax; - } else { - y1 = bymin; - y2 = 0.5 * (bymin + bymax); - } - 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 +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; + } + } + + // 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]; + } + di = (d + d_w + 1) % n; + // Calculate the bounding box of the sub-box. + if (transgc[e][d][w] & 1) { // x-axis + x1 = 0.5 * (bxmin + bxmax); + x2 = bxmax; + } else { + x1 = bxmin; + x2 = 0.5 * (bxmin + bxmax); + } + if (transgc[e][d][w] & 2) { // y-axis + y1 = 0.5 * (bymin + bymax); + y2 = bymax; + } else { + y1 = bymin; + y2 = 0.5 * (bymin + bymax); + } + 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 } /////////////////////////////////////////////////////////////////////////////// @@ -11540,20 +11436,19 @@ void tetgenmesh::hilbert_sort3(point* vertexarray, int arraysize, int e, int d, // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::brio_multiscale_sort(point* vertexarray, int arraysize, - int threshold, REAL ratio, int *depth) -{ - int middle; - - middle = 0; - if (arraysize >= threshold) { - (*depth)++; - middle = arraysize * ratio; - brio_multiscale_sort(vertexarray, middle, threshold, ratio, depth); - } - // Sort the right-array (rnd-th round) using the Hilbert curve. - hilbert_sort3(&(vertexarray[middle]), arraysize - middle, 0, 0, // e, d - xmin, xmax, ymin, ymax, zmin, zmax, 0); // depth. +void tetgenmesh::brio_multiscale_sort(point* vertexarray, int arraysize, int threshold, REAL ratio, + int* depth) { + int middle; + + middle = 0; + if (arraysize >= threshold) { + (*depth)++; + middle = arraysize * ratio; + brio_multiscale_sort(vertexarray, middle, threshold, ratio, depth); + } + // Sort the right-array (rnd-th round) using the Hilbert curve. + hilbert_sort3(&(vertexarray[middle]), arraysize - middle, 0, 0, // e, d + xmin, xmax, ymin, ymax, zmin, zmax, 0); // depth. } /////////////////////////////////////////////////////////////////////////////// @@ -11562,23 +11457,22 @@ void tetgenmesh::brio_multiscale_sort(point* vertexarray, int arraysize, // // /////////////////////////////////////////////////////////////////////////////// -unsigned long tetgenmesh::randomnation(unsigned int choices) -{ - unsigned long newrandom; - - if (choices >= 714025l) { - newrandom = (randomseed * 1366l + 150889l) % 714025l; - randomseed = (newrandom * 1366l + 150889l) % 714025l; - newrandom = newrandom * (choices / 714025l) + randomseed; - if (newrandom >= choices) { - return newrandom - choices; +unsigned long tetgenmesh::randomnation(unsigned int choices) { + unsigned long newrandom; + + if (choices >= 714025l) { + newrandom = (randomseed * 1366l + 150889l) % 714025l; + randomseed = (newrandom * 1366l + 150889l) % 714025l; + newrandom = newrandom * (choices / 714025l) + randomseed; + if (newrandom >= choices) { + return newrandom - choices; + } else { + return newrandom; + } } else { - return newrandom; + randomseed = (randomseed * 1366l + 150889l) % 714025l; + return randomseed % choices; } - } else { - randomseed = (randomseed * 1366l + 150889l) % 714025l; - return randomseed % choices; - } } /////////////////////////////////////////////////////////////////////////////// @@ -11592,99 +11486,94 @@ unsigned long tetgenmesh::randomnation(unsigned int choices) // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::randomsample(point searchpt,triface *searchtet) -{ - tetrahedron *firsttet, *tetptr; - point torg; - void **sampleblock; - uintptr_t alignptr; - long sampleblocks, samplesperblock, samplenum; - long tetblocks, i, j; - REAL searchdist, dist; - - if (b->verbose > 2) { - printf(" Random sampling tetrahedra for searching point %d.\n", - pointmark(searchpt)); - } - - if (!nonconvex) { - if (searchtet->tet == NULL) { - // A null tet. Choose the recenttet as the starting tet. - *searchtet = recenttet; - } - - // '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; - } - } - } else { - // The mesh is non-convex. Do not use 'recenttet'. - searchdist = longest; - } - - // 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++; - } - // 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; - } +void tetgenmesh::randomsample(point searchpt, triface* searchtet) { + tetrahedron *firsttet, *tetptr; + point torg; + void** sampleblock; + uintptr_t alignptr; + long sampleblocks, samplesperblock, samplenum; + long tetblocks, i, j; + REAL searchdist, dist; + + if (b->verbose > 2) { + printf(" Random sampling tetrahedra for searching point %d.\n", pointmark(searchpt)); + } + + if (!nonconvex) { + if (searchtet->tet == NULL) { + // A null tet. Choose the recenttet as the starting tet. + *searchtet = recenttet; + } + + // '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; + } + } + } else { + // The mesh is non-convex. Do not use 'recenttet'. + searchdist = longest; + } + + // 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++; + } + // 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; + } } /////////////////////////////////////////////////////////////////////////////// @@ -11709,193 +11598,192 @@ void tetgenmesh::randomsample(point searchpt,triface *searchtet) // // /////////////////////////////////////////////////////////////////////////////// -enum tetgenmesh::locateresult - tetgenmesh::locate(point searchpt, triface* searchtet, int chkencflag) -{ - point torg, tdest, tapex, toppo; - enum {ORGMOVE, DESTMOVE, APEXMOVE} nextmove; - REAL ori, oriorg, oridest, oriapex; - enum locateresult loc = OUTSIDE; - int t1ver; - int s; - - torg = tdest = tapex = toppo = NULL; - - if (searchtet->tet == NULL) { - // A null tet. Choose the recenttet as the starting tet. - searchtet->tet = recenttet.tet; - } - - // 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); - } - - // 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; - } - if (searchtet->ver == 4) { - terminatetetgen(this, 2); - } - - // Walk through tetrahedra to locate the point. - while (true) { - - 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; - } +enum tetgenmesh::locateresult tetgenmesh::locate(point searchpt, triface* searchtet, + int chkencflag) { + point torg, tdest, tapex, toppo; + enum { ORGMOVE, DESTMOVE, APEXMOVE } nextmove; + REAL ori, oriorg, oridest, oriapex; + enum locateresult loc = OUTSIDE; + int t1ver; + int s; + + torg = tdest = tapex = toppo = NULL; + + if (searchtet->tet == NULL) { + // A null tet. Choose the recenttet as the starting tet. + searchtet->tet = recenttet.tet; + } + + // 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); + } + + // 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; + } + if (searchtet->ver == 4) { + terminatetetgen(this, 2); + } + + // Walk through tetrahedra to locate the point. + while (true) { + + 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; } - } 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; - } + + // 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 { - // Only the face opposite to origin is viable. - nextmove = ORGMOVE; + 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 { + nextmove = APEXMOVE; + } + } else { + // Only the face opposite to destination is viable. + nextmove = DESTMOVE; + } + } else { + if (oriapex < 0) { + // Only the face opposite to apex is viable. + nextmove = APEXMOVE; + } else { + // The point we seek must be on the boundary of or inside this + // tetrahedron. Check for boundary cases. + if (oriorg == 0) { + // Go to the face opposite to origin. + 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; + } + } } - } - } 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 { - nextmove = APEXMOVE; - } + + // Move to the selected face. + if (nextmove == ORGMOVE) { + enextesymself(*searchtet); + } else if (nextmove == DESTMOVE) { + eprevesymself(*searchtet); } else { - // Only the face opposite to destination is viable. - nextmove = DESTMOVE; + esymself(*searchtet); } - } 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. - enextesymself(*searchtet); - if (oridest == 0) { - eprevself(*searchtet); // edge oppo->apex - if (oriapex == 0) { - // oppo is duplicated with p. - loc = ONVERTEX; // return ONVERTEX; + if (chkencflag) { + // Check if we are walking across a subface. + if (issubface(*searchtet)) { + loc = ENCSUBFACE; 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; + } + // Move to the adjacent tetrahedron (maybe a hull tetrahedron). + fsymself(*searchtet); + if (oppo(*searchtet) == dummypoint) { + loc = OUTSIDE; // return OUTSIDE; break; - } - loc = INTETRAHEDRON; // return INTETRAHEDRON; - break; } - } - } - - // Move to the selected face. - if (nextmove == ORGMOVE) { - enextesymself(*searchtet); - } else if (nextmove == DESTMOVE) { - eprevesymself(*searchtet); - } else { - esymself(*searchtet); - } - if (chkencflag) { - // Check if we are walking across a subface. - if (issubface(*searchtet)) { - loc = ENCSUBFACE; - break; - } - } - // Move to the adjacent tetrahedron (maybe a hull tetrahedron). - fsymself(*searchtet); - if (oppo(*searchtet) == dummypoint) { - loc = OUTSIDE; // return OUTSIDE; - break; - } - // Retreat the three vertices of the base face. - torg = org(*searchtet); - tdest = dest(*searchtet); - tapex = apex(*searchtet); + // Retreat the three vertices of the base face. + torg = org(*searchtet); + tdest = dest(*searchtet); + tapex = apex(*searchtet); - } // while (true) + } // while (true) - return loc; + return loc; } /////////////////////////////////////////////////////////////////////////////// @@ -11907,16 +11795,15 @@ enum tetgenmesh::locateresult // // /////////////////////////////////////////////////////////////////////////////// -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; - } +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; + } } /////////////////////////////////////////////////////////////////////////////// @@ -11927,7 +11814,7 @@ void tetgenmesh::flippush(badface*& fstack, triface* flipface) // Return the total number of performed flips. // // // // Comment: This routine should be only used in the incremental Delaunay // -// construction. In other cases, lawsonflip3d() should be used. // +// construction. In other cases, lawsonflip3d() should be used. // // // // 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 // @@ -11938,233 +11825,229 @@ void tetgenmesh::flippush(badface*& fstack, triface* flipface) // // /////////////////////////////////////////////////////////////////////////////// -int tetgenmesh::incrementalflip(point newpt, int hullflag, flipconstraints *fc) -{ - badface *popface; - triface fliptets[5], *parytet; - point *pts, *parypt, pe; - REAL sign, ori; - int flipcount = 0; - int t1ver; - int i; - - 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 { - 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); +int tetgenmesh::incrementalflip(point newpt, int hullflag, flipconstraints* fc) { + badface* popface; + triface fliptets[5], *parytet; + point *pts, *parypt, pe; + REAL sign, ori; + int flipcount = 0; + int t1ver; + int i; + + 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; } - 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; } - // 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); - } + // 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); - 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]); + // 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 { + 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; } - if (fliptets[3].tet == fliptets[0].tet) { - // A 3-to-2 flip is found. (No hull tet.) - flip32(fliptets, 0, fc); - flipcount++; + + // 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 { - // 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++; - } - } + sign = insphere_s(pts[4], pts[5], pts[6], pts[7], newpt); } - } // 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) + 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]); + } + 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]; + } - // Unmark saved tetrahedra. - for (i = 0; i < cavebdrylist->objects; i++) { - parytet = (triface *) fastlookup(cavebdrylist, i); - unmarktest(*parytet); - } - cavebdrylist->restart(); + } // while (flipstack) - if (hullflag) { - // Unmark infected vertices. - for (i = 0; i < cavetetvertlist->objects; i++) { - parypt = (point *) fastlookup(cavetetvertlist, i); - puninfect(*parypt); + // Unmark saved tetrahedra. + for (i = 0; i < cavebdrylist->objects; i++) { + parytet = (triface*)fastlookup(cavebdrylist, i); + unmarktest(*parytet); } - cavetetvertlist->restart(); - } + cavebdrylist->restart(); + if (hullflag) { + // Unmark infected vertices. + for (i = 0; i < cavetetvertlist->objects; i++) { + parypt = (point*)fastlookup(cavetetvertlist, i); + puninfect(*parypt); + } + cavetetvertlist->restart(); + } - return flipcount; + return flipcount; } /////////////////////////////////////////////////////////////////////////////// @@ -12176,80 +12059,79 @@ int tetgenmesh::incrementalflip(point newpt, int hullflag, flipconstraints *fc) // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::initialdelaunay(point pa, point pb, point pc, point pd) -{ - triface firsttet, tetopa, tetopb, tetopc, tetopd; - triface worktet, worktet1; - - if (b->verbose > 2) { - printf(" Create init tet (%d, %d, %d, %d)\n", pointmark(pa), - pointmark(pb), pointmark(pc), pointmark(pd)); - } - - // 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; - - // 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 - - // 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; +void tetgenmesh::initialdelaunay(point pa, point pb, point pc, point pd) { + triface firsttet, tetopa, tetopb, tetopc, tetopd; + triface worktet, worktet1; + + if (b->verbose > 2) { + printf(" Create init tet (%d, %d, %d, %d)\n", pointmark(pa), pointmark(pb), + pointmark(pc), pointmark(pd)); + } + + // 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; + + // 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 + + // 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; } /////////////////////////////////////////////////////////////////////////////// @@ -12259,217 +12141,204 @@ void tetgenmesh::initialdelaunay(point pa, point pb, point pc, point pd) // // /////////////////////////////////////////////////////////////////////////////// +void tetgenmesh::incrementaldelaunay(clock_t& tv) { + 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; -void tetgenmesh::incrementaldelaunay(clock_t& tv) -{ - 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; - - 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); - } - } - - tv = clock(); // Remember the time for sorting points. - - // Calculate the diagonal size of its bounding box. - bboxsize = sqrt(norm2(xmax - xmin, ymax - ymin, zmax - zmin)); - bboxsize2 = bboxsize * bboxsize; - bboxsize3 = bboxsize2 * bboxsize; - - // Make sure the second vertex is not identical with the first one. - i = 1; - while ((distance(permutarray[0],permutarray[i])/bboxsize)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; - } - - // 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) { - i++; - if (i == in->numberofpoints - 1) { - printf("Exception: All vertices are (nearly) collinear (Tol = %g).\n", - b->epsilon); - terminatetetgen(this, 10); - } - for (j = 0; j < 3; j++) { - v2[j] = permutarray[i][j] - permutarray[0][j]; + if (!b->quiet) { + printf("Delaunizing vertices...\n"); } - 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; - } - - // 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) { - 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; - } - - // 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; - } - - // Create the initial Delaunay tetrahedralization. - initialdelaunay(permutarray[0], permutarray[1], permutarray[2], - permutarray[3]); - - if (b->verbose) { - printf(" Incrementally inserting vertices.\n"); - } - 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); - } - if (b->brio_hilbert || b->no_sort) { // -b or -b/1 - // Start the last updated tet. - searchtet.tet = recenttet.tet; - } else { // -b0 - // Randomly choose the starting tet for point location. - searchtet.tet = NULL; - } - ivf.iloc = (int) OUTSIDE; - // Insert the vertex. - 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 { - if (ivf.iloc == (int) ONVERTEX) { - // The point already exists. Mark it and do nothing on it. - swapvertex = org(searchtet); - if (b->object != tetgenbehavior::STL) { - if (!b->quiet) { - printf("Warning: Point #%d is coincident with #%d. Ignored!\n", - pointmark(permutarray[i]), pointmark(swapvertex)); - } + + // 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"); } - setpoint2ppt(permutarray[i], swapvertex); - setpointtype(permutarray[i], DUPLICATEDVERTEX); - dupverts++; - } else if (ivf.iloc == (int) NEARVERTEX) { - swapvertex = org(searchtet); - 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], 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. - setpoint2ppt(permutarray[i], swapvertex); - setpointtype(permutarray[i], DUPLICATEDVERTEX); - dupverts++; - } else if (ivf.iloc == (int) NONREGULAR) { - // The point is non-regular. Skipped. + for (i = 0; i < in->numberofpoints; i++) { + permutarray[i] = (point)points->traverse(); + } + } else { if (b->verbose) { - printf(" Point #%d is non-regular, skipped.\n", - pointmark(permutarray[i])); + 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); } - setpointtype(permutarray[i], NREGULARVERTEX); - nonregularcount++; - } } - } + tv = clock(); // Remember the time for sorting points. + // Calculate the diagonal size of its bounding box. + bboxsize = sqrt(norm2(xmax - xmin, ymax - ymin, zmax - zmin)); + bboxsize2 = bboxsize * bboxsize; + bboxsize3 = bboxsize2 * bboxsize; - delete [] permutarray; -} + // Make sure the second vertex is not identical with the first one. + i = 1; + while ((distance(permutarray[0], permutarray[i]) / bboxsize) < b->epsilon) { + i++; + if (i == in->numberofpoints - 1) { + printf("Exception: All vertices are (nearly) identical (Tol = %g).\n", 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; + } + + // 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) { + i++; + if (i == in->numberofpoints - 1) { + printf("Exception: All vertices are (nearly) collinear (Tol = %g).\n", b->epsilon); + terminatetetgen(this, 10); + } + 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; + } + + // 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) { + 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; + } + + // 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; + } + + // Create the initial Delaunay tetrahedralization. + initialdelaunay(permutarray[0], permutarray[1], permutarray[2], permutarray[3]); + + if (b->verbose) { + printf(" Incrementally inserting vertices.\n"); + } + 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); + } + if (b->brio_hilbert || b->no_sort) { // -b or -b/1 + // Start the last updated tet. + searchtet.tet = recenttet.tet; + } else { // -b0 + // Randomly choose the starting tet for point location. + searchtet.tet = NULL; + } + ivf.iloc = (int)OUTSIDE; + // Insert the vertex. + 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 { + if (ivf.iloc == (int)ONVERTEX) { + // The point already exists. Mark it and do nothing on it. + swapvertex = org(searchtet); + if (b->object != tetgenbehavior::STL) { + if (!b->quiet) { + printf("Warning: Point #%d is coincident with #%d. Ignored!\n", + pointmark(permutarray[i]), pointmark(swapvertex)); + } + } + setpoint2ppt(permutarray[i], swapvertex); + setpointtype(permutarray[i], DUPLICATEDVERTEX); + dupverts++; + } else if (ivf.iloc == (int)NEARVERTEX) { + swapvertex = org(searchtet); + 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], 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. + setpoint2ppt(permutarray[i], swapvertex); + setpointtype(permutarray[i], DUPLICATEDVERTEX); + dupverts++; + } else if (ivf.iloc == (int)NONREGULAR) { + // The point is non-regular. Skipped. + if (b->verbose) { + printf(" Point #%d is non-regular, skipped.\n", pointmark(permutarray[i])); + } + setpointtype(permutarray[i], NREGULARVERTEX); + nonregularcount++; + } + } + } + + delete[] permutarray; +} //// //// //// //// @@ -12485,16 +12354,15 @@ void tetgenmesh::incrementaldelaunay(clock_t& tv) // // /////////////////////////////////////////////////////////////////////////////// -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; +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; } /////////////////////////////////////////////////////////////////////////////// @@ -12507,111 +12375,110 @@ void tetgenmesh::flipshpush(face* flipedge) // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::flip22(face* flipfaces, int flipflag, int chkencflag) -{ - face bdedges[4], outfaces[4], infaces[4]; - face bdsegs[4]; - face checkface; - point pa, pb, pc, pd; - int i; - - pa = sorg(flipfaces[0]); - pb = sdest(flipfaces[0]); - pc = sapex(flipfaces[0]); - pd = sapex(flipfaces[1]); - - 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); - } - } - } - } - - // 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])); - } - - // 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 { - ssdissolve(bdedges[i]); +void tetgenmesh::flip22(face* flipfaces, int flipflag, int chkencflag) { + face bdedges[4], outfaces[4], infaces[4]; + face bdsegs[4]; + face checkface; + point pa, pb, pc, pd; + int i; + + pa = sorg(flipfaces[0]); + pb = sdest(flipfaces[0]); + pc = sapex(flipfaces[0]); + pd = sapex(flipfaces[1]); + + if (sorg(flipfaces[1]) != pb) { + sesymself(flipfaces[1]); } - } - if (chkencflag & 2) { - // Queue the flipped subfaces for quality/encroaching checks. - for (i = 0; i < 2; i++) { - enqueuesubface(badsubfacs, &(flipfaces[i])); + 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); + } + } + } } - } - recentsh = flipfaces[0]; + // 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 (flipflag) { - // Put the boundary edges into flip stack. + // Reconnect boundary edges to outer boundary faces. for (i = 0; i < 4; i++) { - flipshpush(&(bdedges[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 { + ssdissolve(bdedges[i]); + } + } + + if (chkencflag & 2) { + // Queue the flipped subfaces for quality/encroaching checks. + for (i = 0; i < 2; i++) { + enqueuesubface(badsubfacs, &(flipfaces[i])); + } + } + + recentsh = flipfaces[0]; + + if (flipflag) { + // Put the boundary edges into flip stack. + for (i = 0; i < 4; i++) { + flipshpush(&(bdedges[i])); + } } - } } /////////////////////////////////////////////////////////////////////////////// @@ -12629,91 +12496,90 @@ void tetgenmesh::flip22(face* flipfaces, int flipflag, int chkencflag) // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::flip31(face* flipfaces, int flipflag) -{ - face bdedges[3], outfaces[3], infaces[3]; - face bdsegs[3]; - face checkface; - point pa, pb, pc; - int i; - - 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); - } - } - } - } // 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])); - } - - // 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]); - } - } - sbond1(bdedges[i], outfaces[i]); - sbond1(infaces[i], bdedges[i]); - } - if (bdsegs[i].sh != NULL) { - ssbond(bdedges[i], bdsegs[i]); - } - } - - recentsh = flipfaces[3]; - - if (flipflag) { - // Put the boundary edges into flip stack. +void tetgenmesh::flip31(face* flipfaces, int flipflag) { + face bdedges[3], outfaces[3], infaces[3]; + face bdsegs[3]; + face checkface; + point pa, pb, pc; + int i; + + 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); + } + } + } + } // 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])); + } + + // 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++) { - flipshpush(&(bdedges[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]); + } + } + sbond1(bdedges[i], outfaces[i]); + sbond1(infaces[i], bdedges[i]); + } + if (bdsegs[i].sh != NULL) { + ssbond(bdedges[i], bdsegs[i]); + } + } + + recentsh = flipfaces[3]; + + if (flipflag) { + // Put the boundary edges into flip stack. + for (i = 0; i < 3; i++) { + flipshpush(&(bdedges[i])); + } } - } } /////////////////////////////////////////////////////////////////////////////// @@ -12722,55 +12588,54 @@ void tetgenmesh::flip31(face* flipfaces, int flipflag) // // /////////////////////////////////////////////////////////////////////////////// -long tetgenmesh::lawsonflip() -{ - badface *popface; - face flipfaces[2]; - point pa, pb, pc, pd; - REAL sign; - long flipcount = 0; - - if (b->verbose > 2) { - printf(" Lawson flip %ld edges.\n", flippool->items); - } - - while (flipstack != (badface *) NULL) { - - // 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); - - // 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; - - // 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]); +long tetgenmesh::lawsonflip() { + badface* popface; + face flipfaces[2]; + point pa, pb, pc, pd; + REAL sign; + long flipcount = 0; + + if (b->verbose > 2) { + printf(" Lawson flip %ld edges.\n", flippool->items); + } + + while (flipstack != (badface*)NULL) { + + // 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); + + // 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; - sign = incircle3d(pa, pb, pc, pd); + // 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]); + + sign = incircle3d(pa, pb, pc, pd); - if (sign < 0) { - // It is non-locally Delaunay. Flip it. - flip22(flipfaces, 1, 0); - flipcount++; + if (sign < 0) { + // It is non-locally Delaunay. Flip it. + flip22(flipfaces, 1, 0); + flipcount++; + } } - } - if (b->verbose > 2) { - printf(" Performed %ld flips.\n", flipcount); - } + if (b->verbose > 2) { + printf(" Performed %ld flips.\n", flipcount); + } - return flipcount; + return flipcount; } /////////////////////////////////////////////////////////////////////////////// @@ -12787,7 +12652,7 @@ long tetgenmesh::lawsonflip() // 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. // +// 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'. // @@ -12804,778 +12669,769 @@ long tetgenmesh::lawsonflip() // // /////////////////////////////////////////////////////////////////////////////// -int tetgenmesh::sinsertvertex(point insertpt, face *searchsh, face *splitseg, - int iloc, int bowywat, int rflag) -{ - 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(" Insert facet point %d.\n", pointmark(insertpt)); - } - - if (bowywat == 3) { - loc = INSTAR; - } - - if ((splitseg != NULL) && (splitseg->sh != NULL)) { - // A segment is going to be split, no point location. - spivot(*splitseg, *searchsh); - if (loc != INSTAR) loc = ONEDGE; - } else { - if (loc != INSTAR) loc = (enum locateresult) iloc; - if (loc == OUTSIDE) { - // Do point location in surface mesh. - if (searchsh->sh == NULL) { - *searchsh = recentsh; - } - // Search the vertex. An above point must be provided ('aflag' = 1). - loc = slocate(insertpt, searchsh, 1, 1, rflag); - } - } - - - // 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) { +int tetgenmesh::sinsertvertex(point insertpt, face* searchsh, face* splitseg, int iloc, int bowywat, + int rflag) { + 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(" Insert facet point %d.\n", pointmark(insertpt)); + } + + if (bowywat == 3) { + loc = INSTAR; + } + if ((splitseg != NULL) && (splitseg->sh != NULL)) { - splitseg->shver = 0; - pa = sorg(*splitseg); + // A segment is going to be split, no point location. + spivot(*splitseg, *searchsh); + if (loc != INSTAR) loc = ONEDGE; } else { - pa = sorg(*searchsh); - } - 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. + 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); } - } 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 = 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)); - } - if (useinsertradius) { - setfacetindex(newsh, getfacetindex(*searchsh)); - } - // 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(). - } - - // 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). + + // 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); + } + 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); + caveshlist->newindex((void**)&parysh); *parysh = neighsh; - } - } else { - sign = 1; // A boundary edge. + // 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; } - } 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. + } // 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 { - sign = 1; // A boundary edge. + if (sorg(casout) != sdest(neighsh)) sesymself(casout); + neighsh = casout; } - } 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 - - - // 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)); + // 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)); + } + if (useinsertradius) { + setfacetindex(newsh, getfacetindex(*searchsh)); + } + // 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(). } - 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); - } - - if (newsh.sh != NULL) { - // Set a handle for searching. - recentsh = newsh; - } - - // Update the point-to-subface map. - if (pointtype(insertpt) == FREEFACETVERTEX) { - setpoint2sh(insertpt, sencode(newsh)); - } - - // 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); - } - } - 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); - } - } - } - - 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); - } - // 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++) { + // 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); - spivot(cavesh, newsh); - sdissolve(newsh); - } + } // j + } // 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)); } - //recentsh = newsh; // Update the point-to-subface map. - if (pointtype(insertpt) == FREEFACETVERTEX) { - setpoint2sh(insertpt, sencode(newsh)); + 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); } - 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); - - // 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); - } - - - // 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); - } + if (newsh.sh != NULL) { + // Set a handle for searching. + recentsh = newsh; } - 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; + // Update the point-to-subface map. + if (pointtype(insertpt) == FREEFACETVERTEX) { + setpoint2sh(insertpt, sencode(newsh)); } - } // if (loc == ONEDGE) - - return (int) loc; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// sremovevertex() Remove a vertex from the surface mesh. // -// // -// '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. // -// // -// Within each facet, we first use a sequence of 2-to-2 flips to flip any // -// edge at p, finally use a 3-to-1 flip to remove p. // -// // -// All new created subfaces are returned in the global array 'caveshbdlist'. // -// The new segment (when p is on segment) is returned in 'parentseg'. // -// // + // 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); + } + } + 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); + } + } + } + + 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); + } + // 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 ((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); + + // 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); + } + + // 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); + } + } + 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) + + return (int)loc; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// sremovevertex() Remove a vertex from the surface mesh. // +// // +// '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. // +// // +// Within each facet, we first use a sequence of 2-to-2 flips to flip any // +// edge at p, finally use a 3-to-1 flip to remove p. // +// // +// All new created subfaces are returned in the global array 'caveshbdlist'. // +// The new segment (when p is on segment) is returned in 'parentseg'. // +// // // If 'lawson' > 0, the Lawson flip algorithm is used to recover Delaunay- // // ness after p is removed. // // // /////////////////////////////////////////////////////////////////////////////// -int tetgenmesh::sremovevertex(point delpt, face* parentsh, face* parentseg, - int lawson) -{ - face flipfaces[4], spinsh, *parysh; - 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; - // 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; - senextself(adjseg1); - senext2(abseg, adjseg2); - sbond(adjseg1, adjseg2); - } - // Connect [a, b]<->[b, #]. - senext(*parentseg, adjseg1); - spivotself(adjseg1); - if (adjseg1.sh != NULL) { - adjseg1.shver = 0; - 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 == NULL) { - break; // It is possible there is only one facet. - } - if (spinsh.sh == parentsh->sh) break; - } - } - - // 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); - } - // 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]. - break; - } - spivotself(neighsh); - 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] - // 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 { - // p is inside the surface. - if (b->verbose > 2) { - printf(" Remove vertex %d from surface.\n", pointmark(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. - 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); - if (spinsh.sh == parentsh->sh) break; - if (sorg(spinsh) != delpt) sesymself(spinsh); - } // 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; +int tetgenmesh::sremovevertex(point delpt, face* parentsh, face* parentseg, int lawson) { + 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; + // 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)); } - flip31(flipfaces, lawson); - for (i = 0; i < 3; i++) { - shellfacedealloc(subfaces, flipfaces[i].sh); + if (useinsertradius) { + setfacetindex(abseg, getfacetindex(*parentseg)); + } + // Connect [#, a]<->[a, b]. + senext2(prevseg, adjseg1); + spivotself(adjseg1); + if (adjseg1.sh != NULL) { + adjseg1.shver = 0; + senextself(adjseg1); + senext2(abseg, adjseg2); + sbond(adjseg1, adjseg2); + } + // Connect [a, b]<->[b, #]. + senext(*parentseg, adjseg1); + spivotself(adjseg1); + if (adjseg1.sh != NULL) { + adjseg1.shver = 0; + 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 == NULL) { + break; // It is possible there is only one facet. + } + if (spinsh.sh == parentsh->sh) break; + } } + + // 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); + } + // 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]. + break; + } + spivotself(neighsh); + 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] + // 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(); - // 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 + // 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); + } + } - if (i == caveshlist->objects) { - // 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) + // Delete the two subsegments containing p. + shellfacedealloc(subsegs, parentseg->sh); + shellfacedealloc(subsegs, prevseg.sh); + // Return the new segment. + *parentseg = abseg; + } else { + // p is inside the surface. + if (b->verbose > 2) { + printf(" Remove vertex %d from surface.\n", pointmark(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. + 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); + if (spinsh.sh == parentsh->sh) break; + if (sorg(spinsh) != delpt) sesymself(spinsh); + } // 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) { + // 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]; + } - } // it + // The edge list at p are changed. + caveshlist->restart(); + } // while (1) - cavesegshlist->restart(); + } // it - if (b->verbose > 2) { - printf(" Created %ld new subfaces.\n", caveshbdlist->objects); - } + cavesegshlist->restart(); + if (b->verbose > 2) { + printf(" Created %ld new subfaces.\n", caveshbdlist->objects); + } - if (lawson) { - lawsonflip(); - } + if (lawson) { + lawsonflip(); + } - return 0; + return 0; } /////////////////////////////////////////////////////////////////////////////// @@ -13605,196 +13461,194 @@ int tetgenmesh::sremovevertex(point delpt, face* parentsh, face* parentseg, // // /////////////////////////////////////////////////////////////////////////////// -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); - } - - // 'dummypoint' is given. Make sure it is above [a,b,c] - ori = orient3d(pa, pb, pc, dummypoint); - if (ori > 0) { - sesymself(*searchsh); // Reverse the face orientation. - } else if (ori == 0.0) { - // This case should not happen theoretically. But... - return UNKNOWN; - } - - // Find an edge of the face s.t. p lies on its right-hand side (CCW). - for (i = 0; i < 3; i++) { +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); - ori = orient3d(pa, pb, dummypoint, searchpt); - if (ori > 0) break; - senextself(*searchsh); - } - if (i == 3) { - return UNKNOWN; - } - - pc = sapex(*searchsh); - - 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; - } - } else { - 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; - } - } - } - } + pc = sapex(*searchsh); - // Move to the next face. - if (nextmove == MOVE_BC) { - senextself(*searchsh); - } else { - senext2self(*searchsh); + if (!aflag) { + // No above point is given. Calculate an above point for this facet. + calculateabovepoint4(pa, pb, pc, searchpt); } - if (!cflag) { - // NON-convex case. Check if we will cross a boundary. - if (isshsubseg(*searchsh)) { - return ENCSEGMENT; - } + + // 'dummypoint' is given. Make sure it is above [a,b,c] + ori = orient3d(pa, pb, pc, dummypoint); + if (ori > 0) { + sesymself(*searchsh); // Reverse the face orientation. + } else if (ori == 0.0) { + // This case should not happen theoretically. But... + return UNKNOWN; } - spivot(*searchsh, neighsh); - if (neighsh.sh == NULL) { - return OUTSIDE; // A hull edge. + + // 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); } - // Adjust the edge orientation. - if (sorg(neighsh) != sdest(*searchsh)) { - sesymself(neighsh); + if (i == 3) { + return UNKNOWN; } - // 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; + senext2self(*searchsh); + return ONVERTEX; } - } // while (1) + while (1) { - // assert(loc == ONFACE || loc == ONEDGE); + 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; + } + } else { + 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; + } + } + } + } - if (rflag) { - // Round the locate result before return. - REAL n[3], area_abc, area_abp, area_bcp, area_cap; + // 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; + } + } + spivot(*searchsh, neighsh); + if (neighsh.sh == NULL) { + return OUTSIDE; // A hull edge. + } + // Adjust the edge orientation. + if (sorg(neighsh) != sdest(*searchsh)) { + sesymself(neighsh); + } - pa = sorg(*searchsh); - pb = sdest(*searchsh); - pc = sapex(*searchsh); + // Update the newly discovered face and its endpoints. + *searchsh = neighsh; + pa = sorg(*searchsh); + pb = sdest(*searchsh); + pc = sapex(*searchsh); - facenormal(pa, pb, pc, n, 1, NULL); - area_abc = sqrt(dot(n, n)); + if (pc == searchpt) { + senext2self(*searchsh); + return ONVERTEX; + } - facenormal(pb, pc, searchpt, n, 1, NULL); - area_bcp = sqrt(dot(n, n)); - if ((area_bcp / area_abc) < b->epsilon) { - area_bcp = 0; // Rounding. - } + } // while (1) - facenormal(pc, pa, searchpt, n, 1, NULL); - area_cap = sqrt(dot(n, n)); - if ((area_cap / area_abc) < b->epsilon) { - area_cap = 0; // Rounding - } + // assert(loc == ONFACE || loc == ONEDGE); - 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 (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. + } + + 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 (area_abp == 0) { - if (area_bcp == 0) { - senextself(*searchsh); - loc = ONVERTEX; // p is close to b. - } else { - if (area_cap == 0) { - loc = ONVERTEX; // p is close to a. + if (area_abp == 0) { + if (area_bcp == 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]. + } + } + } else if (area_bcp == 0) { + if (area_cap == 0) { + senext2self(*searchsh); + loc = ONVERTEX; // p is close to c. + } else { + 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 = ONEDGE; // p is on edge [a,b]. - } - } - } else if (area_bcp == 0) { - if (area_cap == 0) { - senext2self(*searchsh); - loc = ONVERTEX; // p is close to c. - } else { - 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]. - } - } // if (rflag) + loc = ONFACE; // p is on face [a,b,c]. + } + } // if (rflag) - return loc; + return loc; } /////////////////////////////////////////////////////////////////////////////// @@ -13813,220 +13667,215 @@ enum tetgenmesh::locateresult tetgenmesh::slocate(point searchpt, // // /////////////////////////////////////////////////////////////////////////////// -enum tetgenmesh::interresult tetgenmesh::sscoutsegment(face *searchsh, - point endpt, int insertsegflag, int reporterrorflag, int chkencflag) -{ - face flipshs[2], neighsh; - point startpt, pa, pb, pc, pd; - enum interresult dir; - enum {MOVE_AB, MOVE_CA} nextmove; - REAL ori_ab, ori_ca, len; - - pc = NULL; // Avoid warnings from MSVC - // The origin of 'searchsh' is fixed. - startpt = sorg(*searchsh); - nextmove = MOVE_AB; // Avoid compiler warning. - - if (b->verbose > 2) { - printf(" Scout segment (%d, %d).\n", pointmark(startpt), - pointmark(endpt)); - } - len = distance(startpt, endpt); - - // Search an edge in 'searchsh' on the path of this segment. - while (1) { +enum tetgenmesh::interresult tetgenmesh::sscoutsegment(face* searchsh, point endpt, + int insertsegflag, int reporterrorflag, + int chkencflag) { + face flipshs[2], neighsh; + point startpt, pa, pb, pc, pd; + enum interresult dir; + enum { MOVE_AB, MOVE_CA } nextmove; + REAL ori_ab, ori_ca, len; - pb = sdest(*searchsh); - if (pb == endpt) { - dir = SHAREEDGE; // Found! - break; - } + pc = NULL; // Avoid warnings from MSVC + // The origin of 'searchsh' is fixed. + startpt = sorg(*searchsh); + nextmove = MOVE_AB; // Avoid compiler warning. - pc = sapex(*searchsh); - if (pc == endpt) { - senext2self(*searchsh); - sesymself(*searchsh); - dir = SHAREEDGE; // Found! - break; + if (b->verbose > 2) { + printf(" Scout segment (%d, %d).\n", pointmark(startpt), pointmark(endpt)); } + len = distance(startpt, endpt); + // Search an edge in 'searchsh' on the path of this segment. + while (1) { - // 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; + pb = sdest(*searchsh); + if (pb == endpt) { + dir = SHAREEDGE; // Found! break; - } else { // (+0) - // The segment collinear with edge [c, a]. + } + + pc = sapex(*searchsh); + if (pc == endpt) { senext2self(*searchsh); - sesymself(*searchsh); - dir = ACROSSVERT; - break; - } - } else { - if (ori_ca > 0) { // (0+) - // The segment is collinear with edge [a, b]. - dir = ACROSSVERT; + sesymself(*searchsh); + dir = SHAREEDGE; // Found! break; - } else { // (00) - // startpt == endpt. Not possible. - terminatetetgen(this, 2); - } } - } - } - - // Move 'searchsh' to the next face, keep the origin unchanged. - if (nextmove == MOVE_AB) { - if (chkencflag) { - // Do not cross boundary. - if (isshsubseg(*searchsh)) { - return ACROSSEDGE; // ACROSS_SEG - } - } - 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); - if (chkencflag) { - // Do not cross boundary. - if (isshsubseg(neighsh)) { - *searchsh = neighsh; - return ACROSSEDGE; // ACROSS_SEG - } + + // Round the results. + if ((sqrt(triarea(startpt, pb, endpt)) / len) < b->epsilon) { + ori_ab = 0.0; + } else { + ori_ab = orient3d(startpt, pb, dummypoint, endpt); } - spivotself(neighsh); - if (sdest(neighsh) != pc) sesymself(neighsh); - *searchsh = neighsh; - } - } else { // MOVE_CA - senext2(*searchsh, neighsh); - if (chkencflag) { - // Do not cross boundary. - if (isshsubseg(neighsh)) { - *searchsh = neighsh; - return ACROSSEDGE; // ACROSS_SEG - } - } - 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). - if (chkencflag) { - // Do not cross boundary. - if (isshsubseg(*searchsh)) { - return ACROSSEDGE; // ACROSS_SEG - } + if ((sqrt(triarea(pc, startpt, endpt)) / len) < b->epsilon) { + ori_ca = 0.0; + } else { + ori_ca = orient3d(pc, startpt, dummypoint, endpt); } - spivot(*searchsh, neighsh); - if (sorg(neighsh) != pb) sesymself(neighsh); - senext(neighsh, *searchsh); - } - } - } // while - - if (dir == SHAREEDGE) { - if (insertsegflag) { - // Insert the segment into the triangulation. - face newseg; - 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 (dir == ACROSSVERT) { - // A point is found collinear with this segment. - if (reporterrorflag) { - point pp = sdest(*searchsh); - printf("PLC Error: A vertex lies in a segment in facet #%d.\n", - shellmark(*searchsh)); - printf(" Vertex: [%d] (%g,%g,%g).\n",pointmark(pp),pp[0],pp[1],pp[2]); - printf(" Segment: [%d, %d]\n", pointmark(startpt), pointmark(endpt)); - } - return dir; - } - - if (dir == ACROSSEDGE) { - // Edge [b, c] intersects with the segment. - senext(*searchsh, flipshs[0]); - if (isshsubseg(flipshs[0])) { - if (reporterrorflag) { - REAL P[3], Q[3], tp = 0, tq = 0; - linelineint(startpt, endpt, pb, pc, P, Q, &tp, &tq); - printf("PLC Error: Two segments intersect at point (%g,%g,%g),", - P[0], P[1], P[2]); - printf(" in facet #%d.\n", shellmark(*searchsh)); - printf(" Segment 1: [%d, %d]\n", pointmark(pb), pointmark(pc)); - printf(" Segment 2: [%d, %d]\n", pointmark(startpt),pointmark(endpt)); - } - return dir; // ACROSS_SEG - } - // Flip edge [b, c], queue unflipped edges (for Delaunay checks). - spivot(flipshs[0], flipshs[1]); - 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); - if (ori_ab <= 0) { - flipshpush(&(flipshs[0])); - } else if (ori_ca <= 0) { - flipshpush(&(flipshs[1])); - } - // Set 'searchsh' s.t. its origin is 'startpt'. - *searchsh = flipshs[0]; - } - - return sscoutsegment(searchsh, endpt, insertsegflag, reporterrorflag, - chkencflag); + 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; + } + } else { + if (ori_ca > 0) { // (0+) + // The segment is collinear with edge [a, b]. + dir = ACROSSVERT; + break; + } else { // (00) + // startpt == endpt. Not possible. + terminatetetgen(this, 2); + } + } + } + } + + // Move 'searchsh' to the next face, keep the origin unchanged. + if (nextmove == MOVE_AB) { + if (chkencflag) { + // Do not cross boundary. + if (isshsubseg(*searchsh)) { + return ACROSSEDGE; // ACROSS_SEG + } + } + 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); + if (chkencflag) { + // Do not cross boundary. + if (isshsubseg(neighsh)) { + *searchsh = neighsh; + return ACROSSEDGE; // ACROSS_SEG + } + } + spivotself(neighsh); + if (sdest(neighsh) != pc) sesymself(neighsh); + *searchsh = neighsh; + } + } else { // MOVE_CA + senext2(*searchsh, neighsh); + if (chkencflag) { + // Do not cross boundary. + if (isshsubseg(neighsh)) { + *searchsh = neighsh; + return ACROSSEDGE; // ACROSS_SEG + } + } + 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). + if (chkencflag) { + // Do not cross boundary. + if (isshsubseg(*searchsh)) { + return ACROSSEDGE; // ACROSS_SEG + } + } + spivot(*searchsh, neighsh); + if (sorg(neighsh) != pb) sesymself(neighsh); + senext(neighsh, *searchsh); + } + } + } // while + + if (dir == SHAREEDGE) { + if (insertsegflag) { + // Insert the segment into the triangulation. + face newseg; + 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 (dir == ACROSSVERT) { + // A point is found collinear with this segment. + if (reporterrorflag) { + point pp = sdest(*searchsh); + printf("PLC Error: A vertex lies in a segment in facet #%d.\n", shellmark(*searchsh)); + printf(" Vertex: [%d] (%g,%g,%g).\n", pointmark(pp), pp[0], pp[1], pp[2]); + printf(" Segment: [%d, %d]\n", pointmark(startpt), pointmark(endpt)); + } + return dir; + } + + if (dir == ACROSSEDGE) { + // Edge [b, c] intersects with the segment. + senext(*searchsh, flipshs[0]); + if (isshsubseg(flipshs[0])) { + if (reporterrorflag) { + REAL P[3], Q[3], tp = 0, tq = 0; + linelineint(startpt, endpt, pb, pc, P, Q, &tp, &tq); + printf("PLC Error: Two segments intersect at point (%g,%g,%g),", P[0], P[1], P[2]); + printf(" in facet #%d.\n", shellmark(*searchsh)); + printf(" Segment 1: [%d, %d]\n", pointmark(pb), pointmark(pc)); + printf(" Segment 2: [%d, %d]\n", pointmark(startpt), pointmark(endpt)); + } + return dir; // ACROSS_SEG + } + // Flip edge [b, c], queue unflipped edges (for Delaunay checks). + spivot(flipshs[0], flipshs[1]); + 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); + if (ori_ab <= 0) { + flipshpush(&(flipshs[0])); + } else if (ori_ca <= 0) { + flipshpush(&(flipshs[1])); + } + // Set 'searchsh' s.t. its origin is 'startpt'. + *searchsh = flipshs[0]; + } + + return sscoutsegment(searchsh, endpt, insertsegflag, reporterrorflag, chkencflag); } /////////////////////////////////////////////////////////////////////////////// @@ -14037,89 +13886,88 @@ enum tetgenmesh::interresult tetgenmesh::sscoutsegment(face *searchsh, // // /////////////////////////////////////////////////////////////////////////////// -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 { - // A hull side. Check if it is protected by a segment. - if (!isshsubseg(searchsh)) { - // Not protected. Save this face. - if (!sinfected(searchsh)) { +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 { + // 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; + } + } + } + senextself(searchsh); + } + } + + // 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); + caveshbdlist->newindex((void**)&parysh); *parysh = searchsh; - } } - } - senextself(searchsh); } - } - // 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; + // 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++) { + spivot(searchsh, neighsh); + if (neighsh.sh != NULL) { + if (!isshsubseg(searchsh)) { + if (!sinfected(neighsh)) { + sinfect(neighsh); + caveshbdlist->newindex((void**)&parysh); + *parysh = neighsh; + } + } else { + sdissolve(neighsh); // Disconnect a protected face. + } + } + senextself(searchsh); + } } - } - // 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++) { - spivot(searchsh, neighsh); - if (neighsh.sh != NULL) { - if (!isshsubseg(searchsh)) { - if (!sinfected(neighsh)) { - sinfect(neighsh); - caveshbdlist->newindex((void **) &parysh); - *parysh = neighsh; - } + // 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 { - sdissolve(neighsh); // Disconnect a protected face. + sunmarktest(*parysh); } - } - senextself(searchsh); - } - } - - // 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); } - } - caveshlist->restart(); - caveshbdlist->restart(); + caveshlist->restart(); + caveshbdlist->restart(); } /////////////////////////////////////////////////////////////////////////////// @@ -14134,230 +13982,230 @@ void tetgenmesh::scarveholes(int holes, REAL* holelist) // // /////////////////////////////////////////////////////////////////////////////// -int tetgenmesh::triangulate(int shmark, arraypool* ptlist, arraypool* conlist, - int holes, REAL* holelist) -{ - face searchsh, newsh, *parysh; - face newseg, *paryseg; - point pa, pb, pc, *ppt, *cons; - int iloc; - int i, j; - - 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 (ptlist->objects < 2l) { - // Not a segment or a facet. - return 1; - } else 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); - setshellmark(newseg, -1); +int tetgenmesh::triangulate(int shmark, arraypool* ptlist, arraypool* conlist, int holes, + REAL* holelist) { + face searchsh, newsh, *parysh; + face newseg, *paryseg; + point pa, pb, pc, *ppt, *cons; + int iloc; + int i, j; + + 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 (ptlist->objects < 2l) { + // Not a segment or a facet. + return 1; + } else 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); + setshellmark(newseg, -1); + } + if (pointtype(pa) == VOLVERTEX) { + setpointtype(pa, FACETVERTEX); + } + if (pointtype(pb) == VOLVERTEX) { + setpointtype(pb, FACETVERTEX); + } + return 1; + } else if (ptlist->objects == 3) { + pa = *(point*)fastlookup(ptlist, 0); + pb = *(point*)fastlookup(ptlist, 1); + pc = *(point*)fastlookup(ptlist, 2); + } else { + // Calculate an above point of this facet. + if (!calculateabovepoint(ptlist, &pa, &pb, &pc)) { + if (!b->quiet) { + printf("Warning: Unable to triangulate facet #%d. Skipped!\n", shmark); + } + return 0; // The point set is degenerate. + } + } + + // Create an initial triangulation. + makeshellface(subfaces, &newsh); + setshvertices(newsh, pa, pb, pc); + setshellmark(newsh, shmark); + recentsh = newsh; + if (pointtype(pa) == VOLVERTEX) { - setpointtype(pa, FACETVERTEX); + setpointtype(pa, FACETVERTEX); } if (pointtype(pb) == VOLVERTEX) { - setpointtype(pb, FACETVERTEX); + setpointtype(pb, FACETVERTEX); } - return 1; - } else if (ptlist->objects == 3) { - pa = * (point *) fastlookup(ptlist, 0); - pb = * (point *) fastlookup(ptlist, 1); - pc = * (point *) fastlookup(ptlist, 2); - } else { - // Calculate an above point of this facet. - if (!calculateabovepoint(ptlist, &pa, &pb, &pc)) { - if (!b->quiet) { - printf("Warning: Unable to triangulate facet #%d. Skipped!\n",shmark); - } - return 0; // The point set is degenerate. - } - } - - // Create an initial triangulation. - makeshellface(subfaces, &newsh); - setshvertices(newsh, pa, pb, pc); - setshellmark(newsh, shmark); - recentsh = newsh; - - if (pointtype(pa) == VOLVERTEX) { - setpointtype(pa, FACETVERTEX); - } - if (pointtype(pb) == VOLVERTEX) { - setpointtype(pb, FACETVERTEX); - } - if (pointtype(pc) == VOLVERTEX) { - setpointtype(pc, FACETVERTEX); - } - - // Are there area constraints? - if (b->quality && (in->facetconstraintlist != NULL)) { - for (i = 0; i < in->numberoffacetconstraints; i++) { - if (shmark == ((int) in->facetconstraintlist[i * 2])) { - REAL area = in->facetconstraintlist[i * 2 + 1]; - setareabound(newsh, area); - break; - } + if (pointtype(pc) == VOLVERTEX) { + setpointtype(pc, FACETVERTEX); } - } - 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); - setshellmark(newseg, -1); - ssbond(newsh, newseg); - senextself(newsh); - } - return 1; - } - - // Triangulate the facet. It may not success (due to rounding error, or - // incorrect input data), use 'caveencshlist' and 'caveencseglist' are - // re-used to store all the newly created subfaces and segments. So we - // can clean them if the triangulation is not successful. - caveencshlist->newindex((void **) &parysh); - *parysh = newsh; - - // 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 (iloc != ((int) ONVERTEX)) { - // Point inserted successfully. - if (pointtype(*ppt) == VOLVERTEX) { - setpointtype(*ppt, FACETVERTEX); - } - // Save the set of new subfaces. - for (j = 0; j < caveshbdlist->objects; j++) { - // Get an old subface at edge [a, b]. - parysh = (face *) fastlookup(caveshbdlist, j); - spivot(*parysh, searchsh); // The new subface [a, b, p]. - // Do not save a deleted new face (degenerated). - if (searchsh.sh[3] != NULL) { - caveencshlist->newindex((void **) &parysh); - *parysh = searchsh; - } - } - // Delete all removed subfaces. - for (j = 0; j < caveshlist->objects; j++) { - parysh = (face *) fastlookup(caveshlist, j); - shellfacedealloc(subfaces, parysh->sh); + // Are there area constraints? + if (b->quality && (in->facetconstraintlist != NULL)) { + for (i = 0; i < in->numberoffacetconstraints; i++) { + if (shmark == ((int)in->facetconstraintlist[i * 2])) { + REAL area = in->facetconstraintlist[i * 2 + 1]; + setareabound(newsh, area); + break; + } } - // Clear the global lists. - caveshbdlist->restart(); - caveshlist->restart(); - cavesegshlist->restart(); - } else { - // The facet triangulation is failed. - break; - } - } - } // i - puninfect(pa); - puninfect(pb); - puninfect(pc); - - if (i < ptlist->objects) { - //The facet triangulation is failed. Clean the new subfaces. - // There is no new segment be created yet. - if (!b->quiet) { - printf("Warning: Fail to triangulate facet #%d. Skipped!\n", shmark); } - for (i = 0; i < caveencshlist->objects; i++) { - parysh = (face *) fastlookup(caveencshlist, i); - if (parysh->sh[3] != NULL) { - shellfacedealloc(subfaces, parysh->sh); - } - } - caveencshlist->restart(); - return 0; - } - - // 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 != (int) 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; - } + + 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); + setshellmark(newseg, -1); + ssbond(newsh, newseg); + senextself(newsh); } - searchsh.sh = shellfacetraverse(subfaces); - } - } - // Recover the segment. Some edges may be flipped. - if (sscoutsegment(&searchsh, cons[1], 1, 1, 0) != SHAREEDGE) { - break; // Fail to recover a segment. - } - // Save this newseg. - sspivot(searchsh, newseg); - caveencseglist->newindex((void **) &paryseg); - *paryseg = newseg; - if (flipstack != NULL) { - // Recover locally Delaunay edges. - lawsonflip(); + return 1; } - } // i - if (i < conlist->objects) { - if (!b->quiet) { - printf("Warning: Fail to recover a segment in facet #%d. Skipped!\n", - shmark); - } - for (i = 0; i < caveencshlist->objects; i++) { - parysh = (face *) fastlookup(caveencshlist, i); - if (parysh->sh[3] != NULL) { - shellfacedealloc(subfaces, parysh->sh); - } + // Triangulate the facet. It may not success (due to rounding error, or + // incorrect input data), use 'caveencshlist' and 'caveencseglist' are + // re-used to store all the newly created subfaces and segments. So we + // can clean them if the triangulation is not successful. + caveencshlist->newindex((void**)&parysh); + *parysh = newsh; + + // 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 (iloc != ((int)ONVERTEX)) { + // Point inserted successfully. + if (pointtype(*ppt) == VOLVERTEX) { + setpointtype(*ppt, FACETVERTEX); + } + // Save the set of new subfaces. + for (j = 0; j < caveshbdlist->objects; j++) { + // Get an old subface at edge [a, b]. + parysh = (face*)fastlookup(caveshbdlist, j); + spivot(*parysh, searchsh); // The new subface [a, b, p]. + // Do not save a deleted new face (degenerated). + if (searchsh.sh[3] != NULL) { + caveencshlist->newindex((void**)&parysh); + *parysh = searchsh; + } + } + // 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 { + // The facet triangulation is failed. + break; + } + } + } // i + puninfect(pa); + puninfect(pb); + puninfect(pc); + + if (i < ptlist->objects) { + // The facet triangulation is failed. Clean the new subfaces. + // There is no new segment be created yet. + if (!b->quiet) { + printf("Warning: Fail to triangulate facet #%d. Skipped!\n", shmark); + } + for (i = 0; i < caveencshlist->objects; i++) { + parysh = (face*)fastlookup(caveencshlist, i); + if (parysh->sh[3] != NULL) { + shellfacedealloc(subfaces, parysh->sh); + } + } + caveencshlist->restart(); + return 0; } - for (i = 0; i < caveencseglist->objects; i++) { - paryseg = (face *) fastlookup(caveencseglist, i); - if (paryseg->sh[3] != NULL) { - shellfacedealloc(subsegs, paryseg->sh); - } + + // 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 != (int)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); + } + } + // Recover the segment. Some edges may be flipped. + if (sscoutsegment(&searchsh, cons[1], 1, 1, 0) != SHAREEDGE) { + break; // Fail to recover a segment. + } + // Save this newseg. + sspivot(searchsh, newseg); + caveencseglist->newindex((void**)&paryseg); + *paryseg = newseg; + if (flipstack != NULL) { + // Recover locally Delaunay edges. + lawsonflip(); + } + } // i + + if (i < conlist->objects) { + if (!b->quiet) { + printf("Warning: Fail to recover a segment in facet #%d. Skipped!\n", shmark); + } + for (i = 0; i < caveencshlist->objects; i++) { + parysh = (face*)fastlookup(caveencshlist, i); + if (parysh->sh[3] != NULL) { + shellfacedealloc(subfaces, parysh->sh); + } + } + for (i = 0; i < caveencseglist->objects; i++) { + paryseg = (face*)fastlookup(caveencseglist, i); + if (paryseg->sh[3] != NULL) { + shellfacedealloc(subsegs, paryseg->sh); + } + } + caveencshlist->restart(); + caveencseglist->restart(); + return 0; } - caveencshlist->restart(); - caveencseglist->restart(); - return 0; - } - // Remove exterior and hole triangles. - scarveholes(holes, holelist); + // Remove exterior and hole triangles. + scarveholes(holes, holelist); - caveencshlist->restart(); - caveencseglist->restart(); - return 1; + caveencshlist->restart(); + caveencseglist->restart(); + return 1; } /////////////////////////////////////////////////////////////////////////////// @@ -14369,243 +14217,242 @@ int tetgenmesh::triangulate(int shmark, arraypool* ptlist, arraypool* conlist, // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::unifysegments() -{ - badface *facelink = NULL, *newlinkitem, *f1, *f2; - face *facperverlist, sface; - face subsegloop, testseg; - point torg, tdest; - REAL ori1, ori2, ori3; - REAL n1[3], n2[3]; - REAL cosang, ang, ang_tol; - int *idx2faclist; - int idx, k, m; - - if (b->verbose > 1) { - printf(" Unifying segments.\n"); - } - // The limit dihedral angle that two facets are not overlapping. - ang_tol = b->facet_overlap_ang_tol / 180.0 * PI; - if (ang_tol < 0.0) ang_tol = 0.0; - - // Create a mapping from vertices to subfaces. - makepoint2submap(subfaces, idx2faclist, facperverlist); - - - 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. - 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. - report_overlapping_facets(&(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. - report_overlapping_facets(&(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. - report_overlapping_facets(&(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. - report_overlapping_facets(&(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) { - report_overlapping_facets(&(f1->ss), &sface); - } else { - report_overlapping_facets(&(f2->ss), &sface); - } - break; +void tetgenmesh::unifysegments() { + badface *facelink = NULL, *newlinkitem, *f1, *f2; + face *facperverlist, sface; + face subsegloop, testseg; + point torg, tdest; + REAL ori1, ori2, ori3; + REAL n1[3], n2[3]; + REAL cosang, ang, ang_tol; + int* idx2faclist; + int idx, k, m; + + if (b->verbose > 1) { + printf(" Unifying segments.\n"); + } + // The limit dihedral angle that two facets are not overlapping. + ang_tol = b->facet_overlap_ang_tol / 180.0 * PI; + if (ang_tol < 0.0) ang_tol = 0.0; + + // Create a mapping from vertices to subfaces. + makepoint2submap(subfaces, idx2faclist, facperverlist); + + 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. + if (sdest(sface) != tdest) { + senext2self(sface); + sesymself(sface); } - } - // 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) { + 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. + report_overlapping_facets(&(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. + report_overlapping_facets(&(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. + report_overlapping_facets(&(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. + report_overlapping_facets(&(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) { + report_overlapping_facets(&(f1->ss), &sface); + } else { + report_overlapping_facets(&(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. + report_overlapping_facets(&(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; + } + } else { + // The first face. + newlinkitem = (badface*)flippool->alloc(); + newlinkitem->ss = sface; + newlinkitem->nextitem = NULL; + facelink = newlinkitem; + } + } // for (k = idx2faclist[idx]; ...) + + // Set the connection between this segment and faces containing it, + // at the same time, remove redundant segments. 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. - report_overlapping_facets(&(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; - } - } else { - // The first face. - newlinkitem = (badface *) flippool->alloc(); - newlinkitem->ss = sface; - newlinkitem->nextitem = NULL; - facelink = newlinkitem; - } - } // for (k = idx2faclist[idx]; ...) - - - // 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); - } - // Bonds the subface and the segment together. - ssbond(f1->ss, subsegloop); - f1 = f1->nextitem; - } - - // 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; - // Calculate the dihedral angle between the two facet. - facenormal(torg, tdest, sapex(f1->ss), n1, 1, NULL); - facenormal(torg, tdest, sapex(f2->ss), n2, 1, NULL); - cosang = dot(n1, n2) / (sqrt(dot(n1, n1)) * sqrt(dot(n2, n2))); - // Rounding. - if (cosang > 1.0) cosang = 1.0; - else if (cosang < -1.0) cosang = -1.0; - ang = acos(cosang); - if (ang < ang_tol) { - // Two facets are treated as overlapping each other. - report_overlapping_facets(&(f1->ss), &(f2->ss), ang); - } else { - // Record the smallest input dihedral angle. - if (ang < minfacetdihed) { - minfacetdihed = ang; - } - sbond1(f1->ss, f2->ss); + 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); + } + // Bonds the subface and the segment together. + ssbond(f1->ss, subsegloop); + f1 = f1->nextitem; + } + + // 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; + // Calculate the dihedral angle between the two facet. + facenormal(torg, tdest, sapex(f1->ss), n1, 1, NULL); + facenormal(torg, tdest, sapex(f2->ss), n2, 1, NULL); + cosang = dot(n1, n2) / (sqrt(dot(n1, n1)) * sqrt(dot(n2, n2))); + // Rounding. + if (cosang > 1.0) + cosang = 1.0; + else if (cosang < -1.0) + cosang = -1.0; + ang = acos(cosang); + if (ang < ang_tol) { + // Two facets are treated as overlapping each other. + report_overlapping_facets(&(f1->ss), &(f2->ss), ang); + } else { + // Record the smallest input dihedral angle. + if (ang < minfacetdihed) { + minfacetdihed = ang; + } + sbond1(f1->ss, f2->ss); + } + f1 = f2; + } } - f1 = f2; - } - } - flippool->restart(); + flippool->restart(); - // Are there length constraints? - if (b->quality && (in->segmentconstraintlist != (REAL *) NULL)) { - int e1, e2; - REAL len; - for (k = 0; k < in->numberofsegmentconstraints; k++) { - e1 = (int) in->segmentconstraintlist[k * 3]; - e2 = (int) in->segmentconstraintlist[k * 3 + 1]; - if (((pointmark(torg) == e1) && (pointmark(tdest) == e2)) || - ((pointmark(torg) == e2) && (pointmark(tdest) == e1))) { - len = in->segmentconstraintlist[k * 3 + 2]; - setareabound(subsegloop, len); - break; + // Are there length constraints? + if (b->quality && (in->segmentconstraintlist != (REAL*)NULL)) { + int e1, e2; + REAL len; + for (k = 0; k < in->numberofsegmentconstraints; k++) { + e1 = (int)in->segmentconstraintlist[k * 3]; + e2 = (int)in->segmentconstraintlist[k * 3 + 1]; + if (((pointmark(torg) == e1) && (pointmark(tdest) == e2)) || + ((pointmark(torg) == e2) && (pointmark(tdest) == e1))) { + len = in->segmentconstraintlist[k * 3 + 2]; + setareabound(subsegloop, len); + break; + } + } } - } - } - subsegloop.sh = shellfacetraverse(subsegs); - } + subsegloop.sh = shellfacetraverse(subsegs); + } - delete [] idx2faclist; - delete [] facperverlist; + delete[] idx2faclist; + delete[] facperverlist; } /////////////////////////////////////////////////////////////////////////////// @@ -14620,449 +14467,443 @@ void tetgenmesh::unifysegments() // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::identifyinputedges(point *idx2verlist) -{ - 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; - - int e1, e2; - REAL len; - - if (!b->quiet) { - printf("Inserting edges ...\n"); - } - - // Construct a map from points to subfaces. - makepoint2submap(subfaces, idx2shlist, shperverlist); - - // Process the set of input edges. - for (i = 0; i < in->numberofedges; i++) { - endpts = &(in->edgelist[(i << 1)]); - if (endpts[0] == endpts[1]) { - if (!b->quiet) { - printf("Warning: Edge #%d is degenerated. Skipped.\n", i); - } - continue; // Skip a degenerated edge. - } - // Recall that all existing segments have a default marker '-1'. - // We assign all identified segments a default marker '-2'. - edgemarker = in->edgemarkerlist ? in->edgemarkerlist[i] : -2; - - // 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 - - 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); - } - } - } else { - // It is a dangling segment (not belong to any facets). - // Get the two endpoints of this segment. - pa = idx2verlist[endpts[0]]; - pb = idx2verlist[endpts[1]]; - if (pa == pb) { - if (!b->quiet) { - printf("Warning: Edge #%d is degenerated. Skipped.\n", i); - } - continue; - } - // 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); - } - } +void tetgenmesh::identifyinputedges(point* idx2verlist) { + 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; - setshellmark(newseg, edgemarker); + int e1, e2; + REAL len; - if (b->quality && (in->segmentconstraintlist != (REAL *) NULL)) { - for (i = 0; i < in->numberofsegmentconstraints; i++) { - e1 = (int) in->segmentconstraintlist[i * 3]; - e2 = (int) in->segmentconstraintlist[i * 3 + 1]; - if (((pointmark(pa) == e1) && (pointmark(pb) == e2)) || - ((pointmark(pa) == e2) && (pointmark(pb) == e1))) { - len = in->segmentconstraintlist[i * 3 + 2]; - setareabound(newseg, len); - break; - } - } + if (!b->quiet) { + printf("Inserting edges ...\n"); } - } // i - - delete [] shperverlist; - delete [] idx2shlist; -} -/////////////////////////////////////////////////////////////////////////////// -// // -// mergefacets() Merge adjacent facets. // -// // -/////////////////////////////////////////////////////////////////////////////// + // Construct a map from points to subfaces. + makepoint2submap(subfaces, idx2shlist, shperverlist); -void tetgenmesh::mergefacets() -{ - face parentsh, neighsh, neineish; - face segloop; - point pa, pb, pc, pd; - REAL n1[3], n2[3]; - REAL cosang, cosang_tol; - - - // Allocate an array to save calcaulated dihedral angles at segments. - arraypool *dihedangarray = new arraypool(sizeof(double), 10); - REAL *paryang = NULL; - - // First, remove coplanar segments. - // The dihedral angle bound for two different facets. - cosang_tol = cos(b->facet_separate_ang_tol / 180.0 * PI); - - subsegs->traversalinit(); - segloop.sh = shellfacetraverse(subsegs); - while (segloop.sh != (shellface *) NULL) { - // Only remove a segment if it has a marker '-1'. - if (shellmark(segloop) != -1) { - segloop.sh = shellfacetraverse(subsegs); - continue; - } - 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. - // Only merge them if they have the same boundary marker. - if (shellmark(parentsh) == shellmark(neighsh)) { - pa = sorg(segloop); - pb = sdest(segloop); - pc = sapex(parentsh); - pd = sapex(neighsh); - // Calculate the dihedral angle at the segment [a,b]. - facenormal(pa, pb, pc, n1, 1, NULL); - facenormal(pa, pb, pd, n2, 1, NULL); - cosang = dot(n1, n2) / (sqrt(dot(n1, n1)) * sqrt(dot(n2, n2))); - if (cosang < cosang_tol) { - ssdissolve(parentsh); - ssdissolve(neighsh); - shellfacedealloc(subsegs, segloop.sh); - // Add the edge to flip stack. - flipshpush(&parentsh); - } else { - // Save 'cosang' to avoid re-calculate it. - // Re-use the pointer at the first segment. - dihedangarray->newindex((void **) &paryang); - *paryang = cosang; - segloop.sh[6] = (shellface) paryang; + // Process the set of input edges. + for (i = 0; i < in->numberofedges; i++) { + endpts = &(in->edgelist[(i << 1)]); + if (endpts[0] == endpts[1]) { + if (!b->quiet) { + printf("Warning: Edge #%d is degenerated. Skipped.\n", i); } - } - } // if (neineish.sh == parentsh.sh) - } - } - segloop.sh = shellfacetraverse(subsegs); - } - - // Second, remove ridge segments at small angles. - // The dihedral angle bound for two different facets. - cosang_tol = cos(b->facet_small_ang_tol / 180.0 * PI); - REAL cosang_sep_tol = cos((b->facet_separate_ang_tol - 5.0) / 180.0 * PI); - face shloop; - face seg1, seg2; - REAL cosang1, cosang2; - int i, j; - - subfaces->traversalinit(); - shloop.sh = shellfacetraverse(subfaces); - while (shloop.sh != (shellface *) NULL) { - for (i = 0; i < 3; i++) { - if (isshsubseg(shloop)) { - senext(shloop, neighsh); - if (isshsubseg(neighsh)) { - // Found two segments sharing at one vertex. - // Check if they form a small angle. - pa = sorg(shloop); - pb = sdest(shloop); - pc = sapex(shloop); - for (j = 0; j < 3; j++) n1[j] = pa[j] - pb[j]; - for (j = 0; j < 3; j++) n2[j] = pc[j] - pb[j]; - cosang = dot(n1, n2) / (sqrt(dot(n1, n1)) * sqrt(dot(n2, n2))); - if (cosang > cosang_tol) { - // Found a small angle. - segloop.sh = NULL; - sspivot(shloop, seg1); - sspivot(neighsh, seg2); - if (seg1.sh[6] != NULL) { - paryang = (REAL *) (seg1.sh[6]); - cosang1 = *paryang; + continue; // Skip a degenerated edge. + } + // Recall that all existing segments have a default marker '-1'. + // We assign all identified segments a default marker '-2'. + edgemarker = in->edgemarkerlist ? in->edgemarkerlist[i] : -2; + + // 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 { - cosang1 = 1.0; // 0 degree; + checkpt = sapex(shperverlist[j]); + if (pointmark(checkpt) == endpts[1]) { + senext2(shperverlist[j], searchsh); + sesymself(searchsh); + break; + } } - if (seg2.sh[6] != NULL) { - paryang = (REAL *) (seg2.sh[6]); - cosang2 = *paryang; + } // j + + 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 { - cosang2 = 1.0; // 0 degree; + // 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 (cosang1 < cosang_sep_tol) { - if (cosang2 < cosang_sep_tol) { - if (cosang1 < cosang2) { - segloop = seg1; - } else { - segloop = seg2; + } else { + // It is a dangling segment (not belong to any facets). + // Get the two endpoints of this segment. + pa = idx2verlist[endpts[0]]; + pb = idx2verlist[endpts[1]]; + if (pa == pb) { + if (!b->quiet) { + printf("Warning: Edge #%d is degenerated. Skipped.\n", i); } - } else { - segloop = seg1; - } - } else { - if (cosang2 < cosang_sep_tol) { - segloop = seg2; - } - } - if (segloop.sh != NULL) { - // Remove this segment. - segloop.shver = 0; - spivot(segloop, parentsh); - spivot(parentsh, neighsh); - ssdissolve(parentsh); - ssdissolve(neighsh); - shellfacedealloc(subsegs, segloop.sh); - // Add the edge to flip stack. - flipshpush(&parentsh); - break; + continue; } - } - } // if (isshsubseg) - } // if (isshsubseg) - senextself(shloop); - } - shloop.sh = shellfacetraverse(subfaces); - } + // 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); + } + } - delete dihedangarray; + setshellmark(newseg, edgemarker); - if (flipstack != NULL) { - lawsonflip(); // Recover Delaunayness. - } -} + if (b->quality && (in->segmentconstraintlist != (REAL*)NULL)) { + for (i = 0; i < in->numberofsegmentconstraints; i++) { + e1 = (int)in->segmentconstraintlist[i * 3]; + e2 = (int)in->segmentconstraintlist[i * 3 + 1]; + if (((pointmark(pa) == e1) && (pointmark(pb) == e2)) || + ((pointmark(pa) == e2) && (pointmark(pb) == e1))) { + len = in->segmentconstraintlist[i * 3 + 2]; + setareabound(newseg, len); + break; + } + } + } + } // i + + delete[] shperverlist; + delete[] idx2shlist; +} /////////////////////////////////////////////////////////////////////////////// // // -// meshsurface() Create a surface mesh of the input PLC. // +// mergefacets() Merge adjacent facets. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::meshsurface() -{ - 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->quiet) { - printf("Creating surface mesh ...\n"); - } - - // Create a map from indices to points. - makeindex2pointmap(idx2verlist); - - // Initialize arrays (block size: 2^8 = 256). - ptlist = new arraypool(sizeof(point *), 8); - conlist = new arraypool(2 * sizeof(point *), 8); - - // Loop the facet list, triangulate each facet. - 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. 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; - } +void tetgenmesh::mergefacets() { + face parentsh, neighsh, neineish; + face segloop; + point pa, pb, pc, pd; + REAL n1[3], n2[3]; + REAL cosang, cosang_tol; + + // Allocate an array to save calcaulated dihedral angles at segments. + arraypool* dihedangarray = new arraypool(sizeof(double), 10); + REAL* paryang = NULL; + + // First, remove coplanar segments. + // The dihedral angle bound for two different facets. + cosang_tol = cos(b->facet_separate_ang_tol / 180.0 * PI); + + subsegs->traversalinit(); + segloop.sh = shellfacetraverse(subsegs); + while (segloop.sh != (shellface*)NULL) { + // Only remove a segment if it has a marker '-1'. + if (shellmark(segloop) != -1) { + segloop.sh = shellfacetraverse(subsegs); + continue; + } + 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. + // Only merge them if they have the same boundary marker. + if (shellmark(parentsh) == shellmark(neighsh)) { + pa = sorg(segloop); + pb = sdest(segloop); + pc = sapex(parentsh); + pd = sapex(neighsh); + // Calculate the dihedral angle at the segment [a,b]. + facenormal(pa, pb, pc, n1, 1, NULL); + facenormal(pa, pb, pd, n2, 1, NULL); + cosang = dot(n1, n2) / (sqrt(dot(n1, n1)) * sqrt(dot(n2, n2))); + if (cosang < cosang_tol) { + ssdissolve(parentsh); + ssdissolve(neighsh); + shellfacedealloc(subsegs, segloop.sh); + // Add the edge to flip stack. + flipshpush(&parentsh); + } else { + // Save 'cosang' to avoid re-calculate it. + // Re-use the pointer at the first segment. + dihedangarray->newindex((void**)&paryang); + *paryang = cosang; + segloop.sh[6] = (shellface)paryang; + } + } + } // if (neineish.sh == parentsh.sh) + } } - } + segloop.sh = shellfacetraverse(subsegs); } - // 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. - } - tstart = idx2verlist[end1]; - // Add tstart to V if it haven't been added yet. - if (!pinfected(tstart)) { - pinfect(tstart); - ptlist->newindex((void **) &pnewpt); - *pnewpt = tstart; - } - // Loop other vertices of this polygon. - for (j = 1; j <= p->numberofvertices; j++) { - // get a vertex. - if (j < p->numberofvertices) { - end2 = p->vertexlist[j]; - } else { - end2 = p->vertexlist[0]; // Form a loop from last to first. + // Second, remove ridge segments at small angles. + // The dihedral angle bound for two different facets. + cosang_tol = cos(b->facet_small_ang_tol / 180.0 * PI); + REAL cosang_sep_tol = cos((b->facet_separate_ang_tol - 5.0) / 180.0 * PI); + face shloop; + face seg1, seg2; + REAL cosang1, cosang2; + int i, j; + + subfaces->traversalinit(); + shloop.sh = shellfacetraverse(subfaces); + while (shloop.sh != (shellface*)NULL) { + for (i = 0; i < 3; i++) { + if (isshsubseg(shloop)) { + senext(shloop, neighsh); + if (isshsubseg(neighsh)) { + // Found two segments sharing at one vertex. + // Check if they form a small angle. + pa = sorg(shloop); + pb = sdest(shloop); + pc = sapex(shloop); + for (j = 0; j < 3; j++) + n1[j] = pa[j] - pb[j]; + for (j = 0; j < 3; j++) + n2[j] = pc[j] - pb[j]; + cosang = dot(n1, n2) / (sqrt(dot(n1, n1)) * sqrt(dot(n2, n2))); + if (cosang > cosang_tol) { + // Found a small angle. + segloop.sh = NULL; + sspivot(shloop, seg1); + sspivot(neighsh, seg2); + if (seg1.sh[6] != NULL) { + paryang = (REAL*)(seg1.sh[6]); + cosang1 = *paryang; + } else { + cosang1 = 1.0; // 0 degree; + } + if (seg2.sh[6] != NULL) { + paryang = (REAL*)(seg2.sh[6]); + cosang2 = *paryang; + } else { + cosang2 = 1.0; // 0 degree; + } + if (cosang1 < cosang_sep_tol) { + if (cosang2 < cosang_sep_tol) { + if (cosang1 < cosang2) { + segloop = seg1; + } else { + segloop = seg2; + } + } else { + segloop = seg1; + } + } else { + if (cosang2 < cosang_sep_tol) { + segloop = seg2; + } + } + if (segloop.sh != NULL) { + // Remove this segment. + segloop.shver = 0; + spivot(segloop, parentsh); + spivot(parentsh, neighsh); + ssdissolve(parentsh); + ssdissolve(neighsh); + shellfacedealloc(subsegs, segloop.sh); + // Add the edge to flip stack. + flipshpush(&parentsh); + break; + } + } + } // if (isshsubseg) + } // if (isshsubseg) + senextself(shloop); } - if ((end2 < in->firstnumber) || - (end2 >= in->firstnumber + in->numberofpoints)) { - if (!b->quiet) { - printf("Warning: Invalid vertex %d in polygon %d", end2, i + 1); - printf(" in facet %d.\n", shmark); - } - } else { - if (end1 != end2) { - // 'end1' and 'end2' form a segment. - tend = idx2verlist[end2]; + shloop.sh = shellfacetraverse(subfaces); + } + + delete dihedangarray; + + if (flipstack != NULL) { + lawsonflip(); // Recover Delaunayness. + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// meshsurface() Create a surface mesh of the input PLC. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::meshsurface() { + 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->quiet) { + printf("Creating surface mesh ...\n"); + } + + // Create a map from indices to points. + makeindex2pointmap(idx2verlist); + + // Initialize arrays (block size: 2^8 = 256). + ptlist = new arraypool(sizeof(point*), 8); + conlist = new arraypool(2 * sizeof(point*), 8); + + // Loop the facet list, triangulate each facet. + 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. 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; + } + } + } + } + + // 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. + } + tstart = idx2verlist[end1]; // Add tstart to V if it haven't been added yet. - if (!pinfected(tend)) { - pinfect(tend); - ptlist->newindex((void **) &pnewpt); - *pnewpt = tend; - } - // Save the segment in S (conlist). - 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 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. - if (!b->quiet) { - printf("Warning: Polygon %d has two identical verts", i + 1); - printf(" in facet %d.\n", shmark); - } - } - // Ignore this vertex. - } + if (!pinfected(tstart)) { + pinfect(tstart); + ptlist->newindex((void**)&pnewpt); + *pnewpt = tstart; + } + // Loop other vertices of this polygon. + for (j = 1; j <= p->numberofvertices; j++) { + // get a vertex. + if (j < p->numberofvertices) { + end2 = p->vertexlist[j]; + } else { + end2 = p->vertexlist[0]; // Form a loop from last to first. + } + if ((end2 < in->firstnumber) || (end2 >= in->firstnumber + in->numberofpoints)) { + if (!b->quiet) { + printf("Warning: Invalid vertex %d in polygon %d", end2, i + 1); + printf(" in facet %d.\n", shmark); + } + } else { + if (end1 != end2) { + // 'end1' and 'end2' form a segment. + tend = idx2verlist[end2]; + // Add tstart to V if it haven't been added yet. + if (!pinfected(tend)) { + pinfect(tend); + ptlist->newindex((void**)&pnewpt); + *pnewpt = tend; + } + // Save the segment in S (conlist). + 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 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. + if (!b->quiet) { + printf("Warning: Polygon %d has two identical verts", i + 1); + printf(" in facet %d.\n", shmark); + } + } + // Ignore this vertex. + } + } + // Is the polygon degenerate (a segment or a vertex)? + if (p->numberofvertices == 2) break; + } + } + // Unmark vertices. + for (i = 0; i < ptlist->objects; i++) { + pnewpt = (point*)fastlookup(ptlist, i); + puninfect(*pnewpt); } - // Is the polygon degenerate (a segment or a vertex)? - if (p->numberofvertices == 2) break; - } + + // Triangulate F into a CDT. + // If in->facetmarklist is NULL, use the default marker -1. + triangulate(in->facetmarkerlist ? in->facetmarkerlist[shmark - 1] : -1, ptlist, conlist, + f->numberofholes, f->holelist); + + // Clear working lists. + ptlist->restart(); + conlist->restart(); } - // Unmark vertices. - for (i = 0; i < ptlist->objects; i++) { - pnewpt = (point *) fastlookup(ptlist, i); - puninfect(*pnewpt); - } - - // Triangulate F into a CDT. - // If in->facetmarklist is NULL, use the default marker -1. - triangulate(in->facetmarkerlist ? in->facetmarkerlist[shmark - 1] : -1, - ptlist, conlist, f->numberofholes, f->holelist); - - // Clear working lists. - ptlist->restart(); - conlist->restart(); - } - - if (!b->diagnose) { - // Remove redundant segments and build the face links. - unifysegments(); - if (in->numberofedges > 0) { - // There are input segments. Insert them. - identifyinputedges(idx2verlist); - } - if (!b->psc && !b->nomergefacet && - (!b->nobisect || (b->nobisect && !b->nobisect_nomerge))) { - // Merge coplanar facets. - mergefacets(); - } - } - - if (b->object == tetgenbehavior::STL) { - // Remove redundant vertices (for .stl input mesh). - jettisonnodes(); - } - - if (b->verbose) { - printf(" %ld (%ld) subfaces (segments).\n", subfaces->items, - subsegs->items); - } - - // The total number of iunput segments. - insegments = subsegs->items; - - delete [] idx2verlist; - delete ptlist; - delete conlist; + + if (!b->diagnose) { + // Remove redundant segments and build the face links. + unifysegments(); + if (in->numberofedges > 0) { + // There are input segments. Insert them. + identifyinputedges(idx2verlist); + } + if (!b->psc && !b->nomergefacet && + (!b->nobisect || (b->nobisect && !b->nobisect_nomerge))) { + // Merge coplanar facets. + mergefacets(); + } + } + + if (b->object == tetgenbehavior::STL) { + // Remove redundant vertices (for .stl input mesh). + jettisonnodes(); + } + + if (b->verbose) { + printf(" %ld (%ld) subfaces (segments).\n", subfaces->items, subsegs->items); + } + + // The total number of iunput segments. + insegments = subsegs->items; + + delete[] idx2verlist; + delete ptlist; + delete conlist; } /////////////////////////////////////////////////////////////////////////////// @@ -15089,147 +14930,144 @@ void 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) -{ - shellface **leftarray, **rightarray; - face sface1, sface2; - point p1, p2, p3; - point p4, p5, p6; - enum interresult intersect; - REAL split; - bool toleft, toright; - int leftsize, rightsize; - int i, j; - - if (b->verbose > 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(this, 1); - } - rightarray = new shellface*[arraysize]; - if (rightarray == NULL) { - terminatetetgen(this, 1); - } - leftsize = rightsize = 0; - - if (axis == 0) { - // Split along x-axis. - split = 0.5 * (bxmin + bxmax); - } else if (axis == 1) { - // Split along y-axis. - split = 0.5 * (bymin + bymax); - } else { - // Split along z-axis. - split = 0.5 * (bzmin + bzmax); - } - - for (i = 0; i < arraysize; i++) { - sface1.sh = subfacearray[i]; - p1 = (point) sface1.sh[3]; - p2 = (point) sface1.sh[4]; - p3 = (point) sface1.sh[5]; - toleft = toright = false; - if (p1[axis] < split) { - toleft = true; - if (p2[axis] >= split || p3[axis] >= split) { - toright = true; - } - } else if (p1[axis] > split) { - toright = true; - if (p2[axis] <= split || p3[axis] <= split) { - toleft = true; - } - } else { - // p1[axis] == split; - toleft = true; - toright = true; - } - if (toleft) { - leftarray[leftsize] = sface1.sh; - leftsize++; - } - if (toright) { - rightarray[rightsize] = sface1.sh; - rightsize++; - } - } - - if (leftsize < arraysize && rightsize < arraysize) { - // Continue to partition the input set. Now 'subfacearray' has been - // split into two sets, it's memory can be freed. 'leftarray' and - // 'rightarray' will be freed in the next recursive (after they're - // partitioned again or performing tests). - delete [] subfacearray; - // Continue to split these two sets. +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; + point p1, p2, p3; + point p4, p5, p6; + enum interresult intersect; + REAL split; + bool toleft, toright; + int leftsize, rightsize; + int i, j; + + if (b->verbose > 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(this, 1); + } + rightarray = new shellface*[arraysize]; + if (rightarray == NULL) { + terminatetetgen(this, 1); + } + leftsize = rightsize = 0; + if (axis == 0) { - interecursive(leftarray, leftsize, 1, bxmin, split, bymin, bymax, - bzmin, bzmax, internum); - interecursive(rightarray, rightsize, 1, split, bxmax, bymin, bymax, - bzmin, bzmax, internum); + // Split along x-axis. + split = 0.5 * (bxmin + bxmax); } else if (axis == 1) { - interecursive(leftarray, leftsize, 2, bxmin, bxmax, bymin, split, - bzmin, bzmax, internum); - interecursive(rightarray, rightsize, 2, bxmin, bxmax, split, bymax, - bzmin, bzmax, internum); + // Split along y-axis. + split = 0.5 * (bymin + bymax); } else { - interecursive(leftarray, leftsize, 0, bxmin, bxmax, bymin, bymax, - bzmin, split, internum); - interecursive(rightarray, rightsize, 0, bxmin, bxmax, bymin, bymax, - split, bzmax, internum); - } - } else { - if (b->verbose > 1) { - printf(" Checking intersecting faces.\n"); + // Split along z-axis. + split = 0.5 * (bzmin + bzmax); } - // Perform a brute-force compare on the set. + for (i = 0; i < arraysize; i++) { - sface1.sh = subfacearray[i]; - p1 = (point) sface1.sh[3]; - p2 = (point) sface1.sh[4]; - p3 = (point) sface1.sh[5]; - for (j = i + 1; j < arraysize; j++) { - sface2.sh = subfacearray[j]; - p4 = (point) sface2.sh[3]; - p5 = (point) sface2.sh[4]; - p6 = (point) sface2.sh[5]; - intersect = (enum interresult) tri_tri_inter(p1, p2, p3, p4, p5, p6); - if (intersect == INTERSECT || intersect == SHAREFACE) { - if (!b->quiet) { - if (intersect == INTERSECT) { - printf(" Facet #%d intersects facet #%d at triangles:\n", - shellmark(sface1), shellmark(sface2)); - printf(" (%4d, %4d, %4d) and (%4d, %4d, %4d)\n", - pointmark(p1), pointmark(p2), pointmark(p3), - pointmark(p4), pointmark(p5), pointmark(p6)); - } else { - printf(" Facet #%d duplicates facet #%d at triangle:\n", - shellmark(sface1), shellmark(sface2)); - printf(" (%4d, %4d, %4d) and (%4d, %4d, %4d)\n", - pointmark(p1), pointmark(p2), pointmark(p3), - pointmark(p4), pointmark(p5), pointmark(p6)); + sface1.sh = subfacearray[i]; + p1 = (point)sface1.sh[3]; + p2 = (point)sface1.sh[4]; + p3 = (point)sface1.sh[5]; + toleft = toright = false; + if (p1[axis] < split) { + toleft = true; + if (p2[axis] >= split || p3[axis] >= split) { + toright = true; } - } - // Increase the number of intersecting pairs. - (*internum)++; - // Infect these two faces (although they may already be infected). - sinfect(sface1); - sinfect(sface2); - } - } - } - // Don't forget to free all three arrays. No further partition. - delete [] leftarray; - delete [] rightarray; - delete [] subfacearray; - } + } else if (p1[axis] > split) { + toright = true; + if (p2[axis] <= split || p3[axis] <= split) { + toleft = true; + } + } else { + // p1[axis] == split; + toleft = true; + toright = true; + } + if (toleft) { + leftarray[leftsize] = sface1.sh; + leftsize++; + } + if (toright) { + rightarray[rightsize] = sface1.sh; + rightsize++; + } + } + + if (leftsize < arraysize && rightsize < arraysize) { + // Continue to partition the input set. Now 'subfacearray' has been + // split into two sets, it's memory can be freed. 'leftarray' and + // 'rightarray' will be freed in the next recursive (after they're + // partitioned again or performing tests). + delete[] subfacearray; + // Continue to split these two sets. + if (axis == 0) { + interecursive(leftarray, leftsize, 1, bxmin, split, bymin, bymax, bzmin, bzmax, + internum); + interecursive(rightarray, rightsize, 1, split, bxmax, bymin, bymax, bzmin, bzmax, + internum); + } else if (axis == 1) { + interecursive(leftarray, leftsize, 2, bxmin, bxmax, bymin, split, bzmin, bzmax, + internum); + interecursive(rightarray, rightsize, 2, bxmin, bxmax, split, bymax, bzmin, bzmax, + internum); + } else { + interecursive(leftarray, leftsize, 0, bxmin, bxmax, bymin, bymax, bzmin, split, + internum); + interecursive(rightarray, rightsize, 0, bxmin, bxmax, bymin, bymax, split, bzmax, + internum); + } + } else { + if (b->verbose > 1) { + printf(" Checking intersecting faces.\n"); + } + // Perform a brute-force compare on the set. + for (i = 0; i < arraysize; i++) { + sface1.sh = subfacearray[i]; + p1 = (point)sface1.sh[3]; + p2 = (point)sface1.sh[4]; + p3 = (point)sface1.sh[5]; + for (j = i + 1; j < arraysize; j++) { + sface2.sh = subfacearray[j]; + p4 = (point)sface2.sh[3]; + p5 = (point)sface2.sh[4]; + p6 = (point)sface2.sh[5]; + intersect = (enum interresult)tri_tri_inter(p1, p2, p3, p4, p5, p6); + if (intersect == INTERSECT || intersect == SHAREFACE) { + if (!b->quiet) { + if (intersect == INTERSECT) { + printf(" Facet #%d intersects facet #%d at triangles:\n", + shellmark(sface1), shellmark(sface2)); + printf(" (%4d, %4d, %4d) and (%4d, %4d, %4d)\n", pointmark(p1), + pointmark(p2), pointmark(p3), pointmark(p4), pointmark(p5), + pointmark(p6)); + } else { + printf(" Facet #%d duplicates facet #%d at triangle:\n", + shellmark(sface1), shellmark(sface2)); + printf(" (%4d, %4d, %4d) and (%4d, %4d, %4d)\n", pointmark(p1), + pointmark(p2), pointmark(p3), pointmark(p4), pointmark(p5), + pointmark(p6)); + } + } + // Increase the number of intersecting pairs. + (*internum)++; + // Infect these two faces (although they may already be infected). + sinfect(sface1); + sinfect(sface2); + } + } + } + // Don't forget to free all three arrays. No further partition. + delete[] leftarray; + delete[] rightarray; + delete[] subfacearray; + } } /////////////////////////////////////////////////////////////////////////////// @@ -15257,61 +15095,59 @@ void tetgenmesh::interecursive(shellface** subfacearray, int arraysize, // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::detectinterfaces() -{ - shellface **subfacearray; - face shloop; - int internum; - int i; - - if (!b->quiet) { - printf("Detecting self-intersecting facets...\n"); - } - - // Construct a map from indices to subfaces; - subfacearray = new shellface*[subfaces->items]; - subfaces->traversalinit(); - shloop.sh = shellfacetraverse(subfaces); - i = 0; - while (shloop.sh != (shellface *) NULL) { - subfacearray[i] = shloop.sh; +void tetgenmesh::detectinterfaces() { + shellface** subfacearray; + face shloop; + int internum; + int i; + + if (!b->quiet) { + printf("Detecting self-intersecting facets...\n"); + } + + // Construct a map from indices to subfaces; + subfacearray = new shellface*[subfaces->items]; + subfaces->traversalinit(); shloop.sh = shellfacetraverse(subfaces); - i++; - } + i = 0; + while (shloop.sh != (shellface*)NULL) { + subfacearray[i] = shloop.sh; + shloop.sh = shellfacetraverse(subfaces); + i++; + } - internum = 0; - // Recursively split the set of triangles into two sets using a cut plane - // parallel to x-, or, y-, or z-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); + internum = 0; + // Recursively split the set of triangles into two sets using a cut plane + // 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); + + if (!b->quiet) { + if (internum > 0) { + printf("\n!! Found %d pairs of faces are intersecting.\n\n", internum); + } else { + printf("\nNo faces are intersecting.\n\n"); + } + } - if (!b->quiet) { if (internum > 0) { - printf("\n!! Found %d pairs of faces are intersecting.\n\n", internum); + // Traverse all subfaces, deallocate those have not been infected (they + // are not intersecting faces). Uninfect those have been infected. + // After this loop, only intersecting faces remain. + subfaces->traversalinit(); + shloop.sh = shellfacetraverse(subfaces); + while (shloop.sh != (shellface*)NULL) { + if (sinfected(shloop)) { + suninfect(shloop); + } else { + shellfacedealloc(subfaces, shloop.sh); + } + shloop.sh = shellfacetraverse(subfaces); + } } else { - printf("\nNo faces are intersecting.\n\n"); + // Deallocate all subfaces. + subfaces->restart(); } - } - - if (internum > 0) { - // Traverse all subfaces, deallocate those have not been infected (they - // are not intersecting faces). Uninfect those have been infected. - // After this loop, only intersecting faces remain. - subfaces->traversalinit(); - shloop.sh = shellfacetraverse(subfaces); - while (shloop.sh != (shellface *) NULL) { - if (sinfected(shloop)) { - suninfect(shloop); - } else { - shellfacedealloc(subfaces, shloop.sh); - } - shloop.sh = shellfacetraverse(subfaces); - } - } else { - // Deallocate all subfaces. - subfaces->restart(); - } } //// //// @@ -15332,66 +15168,65 @@ void tetgenmesh::detectinterfaces() // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::makesegmentendpointsmap() -{ - arraypool *segptlist; - face segloop, prevseg, nextseg; - point eorg, edest, *parypt; - int segindex = 0, idx = 0; - int i; - - if (b->verbose > 0) { - printf(" Creating the segment-endpoints map.\n"); - } - - segptlist = new arraypool(2 * sizeof(point), 10); - - // 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); - 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++; +void tetgenmesh::makesegmentendpointsmap() { + arraypool* segptlist; + face segloop, prevseg, nextseg; + point eorg, edest, *parypt; + int segindex = 0, idx = 0; + int i; + + if (b->verbose > 0) { + printf(" Creating the segment-endpoints map.\n"); } + + segptlist = new arraypool(2 * sizeof(point), 10); + + // 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); + 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); + } - if (b->verbose) { - printf(" Found %ld segments.\n", segptlist->objects); - } + if (b->verbose) { + printf(" Found %ld segments.\n", segptlist->objects); + } - segmentendpointslist = new point[segptlist->objects * 2]; + segmentendpointslist = new point[segptlist->objects * 2]; - totalworkmemory += (segptlist->objects * 2) * sizeof(point *); + totalworkmemory += (segptlist->objects * 2) * sizeof(point*); - for (i = 0; i < segptlist->objects; i++) { - parypt = (point *) fastlookup(segptlist, i); - segmentendpointslist[idx++] = parypt[0]; - segmentendpointslist[idx++] = parypt[1]; - } + for (i = 0; i < segptlist->objects; i++) { + parypt = (point*)fastlookup(segptlist, i); + segmentendpointslist[idx++] = parypt[0]; + segmentendpointslist[idx++] = parypt[1]; + } - delete segptlist; + delete segptlist; } /////////////////////////////////////////////////////////////////////////////// @@ -15412,183 +15247,180 @@ void tetgenmesh::makesegmentendpointsmap() // // /////////////////////////////////////////////////////////////////////////////// -enum tetgenmesh::interresult - tetgenmesh::finddirection(triface* searchtet, point endpt) -{ - triface neightet; - point pa, pb, pc, pd; - enum {HMOVE, RMOVE, LMOVE} nextmove; - REAL hori, rori, lori; - int t1ver; - int s; - - // The origin is fixed. - pa = org(*searchtet); - if ((point) searchtet->tet[7] == dummypoint) { - // A hull tet. Choose the neighbor of its base face. - 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 { - searchtet->ver = 0; - } - } - - pb = dest(*searchtet); - // Check whether the destination or apex is 'endpt'. - if (pb == endpt) { - // pa->pb is the search edge. - return ACROSSVERT; - } - - pc = apex(*searchtet); - if (pc == endpt) { - // pa->pc is the search edge. - eprevesymself(*searchtet); - return ACROSSVERT; - } - - // Walk through tets around pa until the right one is found. - while (1) { - - pd = oppo(*searchtet); - // Check whether the opposite vertex is 'endpt'. - if (pd == endpt) { - // pa->pd is the search edge. - esymself(*searchtet); - 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. - if (nonconvex) { - return ACROSSFACE; // return ACROSSSUB; // Hit a bounday. - } else { - terminatetetgen(this, 2); - } - } - - // Now assume that the base face abc coincides with the horizon plane, - // and d lies above the horizon. The search point 'endpt' may lie - // above or below the horizon. We test the orientations of 'endpt' - // with respect to three planes: abc (horizon), bad (right plane), - // and acd (left plane). - hori = orient3d(pa, pb, pc, endpt); - rori = orient3d(pb, pa, pd, endpt); - lori = orient3d(pa, pc, pd, endpt); - - // Now decide the tet to move. It is possible there are more than one - // 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. - s = randomnation(3); - if (s == 0) { - nextmove = HMOVE; - } else if (s == 1) { - nextmove = RMOVE; - } else { - nextmove = LMOVE; - } - } else { - // Two tets, below horizon and below right, are viable. - if (randomnation(2)) { - nextmove = HMOVE; - } else { - nextmove = RMOVE; - } - } - } else { - if (lori > 0) { - // Two tets, below horizon and below left, are viable. - if (randomnation(2)) { - nextmove = HMOVE; - } else { - nextmove = LMOVE; - } - } else { - // The tet below horizon is chosen. - nextmove = HMOVE; - } - } - } else { - if (rori > 0) { - if (lori > 0) { - // Two tets, below right and below left, are viable. - if (randomnation(2)) { - nextmove = RMOVE; - } else { - nextmove = LMOVE; - } - } else { - // The tet below right is chosen. - nextmove = RMOVE; - } - } else { - if (lori > 0) { - // The tet below left is chosen. - nextmove = LMOVE; +enum tetgenmesh::interresult tetgenmesh::finddirection(triface* searchtet, point endpt) { + triface neightet; + point pa, pb, pc, pd; + enum { HMOVE, RMOVE, LMOVE } nextmove; + REAL hori, rori, lori; + int t1ver; + int s; + + // The origin is fixed. + pa = org(*searchtet); + if ((point)searchtet->tet[7] == dummypoint) { + // A hull tet. Choose the neighbor of its base face. + 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 { - // 'endpt' lies either on the plane(s) or across face bcd. - if (hori == 0) { - if (rori == 0) { - // pa->'endpt' is COLLINEAR with pa->pb. - return ACROSSVERT; - } - if (lori == 0) { - // pa->'endpt' is COLLINEAR with pa->pc. - eprevesymself(*searchtet); // [a,c,d] - return ACROSSVERT; - } - // pa->'endpt' crosses the edge pb->pc. - return ACROSSEDGE; - } - if (rori == 0) { - if (lori == 0) { - // pa->'endpt' is COLLINEAR with pa->pd. - esymself(*searchtet); // face bad. - enextself(*searchtet); // face [a,d,b] - return ACROSSVERT; - } - // pa->'endpt' crosses the edge pb->pd. - esymself(*searchtet); // face bad. - enextself(*searchtet); // face adb - return ACROSSEDGE; - } - if (lori == 0) { - // pa->'endpt' crosses the edge pc->pd. - eprevesymself(*searchtet); // [a,c,d] - return ACROSSEDGE; - } - // pa->'endpt' crosses the face bcd. - return ACROSSFACE; + searchtet->ver = 0; } - } } - // Move to the next tet, fix pa as its origin. - if (nextmove == RMOVE) { - fnextself(*searchtet); - } else if (nextmove == LMOVE) { - eprevself(*searchtet); - fnextself(*searchtet); - enextself(*searchtet); - } else { // HMOVE - fsymself(*searchtet); - enextself(*searchtet); - } pb = dest(*searchtet); + // Check whether the destination or apex is 'endpt'. + if (pb == endpt) { + // pa->pb is the search edge. + return ACROSSVERT; + } + pc = apex(*searchtet); + if (pc == endpt) { + // pa->pc is the search edge. + eprevesymself(*searchtet); + return ACROSSVERT; + } + + // Walk through tets around pa until the right one is found. + while (1) { + + pd = oppo(*searchtet); + // Check whether the opposite vertex is 'endpt'. + if (pd == endpt) { + // pa->pd is the search edge. + esymself(*searchtet); + 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. + if (nonconvex) { + return ACROSSFACE; // return ACROSSSUB; // Hit a bounday. + } else { + terminatetetgen(this, 2); + } + } + + // Now assume that the base face abc coincides with the horizon plane, + // and d lies above the horizon. The search point 'endpt' may lie + // above or below the horizon. We test the orientations of 'endpt' + // with respect to three planes: abc (horizon), bad (right plane), + // and acd (left plane). + hori = orient3d(pa, pb, pc, endpt); + rori = orient3d(pb, pa, pd, endpt); + lori = orient3d(pa, pc, pd, endpt); + + // Now decide the tet to move. It is possible there are more than one + // 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. + s = randomnation(3); + if (s == 0) { + nextmove = HMOVE; + } else if (s == 1) { + nextmove = RMOVE; + } else { + nextmove = LMOVE; + } + } else { + // Two tets, below horizon and below right, are viable. + if (randomnation(2)) { + nextmove = HMOVE; + } else { + nextmove = RMOVE; + } + } + } else { + if (lori > 0) { + // Two tets, below horizon and below left, are viable. + if (randomnation(2)) { + nextmove = HMOVE; + } else { + nextmove = LMOVE; + } + } else { + // The tet below horizon is chosen. + nextmove = HMOVE; + } + } + } else { + if (rori > 0) { + if (lori > 0) { + // Two tets, below right and below left, are viable. + if (randomnation(2)) { + nextmove = RMOVE; + } else { + nextmove = LMOVE; + } + } else { + // The tet below right is chosen. + nextmove = RMOVE; + } + } else { + if (lori > 0) { + // The tet below left is chosen. + nextmove = LMOVE; + } else { + // 'endpt' lies either on the plane(s) or across face bcd. + if (hori == 0) { + if (rori == 0) { + // pa->'endpt' is COLLINEAR with pa->pb. + return ACROSSVERT; + } + if (lori == 0) { + // pa->'endpt' is COLLINEAR with pa->pc. + eprevesymself(*searchtet); // [a,c,d] + return ACROSSVERT; + } + // pa->'endpt' crosses the edge pb->pc. + return ACROSSEDGE; + } + if (rori == 0) { + if (lori == 0) { + // pa->'endpt' is COLLINEAR with pa->pd. + esymself(*searchtet); // face bad. + enextself(*searchtet); // face [a,d,b] + return ACROSSVERT; + } + // pa->'endpt' crosses the edge pb->pd. + esymself(*searchtet); // face bad. + enextself(*searchtet); // face adb + return ACROSSEDGE; + } + if (lori == 0) { + // pa->'endpt' crosses the edge pc->pd. + eprevesymself(*searchtet); // [a,c,d] + return ACROSSEDGE; + } + // pa->'endpt' crosses the face bcd. + return ACROSSFACE; + } + } + } - } // while (1) + // Move to the next tet, fix pa as its origin. + if (nextmove == RMOVE) { + fnextself(*searchtet); + } else if (nextmove == LMOVE) { + eprevself(*searchtet); + fnextself(*searchtet); + enextself(*searchtet); + } else { // HMOVE + fsymself(*searchtet); + enextself(*searchtet); + } + pb = dest(*searchtet); + pc = apex(*searchtet); + } // while (1) } /////////////////////////////////////////////////////////////////////////////// @@ -15602,201 +15434,198 @@ enum tetgenmesh::interresult // 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'. // +// which containing 'refpt'. // // // // The parameter 'sedge' is used to report self-intersection. It is the // // whose endpoints are 'startpt' and 'endpt'. It must not be a NULL. // // /////////////////////////////////////////////////////////////////////////////// -enum tetgenmesh::interresult tetgenmesh::scoutsegment(point startpt,point endpt, - face *sedge, triface* searchtet, point* refpt, arraypool* intfacelist) -{ - point pd; - enum interresult dir; - int t1ver; - - if (b->verbose > 2) { - printf(" Scout seg (%d, %d).\n",pointmark(startpt),pointmark(endpt)); - } - - point2tetorg(startpt, *searchtet); - dir = finddirection(searchtet, endpt); - - if (dir == ACROSSVERT) { - pd = dest(*searchtet); - if (pd == endpt) { - if (issubseg(*searchtet)) { - report_selfint_edge(startpt, endpt, sedge, searchtet, dir); - } - return SHAREEDGE; - } else { - // A point is on the path. - report_selfint_edge(startpt, endpt, sedge, searchtet, dir); - return ACROSSVERT; - } - } - - // dir is either ACROSSEDGE or ACROSSFACE. - enextesymself(*searchtet); // Go to the opposite face. - fsymself(*searchtet); // Enter the adjacent tet. +enum tetgenmesh::interresult tetgenmesh::scoutsegment(point startpt, point endpt, face* sedge, + triface* searchtet, point* refpt, + arraypool* intfacelist) { + point pd; + enum interresult dir; + int t1ver; - if (dir == ACROSSEDGE) { - // Check whether two segments are intersecting. - if (issubseg(*searchtet)) { - report_selfint_edge(startpt, endpt, sedge, searchtet, dir); - } - } else if (dir == ACROSSFACE) { - if (checksubfaceflag) { - // Check whether a segment and a subface are intersecting. - if (issubface(*searchtet)) { - report_selfint_edge(startpt, endpt, sedge, searchtet, dir); - } + if (b->verbose > 2) { + printf(" Scout seg (%d, %d).\n", pointmark(startpt), pointmark(endpt)); } - } else { - terminatetetgen(this, 2); - } - if (refpt == NULL) { - // Do not need a reference point. Return. - return dir; - } - - triface neightet, reftet; - point pa, pb, pc; - REAL angmax, ang; - int types[2], poss[4]; - int pos = 0, i, j; - - pa = org(*searchtet); - angmax = interiorangle(pa, startpt, endpt, NULL); - *refpt = pa; - pb = dest(*searchtet); - ang = interiorangle(pb, startpt, endpt, NULL); - if (ang > angmax) { - angmax = ang; - *refpt = pb; - } - pc = apex(*searchtet); - ang = interiorangle(pc, startpt, endpt, NULL); - if (ang > angmax) { - angmax = ang; - *refpt = pc; - } - reftet = *searchtet; // Save the tet containing the refpt. - - // Search intersecting faces along the segment. - while (1) { - - - pd = oppo(*searchtet); - - - // Stop if we meet 'endpt'. - if (pd == endpt) break; - - ang = interiorangle(pd, startpt, endpt, NULL); - if (ang > angmax) { - angmax = ang; - *refpt = pd; - reftet = *searchtet; - } + point2tetorg(startpt, *searchtet); + dir = finddirection(searchtet, endpt); - // Find a face intersecting the segment. - if (dir == ACROSSFACE) { - // One of the three oppo faces in 'searchtet' intersects the segment. - 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; - } - } - } else if (dir == ACROSSEDGE) { - // 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; + if (dir == ACROSSVERT) { + pd = dest(*searchtet); + if (pd == endpt) { + if (issubseg(*searchtet)) { + report_selfint_edge(startpt, endpt, sedge, searchtet, dir); + } + return SHAREEDGE; } else { - dir = DISJOINT; - pos = 0; + // A point is on the path. + report_selfint_edge(startpt, endpt, sedge, searchtet, dir); + return ACROSSVERT; } - } - if (dir == DISJOINT) { - // No intersection. Rotate to the next tet at the edge. - dir = ACROSSEDGE; - fnextself(*searchtet); - continue; - } } - if (dir == ACROSSVERT) { - // This segment passing a vertex. Choose it and return. - for (i = 0; i < pos; i++) { - enextself(neightet); - } - eprev(neightet, *searchtet); - // dest(*searchtet) lies on the segment. - report_selfint_edge(startpt, endpt, sedge, searchtet, dir); - 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. - fsym(neightet, *searchtet); + // 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)) { - report_selfint_edge(startpt, endpt, sedge, searchtet, dir); - } + // Check whether two segments are intersecting. + if (issubseg(*searchtet)) { + report_selfint_edge(startpt, endpt, sedge, searchtet, dir); + } } else if (dir == ACROSSFACE) { - if (checksubfaceflag) { - // Check whether a segment and a subface are intersecting. - if (issubface(*searchtet)) { - report_selfint_edge(startpt, endpt, sedge, searchtet, dir); + if (checksubfaceflag) { + // Check whether a segment and a subface are intersecting. + if (issubface(*searchtet)) { + report_selfint_edge(startpt, endpt, sedge, searchtet, dir); + } } - } } else { - terminatetetgen(this, 2); + terminatetetgen(this, 2); } - } // while (1) - - // 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; - } + if (refpt == NULL) { + // Do not need a reference point. Return. + return dir; + } + triface neightet, reftet; + point pa, pb, pc; + REAL angmax, ang; + int types[2], poss[4]; + int pos = 0, i, j; - *searchtet = reftet; - return dir; -} + pa = org(*searchtet); + angmax = interiorangle(pa, startpt, endpt, NULL); + *refpt = pa; + pb = dest(*searchtet); + ang = interiorangle(pb, startpt, endpt, NULL); + if (ang > angmax) { + angmax = ang; + *refpt = pb; + } + pc = apex(*searchtet); + ang = interiorangle(pc, startpt, endpt, NULL); + if (ang > angmax) { + angmax = ang; + *refpt = pc; + } + reftet = *searchtet; // Save the tet containing the refpt. + + // Search intersecting faces along the segment. + while (1) { + + pd = oppo(*searchtet); + + // Stop if we meet 'endpt'. + if (pd == endpt) break; + + ang = interiorangle(pd, startpt, endpt, NULL); + if (ang > angmax) { + angmax = ang; + *refpt = pd; + reftet = *searchtet; + } + + // Find a face intersecting the segment. + if (dir == ACROSSFACE) { + // One of the three oppo faces in 'searchtet' intersects the segment. + 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; + } + } + } else if (dir == ACROSSEDGE) { + // 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; + } else { + dir = DISJOINT; + pos = 0; + } + } + if (dir == DISJOINT) { + // No intersection. Rotate to the next tet at the edge. + dir = ACROSSEDGE; + fnextself(*searchtet); + continue; + } + } + + if (dir == ACROSSVERT) { + // This segment passing a vertex. Choose it and return. + for (i = 0; i < pos; i++) { + enextself(neightet); + } + eprev(neightet, *searchtet); + // dest(*searchtet) lies on the segment. + report_selfint_edge(startpt, endpt, sedge, searchtet, dir); + 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. + fsym(neightet, *searchtet); + + if (dir == ACROSSEDGE) { + // Check whether two segments are intersecting. + if (issubseg(*searchtet)) { + report_selfint_edge(startpt, endpt, sedge, searchtet, dir); + } + } else if (dir == ACROSSFACE) { + if (checksubfaceflag) { + // Check whether a segment and a subface are intersecting. + if (issubface(*searchtet)) { + report_selfint_edge(startpt, endpt, sedge, searchtet, dir); + } + } + } else { + terminatetetgen(this, 2); + } + + } // while (1) + + // 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; +} /////////////////////////////////////////////////////////////////////////////// // // @@ -15807,75 +15636,71 @@ enum tetgenmesh::interresult tetgenmesh::scoutsegment(point startpt,point endpt, // // /////////////////////////////////////////////////////////////////////////////// -int tetgenmesh::getsteinerptonsegment(face* seg, point refpt, point steinpt) -{ - point ei = sorg(*seg); - point ej = sdest(*seg); - int adjflag = 0, i; - - 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]); +int tetgenmesh::getsteinerptonsegment(face* seg, point refpt, point steinpt) { + point ei = sorg(*seg); + point ej = sdest(*seg); + int adjflag = 0, i; + + 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]); + } + 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]); + } + adjflag = 1; + } else { + // Cut the segment by the projection point of refpt. + projpt2edge(refpt, ei, ej, steinpt); + } + } else { + // Cut the segment by the projection point of refpt. + projpt2edge(refpt, ei, ej, steinpt); } - adjflag = 1; - } else if ((far_pi == far_ej) || (far_pj == far_ej)) { - L = distance(far_ei, far_ej); - L1 = distance(far_ej, refpt); + + // Make sure that steinpt is not too close to ei and ej. + L = distance(ei, ej); + L1 = distance(steinpt, ei); t = L1 / L; - for (i = 0; i < 3; i++) { - steinpt[i] = far_ej[i] + t * (far_ei[i] - far_ej[i]); + if ((t < 0.2) || (t > 0.8)) { + // Split the point at the middle. + for (i = 0; i < 3; i++) { + steinpt[i] = ei[i] + 0.5 * (ej[i] - ei[i]); + } } - adjflag = 1; - } else { - // Cut the segment by the projection point of refpt. - projpt2edge(refpt, ei, ej, steinpt); - } } else { - // 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++) { - steinpt[i] = ei[i] + 0.5 * (ej[i] - ei[i]); - } - } - } else { - // Split the point at the middle. - for (i = 0; i < 3; i++) { - steinpt[i] = ei[i] + 0.5 * (ej[i] - ei[i]); + // Split the point at the middle. + for (i = 0; i < 3; i++) { + steinpt[i] = ei[i] + 0.5 * (ej[i] - ei[i]); + } } - } - - return adjflag; + return adjflag; } - - /////////////////////////////////////////////////////////////////////////////// // // // delaunizesegments() Recover segments in a DT. // @@ -15890,107 +15715,104 @@ int tetgenmesh::getsteinerptonsegment(face* seg, point refpt, point steinpt) // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::delaunizesegments() -{ - triface searchtet, spintet; - face searchsh; - face sseg, *psseg; - point refpt, newpt; - enum interresult dir; - insertvertexflags ivf; - int t1ver; - +void tetgenmesh::delaunizesegments() { + triface searchtet, spintet; + face searchsh; + face sseg, *psseg; + point refpt, newpt; + enum interresult dir; + insertvertexflags ivf; + int t1ver; - ivf.bowywat = 1; // Use Bowyer-Watson insertion. - ivf.sloc = (int) ONEDGE; // on 'sseg'. - ivf.sbowywat = 1; // Use Bowyer-Watson insertion. - ivf.assignmeshsize = b->metric; - ivf.smlenflag = useinsertradius; // Return the closet mesh vertex. + ivf.bowywat = 1; // Use Bowyer-Watson insertion. + ivf.sloc = (int)ONEDGE; // on 'sseg'. + ivf.sbowywat = 1; // Use Bowyer-Watson insertion. + ivf.assignmeshsize = b->metric; + ivf.smlenflag = useinsertradius; // Return the closet mesh vertex. - // Loop until 'subsegstack' is empty. - while (subsegstack->objects > 0l) { - // seglist is used as a stack. - subsegstack->objects--; - psseg = (face *) fastlookup(subsegstack, subsegstack->objects); - sseg = *psseg; + // Loop until 'subsegstack' is empty. + while (subsegstack->objects > 0l) { + // seglist is used as a stack. + subsegstack->objects--; + psseg = (face*)fastlookup(subsegstack, subsegstack->objects); + sseg = *psseg; - // Check if this segment has been recovered. - sstpivot1(sseg, searchtet); - if (searchtet.tet != NULL) { - continue; // Not a missing segment. - } + // Check if this segment has been recovered. + sstpivot1(sseg, searchtet); + if (searchtet.tet != NULL) { + continue; // Not a missing segment. + } - // Search the segment. - dir = scoutsegment(sorg(sseg), sdest(sseg), &sseg,&searchtet,&refpt,NULL); + // Search the segment. + dir = scoutsegment(sorg(sseg), sdest(sseg), &sseg, &searchtet, &refpt, NULL); - if (dir == SHAREEDGE) { - // Found this segment, 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 ((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--; - if (useinsertradius) { - save_segmentpoint_insradius(newpt, ivf.parentpt, ivf.smlen); - } + if (dir == SHAREEDGE) { + // Found this segment, 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 (ivf.iloc == (int) NEARVERTEX) { - // The new point (in the segment) is very close to an existing - // vertex -- a small feature is detected. - point nearpt = org(searchtet); - if (pointtype(nearpt) == FREESEGVERTEX) { - face parentseg; - sdecode(point2sh(nearpt), parentseg); - point p1 = farsorg(sseg); - point p2 = farsdest(sseg); - point p3 = farsorg(parentseg); - point p4 = farsdest(parentseg); - printf("Two segments are very close to each other.\n"); - printf(" Segment 1: [%d, %d] #%d\n", pointmark(p1), - pointmark(p2), shellmark(sseg)); - printf(" Segment 2: [%d, %d] #%d\n", pointmark(p3), - pointmark(p4), shellmark(parentseg)); - terminatetetgen(this, 4); + 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--; + if (useinsertradius) { + save_segmentpoint_insradius(newpt, ivf.parentpt, ivf.smlen); + } + } else { + if (ivf.iloc == (int)NEARVERTEX) { + // The new point (in the segment) is very close to an existing + // vertex -- a small feature is detected. + point nearpt = org(searchtet); + if (pointtype(nearpt) == FREESEGVERTEX) { + face parentseg; + sdecode(point2sh(nearpt), parentseg); + point p1 = farsorg(sseg); + point p2 = farsdest(sseg); + point p3 = farsorg(parentseg); + point p4 = farsdest(parentseg); + printf("Two segments are very close to each other.\n"); + printf(" Segment 1: [%d, %d] #%d\n", pointmark(p1), pointmark(p2), + shellmark(sseg)); + printf(" Segment 2: [%d, %d] #%d\n", pointmark(p3), pointmark(p4), + shellmark(parentseg)); + terminatetetgen(this, 4); + } else { + terminatetetgen(this, 2); + } + } else if (ivf.iloc == (int)ONVERTEX) { + // The new point (in the segment) is coincident with an existing + // vertex -- a self-intersection is detected. + eprevself(searchtet); + report_selfint_edge(sorg(sseg), sdest(sseg), &sseg, &searchtet, ACROSSVERT); + } else { + // An unknown case. Report a bug. + terminatetetgen(this, 2); + } + } } else { - terminatetetgen(this, 2); + // An unknown case. Report a bug. + terminatetetgen(this, 2); } - } else if (ivf.iloc == (int) ONVERTEX) { - // The new point (in the segment) is coincident with an existing - // vertex -- a self-intersection is detected. - eprevself(searchtet); - report_selfint_edge(sorg(sseg), sdest(sseg), &sseg, &searchtet, - ACROSSVERT); - } else { - // An unknown case. Report a bug. - terminatetetgen(this, 2); - } } - } else { - // An unknown case. Report a bug. - terminatetetgen(this, 2); - } - } - } // while + } // while } /////////////////////////////////////////////////////////////////////////////// @@ -16006,51 +15828,50 @@ void tetgenmesh::delaunizesegments() // // /////////////////////////////////////////////////////////////////////////////// -int tetgenmesh::scoutsubface(face* searchsh, triface* searchtet, int shflag) -{ - point pa = sorg(*searchsh); - point pb = sdest(*searchsh); - - // Get a tet whose origin is a. - point2tetorg(pa, *searchtet); - // Search the edge [a,b]. - enum interresult dir = finddirection(searchtet, pb); - if (dir == ACROSSVERT) { - // Check validity of a PLC. - if (dest(*searchtet) != pb) { - if (shflag) { - // A vertex lies on the search edge. - report_selfint_edge(pa, pb, searchsh, searchtet, dir); - } else { - terminatetetgen(this, 2); - } - } - int t1ver; - // The edge exists. Check if the face exists. - point pc = sapex(*searchsh); - // Searchtet holds edge [a,b]. Search a face with apex c. - triface 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 1; - } else { - terminatetetgen(this, 2); +int tetgenmesh::scoutsubface(face* searchsh, triface* searchtet, int shflag) { + point pa = sorg(*searchsh); + point pb = sdest(*searchsh); + + // Get a tet whose origin is a. + point2tetorg(pa, *searchtet); + // Search the edge [a,b]. + enum interresult dir = finddirection(searchtet, pb); + if (dir == ACROSSVERT) { + // Check validity of a PLC. + if (dest(*searchtet) != pb) { + if (shflag) { + // A vertex lies on the search edge. + report_selfint_edge(pa, pb, searchsh, searchtet, dir); + } else { + terminatetetgen(this, 2); + } + } + int t1ver; + // The edge exists. Check if the face exists. + point pc = sapex(*searchsh); + // Searchtet holds edge [a,b]. Search a face with apex c. + triface 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 1; + } else { + terminatetetgen(this, 2); + } + } + fnextself(spintet); + if (spintet.tet == searchtet->tet) break; } - } - fnextself(spintet); - if (spintet.tet == searchtet->tet) break; } - } - return 0; + return 0; } /////////////////////////////////////////////////////////////////////////////// @@ -16072,98 +15893,96 @@ int tetgenmesh::scoutsubface(face* searchsh, triface* searchtet, int shflag) // ing edges can be found by this way. // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::formregion(face* missh, arraypool* missingshs, - arraypool* missingshbds, arraypool* missingshverts) -{ - triface searchtet, spintet; - face neighsh, *parysh; - face neighseg, fakeseg; - point pa, pb, *parypt; - enum interresult dir; - int t1ver; - int i, j; - - smarktest(*missh); - missingshs->newindex((void **) &parysh); - *parysh = *missh; - - // 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) { - // Report a PLC problem. - report_selfint_edge(pa, pb, missh, &searchtet, dir); - } - } - // Collect the vertices of R. - if (!pmarktested(pa)) { - pmarktest(pa); - missingshverts->newindex((void **) &parypt); - *parypt = pa; - } - senextself(*missh); - } // j - } // i +void tetgenmesh::formregion(face* missh, arraypool* missingshs, arraypool* missingshbds, + arraypool* missingshverts) { + triface searchtet, spintet; + face neighsh, *parysh; + face neighseg, fakeseg; + point pa, pb, *parypt; + enum interresult dir; + int t1ver; + int i, j; - // Get the boundary edges of R. - for (i = 0; i < missingshs->objects; i++) { - missh = (face *) fastlookup(missingshs, i); - for (j = 0; j < 3; j++) { - spivot(*missh, neighsh); - if ((neighsh.sh == NULL) || !smarktested(neighsh)) { - // A boundary edge of R. - // Let the segment point to the adjacent tet. - point2tetorg(sorg(*missh), searchtet); - finddirection(&searchtet, sdest(*missh)); - missingshbds->newindex((void **) &parysh); - *parysh = *missh; - // Check if this edge is a segment. - sspivot(*missh, neighseg); - if (neighseg.sh == NULL) { - // Temporarily create a segment at this edge. - makeshellface(subsegs, &fakeseg); - setsorg(fakeseg, sorg(*missh)); - setsdest(fakeseg, sdest(*missh)); - sinfect(fakeseg); // Mark it as faked. - // Connect it to all tets at this edge. - spintet = searchtet; - while (1) { - tssbond1(spintet, fakeseg); - fnextself(spintet); - if (spintet.tet == searchtet.tet) break; - } - neighseg = fakeseg; - } - // Let the segment and the boundary edge point to each other. - ssbond(*missh, neighseg); - sstbond1(neighseg, searchtet); - } - senextself(*missh); - } // j - } // i + smarktest(*missh); + missingshs->newindex((void**)&parysh); + *parysh = *missh; + + // 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) { + // Report a PLC problem. + report_selfint_edge(pa, pb, missh, &searchtet, dir); + } + } + // Collect the vertices of R. + if (!pmarktested(pa)) { + pmarktest(pa); + missingshverts->newindex((void**)&parypt); + *parypt = pa; + } + senextself(*missh); + } // j + } // i + // Get the boundary edges of R. + for (i = 0; i < missingshs->objects; i++) { + missh = (face*)fastlookup(missingshs, i); + for (j = 0; j < 3; j++) { + spivot(*missh, neighsh); + if ((neighsh.sh == NULL) || !smarktested(neighsh)) { + // A boundary edge of R. + // Let the segment point to the adjacent tet. + point2tetorg(sorg(*missh), searchtet); + finddirection(&searchtet, sdest(*missh)); + missingshbds->newindex((void**)&parysh); + *parysh = *missh; + // Check if this edge is a segment. + sspivot(*missh, neighseg); + if (neighseg.sh == NULL) { + // Temporarily create a segment at this edge. + makeshellface(subsegs, &fakeseg); + setsorg(fakeseg, sorg(*missh)); + setsdest(fakeseg, sdest(*missh)); + sinfect(fakeseg); // Mark it as faked. + // Connect it to all tets at this edge. + spintet = searchtet; + while (1) { + tssbond1(spintet, fakeseg); + fnextself(spintet); + if (spintet.tet == searchtet.tet) break; + } + neighseg = fakeseg; + } + // Let the segment and the boundary edge point to each other. + ssbond(*missh, neighseg); + sstbond1(neighseg, searchtet); + } + senextself(*missh); + } // j + } // i - // Unmarktest collected missing subfaces. - for (i = 0; i < missingshs->objects; i++) { - parysh = (face *) fastlookup(missingshs, i); - sunmarktest(*parysh); - } + // Unmarktest collected missing subfaces. + for (i = 0; i < missingshs->objects; i++) { + parysh = (face*)fastlookup(missingshs, i); + sunmarktest(*parysh); + } } /////////////////////////////////////////////////////////////////////////////// @@ -16178,200 +15997,198 @@ void tetgenmesh::formregion(face* missh, arraypool* missingshs, // // /////////////////////////////////////////////////////////////////////////////// -int tetgenmesh::scoutcrossedge(triface& crosstet, arraypool* missingshbds, - arraypool* missingshs) -{ - triface searchtet, spintet, neightet; - face oldsh, searchsh, *parysh; - face neighseg; - point pa, pb, pc, pd, pe; - REAL ori; - int types[2], poss[4]; - int searchflag, interflag; - int t1ver; - int i, j; - - searchflag = 0; - - // Search the first new subface to fill the region. - for (i = 0; i < missingshbds->objects && !searchflag; i++) { - parysh = (face *) fastlookup(missingshbds, i); - sspivot(*parysh, neighseg); - sstpivot1(neighseg, searchtet); - if (org(searchtet) != sorg(*parysh)) { - esymself(searchtet); - } - spintet = searchtet; - while (1) { - if (pmarktested(apex(spintet))) { - // A possible interior face. - neightet = spintet; - oldsh = *parysh; - // Try to recover an interior edge. - for (j = 0; j < 2; j++) { - enextself(neightet); - if (!issubseg(neightet)) { - if (j == 0) { - senext(oldsh, searchsh); - } else { - senext2(oldsh, searchsh); - sesymself(searchsh); - esymself(neightet); - } - // Calculate a lifted point. - pa = sorg(searchsh); - pb = sdest(searchsh); - pc = sapex(searchsh); - pd = dest(neightet); - calculateabovepoint4(pa, pb, pc, pd); - // The lifted point must lie above 'searchsh'. - ori = orient3d(pa, pb, pc, dummypoint); - if (ori > 0) { - sesymself(searchsh); - senextself(searchsh); - } else if (ori == 0) { - terminatetetgen(this, 2); - } - if (sscoutsegment(&searchsh,dest(neightet),0,0,1)==SHAREEDGE) { - // Insert a temp segment to protect the recovered edge. - face tmpseg; - makeshellface(subsegs, &tmpseg); - ssbond(searchsh, tmpseg); - spivotself(searchsh); - ssbond(searchsh, tmpseg); - // Recover locally Delaunay edges. - lawsonflip(); - // Delete the tmp segment. - spivot(tmpseg, searchsh); - ssdissolve(searchsh); - spivotself(searchsh); - ssdissolve(searchsh); - shellfacedealloc(subsegs, tmpseg.sh); - searchflag = 1; - } else { - // Undo the performed flips. - if (flipstack != NULL) { - lawsonflip(); - } - } - break; - } // if (!issubseg(neightet)) - } // j - if (searchflag) break; - } // if (pmarktested(apex(spintet))) - fnextself(spintet); - if (spintet.tet == searchtet.tet) break; - } - } // i +int tetgenmesh::scoutcrossedge(triface& crosstet, arraypool* missingshbds, arraypool* missingshs) { + triface searchtet, spintet, neightet; + face oldsh, searchsh, *parysh; + face neighseg; + point pa, pb, pc, pd, pe; + REAL ori; + int types[2], poss[4]; + int searchflag, interflag; + int t1ver; + int i, j; - if (searchflag) { - // Remove faked segments. - face checkseg; - // Remark: We should not use the array 'missingshbds', since the flips may - // change the subfaces. We search them from the subfaces in R. - for (i = 0; i < missingshs->objects; i++) { - parysh = (face *) fastlookup(missingshs, i); - oldsh = *parysh; - for (j = 0; j < 3; j++) { - if (isshsubseg(oldsh)) { - sspivot(oldsh, checkseg); - if (sinfected(checkseg)) { - // It's a faked segment. Delete it. - sstpivot1(checkseg, searchtet); - spintet = searchtet; - while (1) { - tssdissolve1(spintet); - fnextself(spintet); - if (spintet.tet == searchtet.tet) break; - } - shellfacedealloc(subsegs, checkseg.sh); - ssdissolve(oldsh); - } + searchflag = 0; + + // Search the first new subface to fill the region. + for (i = 0; i < missingshbds->objects && !searchflag; i++) { + parysh = (face*)fastlookup(missingshbds, i); + sspivot(*parysh, neighseg); + sstpivot1(neighseg, searchtet); + if (org(searchtet) != sorg(*parysh)) { + esymself(searchtet); } - senextself(oldsh); - } // j - } + spintet = searchtet; + while (1) { + if (pmarktested(apex(spintet))) { + // A possible interior face. + neightet = spintet; + oldsh = *parysh; + // Try to recover an interior edge. + for (j = 0; j < 2; j++) { + enextself(neightet); + if (!issubseg(neightet)) { + if (j == 0) { + senext(oldsh, searchsh); + } else { + senext2(oldsh, searchsh); + sesymself(searchsh); + esymself(neightet); + } + // Calculate a lifted point. + pa = sorg(searchsh); + pb = sdest(searchsh); + pc = sapex(searchsh); + pd = dest(neightet); + calculateabovepoint4(pa, pb, pc, pd); + // The lifted point must lie above 'searchsh'. + ori = orient3d(pa, pb, pc, dummypoint); + if (ori > 0) { + sesymself(searchsh); + senextself(searchsh); + } else if (ori == 0) { + terminatetetgen(this, 2); + } + if (sscoutsegment(&searchsh, dest(neightet), 0, 0, 1) == SHAREEDGE) { + // Insert a temp segment to protect the recovered edge. + face tmpseg; + makeshellface(subsegs, &tmpseg); + ssbond(searchsh, tmpseg); + spivotself(searchsh); + ssbond(searchsh, tmpseg); + // Recover locally Delaunay edges. + lawsonflip(); + // Delete the tmp segment. + spivot(tmpseg, searchsh); + ssdissolve(searchsh); + spivotself(searchsh); + ssdissolve(searchsh); + shellfacedealloc(subsegs, tmpseg.sh); + searchflag = 1; + } else { + // Undo the performed flips. + if (flipstack != NULL) { + lawsonflip(); + } + } + break; + } // if (!issubseg(neightet)) + } // j + if (searchflag) break; + } // if (pmarktested(apex(spintet))) + fnextself(spintet); + if (spintet.tet == searchtet.tet) break; + } + } // i - fillregioncount++; + if (searchflag) { + // Remove faked segments. + face checkseg; + // Remark: We should not use the array 'missingshbds', since the flips may + // change the subfaces. We search them from the subfaces in R. + for (i = 0; i < missingshs->objects; i++) { + parysh = (face*)fastlookup(missingshs, i); + oldsh = *parysh; + for (j = 0; j < 3; j++) { + if (isshsubseg(oldsh)) { + sspivot(oldsh, checkseg); + if (sinfected(checkseg)) { + // It's a faked segment. Delete it. + sstpivot1(checkseg, searchtet); + spintet = searchtet; + while (1) { + tssdissolve1(spintet); + fnextself(spintet); + if (spintet.tet == searchtet.tet) break; + } + shellfacedealloc(subsegs, checkseg.sh); + ssdissolve(oldsh); + } + } + senextself(oldsh); + } // j + } - return 0; - } // if (i < missingshbds->objects) + fillregioncount++; - searchflag = -1; + return 0; + } // if (i < missingshbds->objects) - for (j = 0; j < missingshbds->objects && (searchflag == -1); 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) { - 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. - if ((types[0] == (int) ACROSSFACE) || - (types[0] == (int) ACROSSEDGE)) { - // Go to the crossing edge [d,e,#,#]. - edestoppo(spintet, crosstet); // // [d,e,#,#]. - if (issubseg(crosstet)) { - // It is a segment. Report a PLC problem. - report_selfint_face(pa, pb, pc, parysh, &crosstet, - interflag, types, poss); - } else { - triface chkface = crosstet; - while (1) { - if (issubface(chkface)) break; - fsymself(chkface); - if (chkface.tet == crosstet.tet) break; - } - if (issubface(chkface)) { - // Two subfaces are intersecting. - report_selfint_face(pa, pb, pc, parysh, &chkface, - interflag, types, poss); - } - } - // Adjust the edge such that d lies below [a,b,c]. - ori = orient3d(pa, pb, pc, pd); - if (ori < 0) { - esymself(crosstet); - } - searchflag = 1; - } else { - // An improper intersection type, ACROSSVERT, TOUCHFACE, - // TOUCHEDGE, SHAREVERT, ... - // Maybe it is due to a PLC problem. - report_selfint_face(pa, pb, pc, parysh, &crosstet, - interflag, types, poss); - } - } - break; - } // if (interflag > 0) - } - } - } - // 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 + searchflag = -1; + + for (j = 0; j < missingshbds->objects && (searchflag == -1); 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) { + 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. + if ((types[0] == (int)ACROSSFACE) || + (types[0] == (int)ACROSSEDGE)) { + // Go to the crossing edge [d,e,#,#]. + edestoppo(spintet, crosstet); // // [d,e,#,#]. + if (issubseg(crosstet)) { + // It is a segment. Report a PLC problem. + report_selfint_face(pa, pb, pc, parysh, &crosstet, + interflag, types, poss); + } else { + triface chkface = crosstet; + while (1) { + if (issubface(chkface)) break; + fsymself(chkface); + if (chkface.tet == crosstet.tet) break; + } + if (issubface(chkface)) { + // Two subfaces are intersecting. + report_selfint_face(pa, pb, pc, parysh, &chkface, + interflag, types, poss); + } + } + // Adjust the edge such that d lies below [a,b,c]. + ori = orient3d(pa, pb, pc, pd); + if (ori < 0) { + esymself(crosstet); + } + searchflag = 1; + } else { + // An improper intersection type, ACROSSVERT, TOUCHFACE, + // TOUCHEDGE, SHAREVERT, ... + // Maybe it is due to a PLC problem. + report_selfint_face(pa, pb, pc, parysh, &crosstet, interflag, + types, poss); + } + } + break; + } // if (interflag > 0) + } + } + } + // 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 - return searchflag; + return searchflag; } /////////////////////////////////////////////////////////////////////////////// @@ -16397,287 +16214,284 @@ int tetgenmesh::scoutcrossedge(triface& crosstet, arraypool* missingshbds, // // /////////////////////////////////////////////////////////////////////////////// -bool tetgenmesh::formcavity(triface* searchtet, arraypool* missingshs, - arraypool* crosstets, arraypool* topfaces, - arraypool* botfaces, arraypool* toppoints, - arraypool* botpoints) -{ - arraypool *crossedges; - triface spintet, neightet, chkface, *parytet; - face *parysh = NULL; - point pa, pd, pe, *parypt; - bool testflag, invalidflag; - int intflag, types[2], poss[4]; - int t1ver; - int i, j, k; - - // 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 = *searchtet; - - invalidflag = 0; - // Collect all crossing tets. Each cross tet is saved in the standard - // 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 && !invalidflag; 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; - } - // Go to the next crossing tet. - fnextself(spintet); - if (spintet.tet == searchtet->tet) break; - } // while (1) +bool tetgenmesh::formcavity(triface* searchtet, arraypool* missingshs, arraypool* crosstets, + arraypool* topfaces, arraypool* botfaces, arraypool* toppoints, + arraypool* botpoints) { + arraypool* crossedges; + triface spintet, neightet, chkface, *parytet; + face* parysh = NULL; + point pa, pd, pe, *parypt; + bool testflag, invalidflag; + int intflag, types[2], poss[4]; + int t1ver; + int i, j, k; - // 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.,to check if one - // of the tetrahedron at this edge has been marked. - testflag = true; - for (j = 0; j < 2 && testflag; j++) { - if (j == 0) { - enext(spintet, neightet); - } else { - eprev(spintet, neightet); + // 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 = *searchtet; + + invalidflag = 0; + // Collect all crossing tets. Each cross tet is saved in the standard + // 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 && !invalidflag; 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; } - 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); - intflag = tri_edge_test(sorg(*parysh), sdest(*parysh), - sapex(*parysh), pe, pa, NULL, 1, types, poss); - if (intflag > 0) { - // Found intersection. 'a' lies below R. - if (intflag == 2) { - enext(spintet, neightet); - if ((types[0] == (int) ACROSSFACE) || - (types[0] == (int) ACROSSEDGE)) { - // Only this case is valid. - } else { - // A non-valid intersection. Maybe a PLC problem. - invalidflag = 1; - } - } else { - // Coplanar intersection. Maybe a PLC problem. - invalidflag = 1; - } - break; - } - intflag = tri_edge_test(sorg(*parysh), sdest(*parysh), - sapex(*parysh), pa, pd, NULL, 1, types, poss); - if (intflag > 0) { - // Found intersection. 'a' lies above R. - if (intflag == 2) { - eprev(spintet, neightet); - if ((types[0] == (int) ACROSSFACE) || - (types[0] == (int) ACROSSEDGE)) { - // Only this case is valid. - } else { - // A non-valid intersection. Maybe a PLC problem. - invalidflag = 1; - } - } else { - // Coplanar intersection. Maybe a PLC problem. - invalidflag = 1; - } - break; - } - } // k - if (k < missingshs->objects) { - // Found a pair of triangle - edge intersection. - if (invalidflag) { - break; // the while (1) loop - } - // Adjust the edge direction, so that its origin lies below R, - // and its destination lies above R. - esymself(neightet); - // This edge may be a segment. - if (issubseg(neightet)) { - report_selfint_face(sorg(*parysh), sdest(*parysh), - sapex(*parysh),parysh,&neightet,intflag,types,poss); - } - // Check if it is an edge of a subface. - chkface = neightet; - while (1) { - if (issubface(chkface)) break; - fsymself(chkface); - if (chkface.tet == neightet.tet) break; - } - if (issubface(chkface)) { - // Two subfaces are intersecting. - report_selfint_face(sorg(*parysh), sdest(*parysh), - sapex(*parysh),parysh,&chkface,intflag,types,poss); - } - - // Mark this edge to avoid testing it again. - markedge(neightet); - crossedges->newindex((void **) &parytet); - *parytet = neightet; - } else { - // No intersection is found. It may be a PLC problem. - invalidflag = 1; - break; // the while (1) loop - } // if (k == missingshs->objects) - } // if (testflag) - } - } // if (pa != dummypoint) - // Go to the next crossing tet. - fnextself(spintet); - if (spintet.tet == searchtet->tet) break; - } // while (1) - } // i + // 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.,to check if one + // of the tetrahedron at this edge has been 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); + intflag = tri_edge_test(sorg(*parysh), sdest(*parysh), sapex(*parysh), + pe, pa, NULL, 1, types, poss); + if (intflag > 0) { + // Found intersection. 'a' lies below R. + if (intflag == 2) { + enext(spintet, neightet); + if ((types[0] == (int)ACROSSFACE) || + (types[0] == (int)ACROSSEDGE)) { + // Only this case is valid. + } else { + // A non-valid intersection. Maybe a PLC problem. + invalidflag = 1; + } + } else { + // Coplanar intersection. Maybe a PLC problem. + invalidflag = 1; + } + break; + } + intflag = tri_edge_test(sorg(*parysh), sdest(*parysh), sapex(*parysh), + pa, pd, NULL, 1, types, poss); + if (intflag > 0) { + // Found intersection. 'a' lies above R. + if (intflag == 2) { + eprev(spintet, neightet); + if ((types[0] == (int)ACROSSFACE) || + (types[0] == (int)ACROSSEDGE)) { + // Only this case is valid. + } else { + // A non-valid intersection. Maybe a PLC problem. + invalidflag = 1; + } + } else { + // Coplanar intersection. Maybe a PLC problem. + invalidflag = 1; + } + break; + } + } // k + if (k < missingshs->objects) { + // Found a pair of triangle - edge intersection. + if (invalidflag) { + break; // the while (1) loop + } + // Adjust the edge direction, so that its origin lies below R, + // and its destination lies above R. + esymself(neightet); + // This edge may be a segment. + if (issubseg(neightet)) { + report_selfint_face(sorg(*parysh), sdest(*parysh), sapex(*parysh), + parysh, &neightet, intflag, types, poss); + } + // Check if it is an edge of a subface. + chkface = neightet; + while (1) { + if (issubface(chkface)) break; + fsymself(chkface); + if (chkface.tet == neightet.tet) break; + } + if (issubface(chkface)) { + // Two subfaces are intersecting. + report_selfint_face(sorg(*parysh), sdest(*parysh), sapex(*parysh), + parysh, &chkface, intflag, types, poss); + } + + // Mark this edge to avoid testing it again. + markedge(neightet); + crossedges->newindex((void**)&parytet); + *parytet = neightet; + } else { + // No intersection is found. It may be a PLC problem. + invalidflag = 1; + break; // the while (1) loop + } // if (k == missingshs->objects) + } // if (testflag) + } + } // if (pa != dummypoint) + // Go to the next crossing tet. + fnextself(spintet); + if (spintet.tet == searchtet->tet) break; + } // while (1) + } // i + + // Unmark all marked edges. + for (i = 0; i < crossedges->objects; i++) { + searchtet = (triface*)fastlookup(crossedges, i); + unmarkedge(*searchtet); + } + crossedges->restart(); + + if (invalidflag) { + // Unmark all collected tets. + for (i = 0; i < crosstets->objects; i++) { + searchtet = (triface*)fastlookup(crosstets, i); + uninfect(*searchtet); + } + // Unmark all collected vertices. + for (i = 0; i < botpoints->objects; i++) { + parypt = (point*)fastlookup(botpoints, i); + puninfect(*parypt); + } + for (i = 0; i < toppoints->objects; i++) { + parypt = (point*)fastlookup(toppoints, i); + puninfect(*parypt); + } + crosstets->restart(); + botpoints->restart(); + toppoints->restart(); - // Unmark all marked edges. - for (i = 0; i < crossedges->objects; i++) { - searchtet = (triface *) fastlookup(crossedges, i); - unmarkedge(*searchtet); - } - crossedges->restart(); + // Randomly split an interior edge of R. + i = randomnation(missingshs->objects - 1); + recentsh = *(face*)fastlookup(missingshs, i); + return false; + } + if (b->verbose > 2) { + printf(" Formed cavity: %ld (%ld) cross tets (edges).\n", crosstets->objects, + crossedges->objects); + } + + // Collect the top and bottom faces and the middle vertices. Since all top + // 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()". - if (invalidflag) { - // Unmark all collected tets. for (i = 0; i < crosstets->objects; i++) { - searchtet = (triface *) fastlookup(crosstets, i); - uninfect(*searchtet); + 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); + *parytet = neightet; + } + edestoppo(*searchtet, spintet); + fsym(spintet, neightet); // neightet is [b,a,d,#] + if (!infected(neightet)) { + // A bottom face. + botfaces->newindex((void**)&parytet); + *parytet = neightet; + } + // Add middle vertices if there are (skip dummypoint). + 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 + + // Uninfect all collected top, bottom, and middle vertices. + for (i = 0; i < toppoints->objects; i++) { + parypt = (point*)fastlookup(toppoints, i); + puninfect(*parypt); } - // Unmark all collected vertices. for (i = 0; i < botpoints->objects; i++) { - parypt = (point *) fastlookup(botpoints, i); - puninfect(*parypt); - } - for (i = 0; i < toppoints->objects; i++) { - parypt = (point *) fastlookup(toppoints, i); - puninfect(*parypt); + parypt = (point*)fastlookup(botpoints, 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; - } - - if (b->verbose > 2) { - printf(" Formed cavity: %ld (%ld) cross tets (edges).\n", - crosstets->objects, crossedges->objects); - } - - // Collect the top and bottom faces and the middle vertices. Since all top - // 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); - *parytet = neightet; - } - edestoppo(*searchtet, spintet); - fsym(spintet, neightet); // neightet is [b,a,d,#] - if (!infected(neightet)) { - // A bottom face. - botfaces->newindex((void **) &parytet); - *parytet = neightet; - } - // Add middle vertices if there are (skip dummypoint). - 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 - - // 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 < botpoints->objects; i++) { - parypt = (point *) fastlookup(botpoints, i); - puninfect(*parypt); - } - cavitycount++; - - return true; -} + cavitycount++; + + return true; +} /////////////////////////////////////////////////////////////////////////////// // // @@ -16696,219 +16510,216 @@ bool tetgenmesh::formcavity(triface* searchtet, arraypool* missingshs, // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::delaunizecavity(arraypool *cavpoints, arraypool *cavfaces, - arraypool *cavshells, arraypool *newtets, - arraypool *crosstets, arraypool *misfaces) -{ - triface searchtet, neightet, *parytet, *parytet1; - face tmpsh, *parysh; - point pa, pb, pc, pd, pt[3], *parypt; - insertvertexflags ivf; - REAL ori; - long baknum, bakhullsize; - int bakchecksubsegflag, bakchecksubfaceflag; - int t1ver; - int i, j; - - 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). - 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 < cavfaces->objects; i++) { - parytet = (triface *) fastlookup(cavfaces, i); - pt[0] = org(*parytet); - pt[1] = dest(*parytet); - pt[2] = apex(*parytet); - for (j = 0; j < 3; j++) { - if (pt[j] != dummypoint) { // Do not include a hull point. - ori = orient3d(pa, pb, pc, pt[j]); - if (ori != 0) { - pd = pt[j]; - if (ori > 0) { // Swap pa and pb. - pt[j] = pa; pa = pb; pb = pt[j]; - } - break; +void tetgenmesh::delaunizecavity(arraypool* cavpoints, arraypool* cavfaces, arraypool* cavshells, + arraypool* newtets, arraypool* crosstets, arraypool* misfaces) { + triface searchtet, neightet, *parytet, *parytet1; + face tmpsh, *parysh; + point pa, pb, pc, pd, pt[3], *parypt; + insertvertexflags ivf; + REAL ori; + long baknum, bakhullsize; + int bakchecksubsegflag, bakchecksubfaceflag; + int t1ver; + int i, j; + + 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). + 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 < cavfaces->objects; i++) { + parytet = (triface*)fastlookup(cavfaces, i); + pt[0] = org(*parytet); + pt[1] = dest(*parytet); + pt[2] = apex(*parytet); + for (j = 0; j < 3; j++) { + if (pt[j] != dummypoint) { // Do not include a hull point. + ori = orient3d(pa, pb, pc, pt[j]); + if (ori != 0) { + pd = pt[j]; + if (ori > 0) { // Swap pa and pb. + pt[j] = pa; + pa = pb; + pb = pt[j]; + } + break; + } + } } - } + if (pd != NULL) break; } - if (pd != NULL) break; - } - // Create an init DT. - initialdelaunay(pa, pb, pc, pd); + // Create an init DT. + initialdelaunay(pa, pb, pc, pd); - // Incrementally insert the vertices (duplicated vertices are ignored). - for (i = 0; i < cavpoints->objects; i++) { - pt[0] = * (point *) fastlookup(cavpoints, i); - searchtet = recenttet; - ivf.iloc = (int) OUTSIDE; - insertpoint(pt[0], &searchtet, NULL, NULL, &ivf); - } + // Incrementally insert the vertices (duplicated vertices are ignored). + for (i = 0; i < cavpoints->objects; i++) { + pt[0] = *(point*)fastlookup(cavpoints, i); + 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); - } + if (b->verbose > 2) { + printf(" Identifying %ld boundary faces of the cavity.\n", cavfaces->objects); + } - while (1) { + while (1) { - // 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; - if (scoutsubface(&tmpsh, &searchtet, 0)) { // shflag = 0 - // 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; - } - } // 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; + if (scoutsubface(&tmpsh, &searchtet, 0)) { // shflag = 0 + // 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; + } + } // i - if (misfaces->objects > 0) { - if (b->verbose > 2) { - printf(" Enlarging the cavity. %ld missing bdry faces\n", - misfaces->objects); - } + if (misfaces->objects > 0) { + if (b->verbose > 2) { + printf(" Enlarging the cavity. %ld missing bdry faces\n", misfaces->objects); + } - // 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(); - - // 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; - } - enextself(*parytet); - } // j - } // if (!infected(parytet)) - } // i - - // 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(); - continue; - } // if (misfaces->objects > 0) - - break; - - } // while (1) - - // Collect all tets of the DT. All new tets are marktested. - marktest(recenttet); - newtets->newindex((void **) &parytet); - *parytet = recenttet; - for (i = 0; i < newtets->objects; i++) { - searchtet = * (triface *) fastlookup(newtets, i); - for (j = 0; j < 4; j++) { - decode(searchtet.tet[j], neightet); - if (!marktested(neightet)) { - marktest(neightet); - newtets->newindex((void **) &parytet); - *parytet = neightet; - } + // 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(); + + // 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; + } + enextself(*parytet); + } // j + } // if (!infected(parytet)) + } // i + + // 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(); + continue; + } // if (misfaces->objects > 0) + + break; + + } // while (1) + + // Collect all tets of the DT. All new tets are marktested. + marktest(recenttet); + newtets->newindex((void**)&parytet); + *parytet = recenttet; + for (i = 0; i < newtets->objects; i++) { + searchtet = *(triface*)fastlookup(newtets, i); + for (j = 0; j < 4; j++) { + decode(searchtet.tet[j], neightet); + if (!marktested(neightet)) { + marktest(neightet); + newtets->newindex((void**)&parytet); + *parytet = neightet; + } + } } - } - cavpoints->restart(); - cavfaces->restart(); + cavpoints->restart(); + cavfaces->restart(); - if (crosstets->objects > baknum) { - // The cavity has been enlarged. - cavityexpcount++; - } + if (crosstets->objects > baknum) { + // The cavity has been enlarged. + cavityexpcount++; + } - // Restore the original values. - hullsize = bakhullsize; - checksubsegflag = bakchecksubsegflag; - checksubfaceflag = bakchecksubfaceflag; - b->verbose++; - b->plc = 1; + // Restore the original values. + hullsize = bakhullsize; + checksubsegflag = bakchecksubsegflag; + checksubfaceflag = bakchecksubfaceflag; + b->verbose++; + b->plc = 1; } /////////////////////////////////////////////////////////////////////////////// @@ -16924,14306 +16735,14126 @@ void tetgenmesh::delaunizecavity(arraypool *cavpoints, arraypool *cavfaces, // // /////////////////////////////////////////////////////////////////////////////// -bool tetgenmesh::fillcavity(arraypool* topshells, arraypool* botshells, - arraypool* midfaces, arraypool* missingshs, - arraypool* topnewtets, arraypool* botnewtets, - triface* crossedge) -{ - arraypool *cavshells; - triface bdrytet, neightet, *parytet; - triface searchtet, spintet; - face *parysh; - face checkseg; - point pa, pb, pc; - bool mflag; - int t1ver; - int i, j; - - // Connect newtets to tets outside the cavity. These connections are needed - // for identifying the middle faces (which belong to R). - for (j = 0; j < 2; j++) { - cavshells = (j == 0 ? topshells : botshells); - if (cavshells != NULL) { - for (i = 0; i < cavshells->objects; i++) { - // Get a temp subface. - parysh = (face *) fastlookup(cavshells, i); - // Get the boundary tet outside the cavity (saved in sh[0]). - decode(parysh->sh[0], bdrytet); - pa = org(bdrytet); - pb = dest(bdrytet); - pc = apex(bdrytet); - // Get the adjacent new tet inside the cavity. - stpivot(*parysh, neightet); - // Mark neightet as an interior tet of this cavity. - infect(neightet); - // Connect the two tets (the old connections are replaced). - bond(bdrytet, neightet); - tsdissolve(neightet); // Clear the pointer to tmpsh. - // Update the point-to-tets map. - setpoint2tet(pa, (tetrahedron) neightet.tet); - setpoint2tet(pb, (tetrahedron) neightet.tet); - setpoint2tet(pc, (tetrahedron) neightet.tet); - } // 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); - if (ori < 0) { - fsymself(toptet); - pa = org(toptet); - pb = dest(toptet); - } else if (ori == 0) { - terminatetetgen(this, 2); - } - // 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; - } +bool tetgenmesh::fillcavity(arraypool* topshells, arraypool* botshells, arraypool* midfaces, + arraypool* missingshs, arraypool* topnewtets, arraypool* botnewtets, + triface* crossedge) { + arraypool* cavshells; + triface bdrytet, neightet, *parytet; + triface searchtet, spintet; + face* parysh; + face checkseg; + point pa, pb, pc; + bool mflag; + int t1ver; + int i, j; + + // Connect newtets to tets outside the cavity. These connections are needed + // for identifying the middle faces (which belong to R). + for (j = 0; j < 2; j++) { + cavshells = (j == 0 ? topshells : botshells); + if (cavshells != NULL) { + for (i = 0; i < cavshells->objects; i++) { + // Get a temp subface. + parysh = (face*)fastlookup(cavshells, i); + // Get the boundary tet outside the cavity (saved in sh[0]). + decode(parysh->sh[0], bdrytet); + pa = org(bdrytet); + pb = dest(bdrytet); + pc = apex(bdrytet); + // Get the adjacent new tet inside the cavity. + stpivot(*parysh, neightet); + // Mark neightet as an interior tet of this cavity. + infect(neightet); + // Connect the two tets (the old connections are replaced). + bond(bdrytet, neightet); + tsdissolve(neightet); // Clear the pointer to tmpsh. + // Update the point-to-tets map. + setpoint2tet(pa, (tetrahedron)neightet.tet); + setpoint2tet(pb, (tetrahedron)neightet.tet); + setpoint2tet(pc, (tetrahedron)neightet.tet); + } // 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); + if (ori < 0) { + fsymself(toptet); + pa = org(toptet); + pb = dest(toptet); + } else if (ori == 0) { + terminatetetgen(this, 2); + } + // 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 + + 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); + // 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) { + terminatetetgen(this, 2); // 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) { + 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 { + // The 'bottet' is not inside the cavity! + terminatetetgen(this, 2); // Check this case + } + } 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!. + terminatetetgen(this, 2); // assert(0); + } + break; // Not matched + } + fsymself(bottet); + } + } // if (!mflag) + } // if (!facemarked(toptet)) + } // if (!bflag) + enextself(*midface); + } // j + } // i + + if (mflag) { + if (b->verbose > 2) { + printf(" Found %ld middle subfaces.\n", midfaces->objects); + } + face oldsh, newsh, casout, casin, neighsh; + + oldsh = *(face*)fastlookup(missingshs, 0); + + // Create new subfaces to fill the region R. + for (i = 0; i < midfaces->objects; i++) { + // Get a matched middle face [a, b, c] + midface = (triface*)fastlookup(midfaces, i); + unmarkface(*midface); + makeshellface(subfaces, &newsh); + setsorg(newsh, org(*midface)); + setsdest(newsh, dest(*midface)); + setsapex(newsh, apex(*midface)); + // The new subface gets its markers from the old one. + setshellmark(newsh, shellmark(oldsh)); + if (checkconstraints) { + setareabound(newsh, areabound(oldsh)); + } + if (useinsertradius) { + setfacetindex(newsh, getfacetindex(oldsh)); + } + // Connect the new subface to adjacent tets. + tsbond(*midface, newsh); + fsym(*midface, neightet); + sesymself(newsh); + tsbond(neightet, newsh); + } + + // Connect new subfaces together and to the bdry of R. + // Delete faked segments. + for (i = 0; i < midfaces->objects; i++) { + // Get a matched middle face [a, b, c] + midface = (triface*)fastlookup(midfaces, i); + for (j = 0; j < 3; j++) { + tspivot(*midface, newsh); + spivot(newsh, casout); + if (casout.sh == NULL) { + // Search its neighbor. + fnext(*midface, searchtet); + while (1) { + // (1) First check if this side is a bdry edge of R. + tsspivot1(searchtet, checkseg); + if (checkseg.sh != NULL) { + // It's a bdry edge of R. + // 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); + } // while (1) + } // if (casout.sh == NULL) + enextself(*midface); } // j - } // if (interflag == 2) - } // pc - } // pb - } // pa - } // toptet.ver - } // i + } // i + + // Delete old subfaces. + for (i = 0; i < missingshs->objects; i++) { + parysh = (face*)fastlookup(missingshs, i); + shellfacedealloc(subfaces, parysh->sh); + } + } 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. + parysh = NULL; // Avoid a warning in MSVC + 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 { + terminatetetgen(this, 2); // assert(0); + } + } else { + // terminatetetgen(this, 2); // Report a bug + } + } - 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; + midfaces->restart(); } 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); - // Check the neighbors at the edges of this face. - for (j = 0; j < 3 && mflag; j++) { - toptet = *midface; - bflag = false; + mflag = true; + } + + // Delete the temp subfaces. + for (j = 0; j < 2; j++) { + cavshells = (j == 0 ? topshells : botshells); + if (cavshells != NULL) { + for (i = 0; i < cavshells->objects; i++) { + parysh = (face*)fastlookup(cavshells, i); + shellfacedealloc(subfaces, parysh->sh); + } + } + } + + topshells->restart(); + if (botshells != NULL) { + botshells->restart(); + } + + return mflag; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// carvecavity() Delete old tets and outer new tets of the cavity. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::carvecavity(arraypool* crosstets, arraypool* topnewtets, arraypool* botnewtets) { + arraypool* newtets; + shellface *sptr, *ssptr; + triface *parytet, *pnewtet, newtet, neightet, spintet; + face checksh, *parysh; + face checkseg, *paryseg; + int t1ver; + int i, j; + + 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. + + // Collect all subfaces and segments which attached to the old tets. + for (i = 0; i < crosstets->objects; i++) { + parytet = (triface*)fastlookup(crosstets, i); + if ((sptr = (shellface*)parytet->tet[9]) != NULL) { + for (j = 0; j < 4; j++) { + if (sptr[j]) { + sdecode(sptr[j], checksh); + if (!sinfected(checksh)) { + sinfect(checksh); + cavetetshlist->newindex((void**)&parysh); + *parysh = checksh; + } + } + } // j + } + if ((ssptr = (shellface*)parytet->tet[8]) != NULL) { + for (j = 0; j < 6; j++) { + if (ssptr[j]) { + sdecode(ssptr[j], checkseg); + // Skip a deleted segment (was a faked segment) + if (checkseg.sh[3] != NULL) { + if (!sinfected(checkseg)) { + sinfect(checkseg); + cavetetseglist->newindex((void**)&paryseg); + *paryseg = checkseg; + } + } + } + } // 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); + 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) { - // Go to the next face in the same tet. - esymself(toptet); - pc = apex(toptet); - if (pmarktested(pc)) { - break; // Find a subface. - } - if (pc == dummypoint) { - terminatetetgen(this, 2); // 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 (!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 (!bflag) { - if (!facemarked(toptet)) { - fsym(*midface, bottet); - spintet = bottet; + if (!infected(spintet)) { + // A boundary segment. Connect this segment to the new tets. + sstbond1(checkseg, spintet); + neightet = spintet; 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 { - // The 'bottet' is not inside the cavity! - terminatetetgen(this, 2); // Check this case - } - } 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!. - terminatetetgen(this, 2); //assert(0); - } - break; // Not matched - } - fsymself(bottet); - } - } // if (!mflag) - } // if (!facemarked(toptet)) - } // if (!bflag) - enextself(*midface); - } // j + 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 < crosstets->objects; i++) { + parytet = (triface*)fastlookup(crosstets, i); + if (ishulltet(*parytet)) { + hullsize--; + } + tetrahedrondealloc(parytet->tet); + } + + crosstets->restart(); // crosstets will be re-used. + + // 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 < newtets->objects; i++) { + parytet = (triface*)fastlookup(newtets, i); + if (infected(*parytet)) { + crosstets->newindex((void**)&pnewtet); + *pnewtet = *parytet; + } + } // i + } + } // j + + // Now we collect all new tets in cavity. + for (i = 0; i < crosstets->objects; i++) { + parytet = (triface*)fastlookup(crosstets, i); + for (j = 0; j < 4; j++) { + decode(parytet->tet[j], neightet); + if (marktested(neightet)) { // Is it a new tet? + if (!infected(neightet)) { + // Find an interior tet. + // assert((point) neightet.tet[7] != dummypoint); // SELF_CHECK + infect(neightet); + crosstets->newindex((void**)&pnewtet); + *pnewtet = neightet; + } + } + } // j + } // i + + parytet = (triface*)fastlookup(crosstets, 0); + recenttet = *parytet; // Remember a live handle. + + // Delete outer new tets. + for (j = 0; j < 2; j++) { + newtets = (j == 0 ? topnewtets : botnewtets); + if (newtets != NULL) { + 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); + } + } + } + } + + crosstets->restart(); + topnewtets->restart(); + if (botnewtets != NULL) { + botnewtets->restart(); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// restorecavity() Reconnect old tets and delete new tets of the cavity. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::restorecavity(arraypool* crosstets, arraypool* topnewtets, arraypool* botnewtets, + arraypool* missingshbds) { + triface *parytet, neightet, spintet; + face* parysh; + face checkseg; + point* ppt; + int t1ver; + int i, j; + + // Reconnect crossing tets to cavity boundary. + for (i = 0; i < crosstets->objects; i++) { + parytet = (triface*)fastlookup(crosstets, i); + parytet->ver = 0; + for (parytet->ver = 0; parytet->ver < 4; parytet->ver++) { + fsym(*parytet, neightet); + if (!infected(neightet)) { + // Restore the old connections of tets. + bond(*parytet, neightet); + } + } + // Update the point-to-tet map. + parytet->ver = 0; + ppt = (point*)&(parytet->tet[4]); + for (j = 0; j < 4; j++) { + setpoint2tet(ppt[j], encode(*parytet)); + } + } + + // Uninfect all crossing tets. + for (i = 0; i < crosstets->objects; i++) { + parytet = (triface*)fastlookup(crosstets, i); + uninfect(*parytet); + } + + // Remember a live handle. + if (crosstets->objects > 0) { + recenttet = *(triface*)fastlookup(crosstets, 0); + } + + // Delete faked segments. + for (i = 0; i < missingshbds->objects; i++) { + parysh = (face*)fastlookup(missingshbds, i); + sspivot(*parysh, checkseg); + 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 - if (mflag) { - if (b->verbose > 2) { - printf(" Found %ld middle subfaces.\n", midfaces->objects); - } - face oldsh, newsh, casout, casin, neighsh; + // Delete new tets. + for (i = 0; i < topnewtets->objects; i++) { + parytet = (triface*)fastlookup(topnewtets, i); + tetrahedrondealloc(parytet->tet); + } + + if (botnewtets != NULL) { + for (i = 0; i < botnewtets->objects; i++) { + parytet = (triface*)fastlookup(botnewtets, i); + tetrahedrondealloc(parytet->tet); + } + } + + crosstets->restart(); + topnewtets->restart(); + if (botnewtets != NULL) { + botnewtets->restart(); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// flipcertify() Insert a crossing face into priority queue. // +// // +// A crossing face of a facet must have at least one top and one bottom ver- // +// tex of the facet. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::flipcertify(triface* chkface, badface** pqueue, point plane_pa, point plane_pb, + point plane_pc) { + badface *parybf, *prevbf, *nextbf; + triface neightet; + face checksh; + point p[5]; + REAL w[5]; + REAL insph, ori4; + int topi, boti; + int i; + + // Compute the flip time \tau. + fsym(*chkface, neightet); + + p[0] = org(*chkface); + p[1] = dest(*chkface); + p[2] = apex(*chkface); + p[3] = oppo(*chkface); + p[4] = oppo(neightet); + + // 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]); + 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; // Test: omit this face. + } + + // 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]; + } else { + w[i] = 0; + } + } + + 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 (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])); + } + + 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 { + break; + } + } + //} // if (!b->flipinsert_random) + // Insert the new item between prev and next items. + if (prevbf == NULL) { + *pqueue = parybf; + } else { + prevbf->nextitem = parybf; + } + parybf->nextitem = nextbf; + } + } else if (ori4 == 0) { + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// flipinsertfacet() Insert a facet into a CDT by flips. // +// // +// 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::flipinsertfacet(arraypool* crosstets, arraypool* toppoints, arraypool* botpoints, + arraypool* midpoints) { + arraypool *crossfaces, *bfacearray; + triface fliptets[6], baktets[2], fliptet, newface; + triface neightet, *parytet; + 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; + + f23count = flip23count; + f32count = flip32count; + f44count = flip44count; + + // Get three affinely independent vertices in the missing region R. + calculateabovepoint(midpoints, &plane_pa, &plane_pb, &plane_pc); + + // 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); + } + } + for (i = 0; i < botpoints->objects; i++) { + parypt = (point*)fastlookup(botpoints, i); + if (!pmarktested(*parypt)) { + pmarktest3(*parypt); + } + } + + // Collect crossing faces. + crossfaces = cavetetlist; // Re-use array 'cavetetlist'. + + // 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); + } + + if (b->verbose > 1) { + printf(" Found %ld crossing faces.\n", crossfaces->objects); + } + + for (i = 0; i < crosstets->objects; i++) { + parytet = (triface*)fastlookup(crosstets, i); + unmarktest(*parytet); + uninfect(*parytet); + } + + // Initialize the priority queue. + pqueue = NULL; + + for (i = 0; i < crossfaces->objects; i++) { + parytet = (triface*)fastlookup(crossfaces, i); + flipcertify(parytet, &pqueue, plane_pa, plane_pb, plane_pc); + } + crossfaces->restart(); + + // The list for temporarily storing unflipable faces. + bfacearray = new arraypool(sizeof(triface), 4); + + fcount = 0; // Count the number of flips. + + // Flip insert the facet. + while (pqueue != NULL) { + + // Pop a face from the priority queue. + popbf = pqueue; + bface = *popbf; + // 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); + 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); + } + flipflag = 0; + + // Check for which type of flip can we do. + convcount = 3; + copcount = 0; + for (i = 0; i < 3; i++) { + 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. + 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) { + // 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); + } + + // Collect tets sharing at this edge. + 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]); + } + 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 { + // 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; + } + } // 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); + terminatetetgen(this, 2); // assert(0); + } + } + + delete bfacearray; + + // 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); + } + + 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); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// insertpoint_cdt() Insert a new point into a CDT. // +// // +/////////////////////////////////////////////////////////////////////////////// + +int tetgenmesh::insertpoint_cdt(point newpt, triface* searchtet, face* splitsh, face* splitseg, + insertvertexflags* ivf, arraypool* cavpoints, arraypool* cavfaces, + arraypool* cavshells, arraypool* newtets, arraypool* crosstets, + arraypool* misfaces) { + triface neightet, *parytet; + face checksh, *parysh, *parysh1; + face *paryseg, *paryseg1; + point* parypt; + int t1ver; + int i; + + if (b->verbose > 2) { + printf(" Insert point %d into CDT\n", pointmark(newpt)); + } + + if (!insertpoint(newpt, searchtet, NULL, NULL, ivf)) { + // Point is not inserted. Check ivf->iloc for reason. + return 0; + } + + 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; + + 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; + } + } + + 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). + tsdissolve(neightet); + fsymself(neightet); + tsdissolve(neightet); + } + } + } + shellfacedealloc(subfaces, parysh->sh); + } + if (splitseg != NULL) { + // Delete the old segment in sC(p). + shellfacedealloc(subsegs, splitseg->sh); + } + + // Clear working lists. + caveshlist->restart(); + caveshbdlist->restart(); + cavesegshlist->restart(); + } // if ((splitsh != NULL) || (splitseg != NULL)) + + // Put all interior subfaces into stack for recovery. + // They were collected in carvecavity(). + // Note: Some collected subfaces may be deleted by sinsertvertex(). + for (i = 0; i < caveencshlist->objects; i++) { + parysh = (face*)fastlookup(caveencshlist, i); + if (parysh->sh[3] != NULL) { + subfacstack->newindex((void**)&parysh1); + *parysh1 = *parysh; + } + } + + // 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; + } + } + + caveencshlist->restart(); + caveencseglist->restart(); + + return 1; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// refineregion() Refine a missing region by inserting points. // +// // +// 'splitsh' represents an edge of the facet to be split. It must be not a // +// segment. +// // +// Assumption: The current mesh is a CDT and is convex. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::refineregion(face& splitsh, arraypool* cavpoints, arraypool* cavfaces, + arraypool* cavshells, arraypool* newtets, arraypool* crosstets, + arraypool* misfaces) { + triface searchtet, spintet; + face splitseg, *paryseg; + point steinpt, pa, pb, refpt; + insertvertexflags ivf; + enum interresult dir; + long baknum = points->items; + int t1ver; + int i; + + // Do not split a segment. + for (i = 0; i < 3; i++) { + sspivot(splitsh, splitseg); + if (splitseg.sh == NULL) break; + senextself(splitsh); + } + + 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; + ivf.smlenflag = useinsertradius; // Return the closet mesh vertex. + + 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. + 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)) { + terminatetetgen(this, 2); + } + if (useinsertradius) { + save_segmentpoint_insradius(steinpt, ivf.parentpt, ivf.smlen); + } + st_segref_count++; + if (steinerleft > 0) steinerleft--; + } else { + terminatetetgen(this, 2); // assert(0); + } + } else { + if (useinsertradius) { + save_facetpoint_insradius(steinpt, ivf.parentpt, ivf.smlen); + } + st_facref_count++; + if (steinerleft > 0) steinerleft--; + } + + 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), &splitseg, &searchtet, &refpt, NULL); + if (dir == SHAREEDGE) { + // Found this segment, insert it. + // 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 { + if ((dir == ACROSSFACE) || (dir == ACROSSEDGE)) { + // Split the segment. + makepoint(&steinpt, 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)) { + terminatetetgen(this, 2); + } + if (useinsertradius) { + save_segmentpoint_insradius(steinpt, ivf.parentpt, ivf.smlen); + } + st_segref_count++; + if (steinerleft > 0) steinerleft--; + } else { + terminatetetgen(this, 2); + } + } + } // while + + if (b->verbose > 2) { + printf(" Added %ld Steiner points.\n", points->items - baknum); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// constrainedfacets() Recover constrained facets in a CDT. // +// // +// All unrecovered subfaces are queued in 'subfacestack'. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::constrainedfacets() { + 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; + + // 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); + 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); + } + + if (b->verbose > 1) { + printf(" Recovering facet #%d: %ld subfaces.\n", facetcount + 1, + tg_facfaces->objects); + } + facetcount++; + + while (tg_facfaces->objects > 0l) { + + tg_facfaces->objects--; + parysh = (face*)fastlookup(tg_facfaces, tg_facfaces->objects); + searchsh = *parysh; + + if (searchsh.sh[3] == NULL) continue; // It is dead. + if (isshtet(searchsh)) continue; // It is recovered. + + searchtet.tet = NULL; + if (scoutsubface(&searchsh, &searchtet, 1)) continue; + + // The subface is missing. Form the missing region. + // Re-use 'tg_crosstets' for 'adjtets'. + formregion(&searchsh, tg_missingshs, tg_missingshbds, tg_missingshverts); + + int searchflag = scoutcrossedge(searchtet, tg_missingshbds, tg_missingshs); + if (searchflag > 0) { + // 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); + // Put all subfaces in R back to tg_facfaces. + for (i = 0; i < tg_missingshs->objects; i++) { + parysh = (face*)fastlookup(tg_missingshs, i); + tg_facfaces->newindex((void**)&parysh1); + *parysh1 = *parysh; + } + success = 1; + // Clear working lists. + tg_crosstets->restart(); + tg_topfaces->restart(); + tg_botfaces->restart(); + tg_toppoints->restart(); + tg_botpoints->restart(); + } // b->flipinsert + + if (success) { + // Recover interior subfaces. + for (i = 0; i < caveencshlist->objects; i++) { + parysh = (face*)fastlookup(caveencshlist, i); + if (!scoutsubface(parysh, &searchtet, 1)) { + // 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), paryseg, &searchtet, + NULL, NULL); + if (dir != SHAREEDGE) { + terminatetetgen(this, 2); + } + // Insert this segment. + // 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); + } + caveencseglist->restart(); + } // success - remesh cavity + } // success - form cavity + else { + terminatetetgen(this, 2); // Report a bug. + } // Not success - form cavity + } else { + // Put all subfaces in R back to tg_facfaces. + for (i = 0; i < tg_missingshs->objects; i++) { + parysh = (face*)fastlookup(tg_missingshs, i); + tg_facfaces->newindex((void**)&parysh1); + *parysh1 = *parysh; + } + if (searchflag != -1) { + // Some edge(s) in the missing regions were flipped. + success = 1; + } else { + restorecavity(tg_crosstets, tg_topnewtets, tg_botnewtets, + tg_missingshbds); // Only remove fake segments. + // Choose an edge to split (set in recentsh) + recentsh = searchsh; + success = 0; // Do refineregion(); + } + } // if (scoutcrossedge) + + // 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); + } + } // 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; + encseglist = NULL; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// constraineddelaunay() Create a constrained Delaunay tetrahedralization.// +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::constraineddelaunay(clock_t& tv) { + face searchsh, *parysh; + face searchseg, *paryseg; + int s, i; + + // Statistics. + long bakfillregioncount; + long bakcavitycount, bakcavityexpcount; + long bakseg_ref_count; + + if (!b->quiet) { + printf("Constrained Delaunay...\n"); + } + + makesegmentendpointsmap(); + makefacetverticesmap(); + + if (b->verbose) { + printf(" Delaunizing segments.\n"); + } + + 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; + } + + // Recover non-Delaunay segments. + delaunizesegments(); + + if (b->verbose) { + printf(" Inserted %ld Steiner points.\n", st_segref_count); + } + + tv = clock(); + + if (b->verbose) { + printf(" Constraining facets.\n"); + } + + // Subfaces will be introduced. + checksubfaceflag = 1; + + bakfillregioncount = fillregioncount; + bakcavitycount = cavitycount; + bakcavityexpcount = cavityexpcount; + bakseg_ref_count = st_segref_count; + + // 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; + } + + // Recover facets. + constrainedfacets(); + + 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); + } + } +} + +//// //// +//// //// +//// constrained_cxx ////////////////////////////////////////////////////////// + +//// steiner_cxx ////////////////////////////////////////////////////////////// +//// //// +//// //// + +/////////////////////////////////////////////////////////////////////////////// +// // +// checkflipeligibility() A call back function for boundary recovery. // +// // +// 'fliptype' indicates which elementary flip will be performed: 1 : 2-to-3, // +// and 2 : 3-to-2, respectively. // +// // +// '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'. // +// // +/////////////////////////////////////////////////////////////////////////////// + +int tetgenmesh::checkflipeligibility(int fliptype, point pa, point pb, point pc, point pd, point pe, + int level, int edgepivot, flipconstraints* fc) { + point tmppts[3]; + enum interresult dir; + int types[2], poss[4]; + int intflag; + int rejflag = 0; + int i; + + if (fc->seg[0] != NULL) { + // A constraining edge is given (e.g., for edge recovery). + if (fliptype == 1) { + // A 2-to-3 flip: [a,b,c] => [e,d,a], [e,d,b], [e,d,c]. + 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; + } + } + } 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; + } + } + } + } // 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. + // 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 { + // 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; + } + } + } + } // 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. + 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; + } + } + } + } + } else { // level > 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 { + // 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 + } + } + + return rejflag; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// removeedgebyflips() Attempt to remove an edge by flips. // +// // +// 'flipedge' is a non-convex or flat edge [a,b,#,#] to be removed. // +// // +// The return value is a positive integer, it indicates whether the edge is // +// removed or not. A value "2" means the edge is removed, otherwise, the // +// edge is not removed and the value (must >= 3) is the current number of // +// tets in the edge star. // +// // +/////////////////////////////////////////////////////////////////////////////// + +int tetgenmesh::removeedgebyflips(triface* flipedge, flipconstraints* fc) { + 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; + } + } + return 0; + } + } + + // Count the number of tets at edge [a,b]. + n = 0; + spintet = *flipedge; + while (1) { + n++; + fnextself(spintet); + if (spintet.tet == flipedge->tet) break; + } + if (n < 3) { + // It is only possible when the mesh contains inverted tetrahedra. + terminatetetgen(this, 2); // Report a bug + } + + if ((b->flipstarsize > 0) && (n > b->flipstarsize)) { + // The star size exceeds the limit. + return 0; // Do not flip it. + } + + // 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; + } + + // Try to flip the edge (level = 0, edgepivot = 0). + nn = flipnm(abtets, n, 0, 0, fc); + + 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]; + } + + // 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; + + delete[] abtets; + + return nn; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// removefacebyflips() Remove a face by flips. // +// // +// Return 1 if the face is removed. Otherwise, return 0. // +// // +// ASSUMPTIONS: // +// - 'flipface' must not be a subface. // +// - 'flipface' must not be a hull face. // +// // +/////////////////////////////////////////////////////////////////////////////// + +int tetgenmesh::removefacebyflips(triface* flipface, flipconstraints* fc) { + triface fliptets[3], flipedge; + point pa, pb, pc, pd, pe; + REAL ori; + int reducflag = 0; + + 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] + } + } else { + enext(*flipface, flipedge); // [b,c] + } + } else { + flipedge = *flipface; // [a,b] + } + + 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; + } + } + + // Face is not removed. + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// 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. // +// // +// The parameter 'sedge' is used to report self-intersection. If it is not // +// a NULL, it is EITHER a segment OR a subface that contains this edge. // +// // +// Note that this routine assumes that the tetrahedralization is convex. // +// // +/////////////////////////////////////////////////////////////////////////////// + +int tetgenmesh::recoveredgebyflips(point startpt, point endpt, face* sedge, triface* searchtet, + int fullsearch) { + flipconstraints fc; + enum interresult dir; + + fc.seg[0] = startpt; + fc.seg[1] = endpt; + fc.checkflipeligibility = 1; + + // The mainloop of the edge reocvery. + while (1) { // Loop I + + // Search the edge from 'startpt'. + point2tetorg(startpt, *searchtet); + dir = finddirection(searchtet, endpt); + if (dir == ACROSSVERT) { + if (dest(*searchtet) == endpt) { + return 1; // Edge is recovered. + } else { + if (sedge) { + return report_selfint_edge(startpt, endpt, sedge, searchtet, dir); + } else { + return 0; + } + } + } + + // The edge is missing. + + // Try to remove the first intersecting face/edge. + enextesymself(*searchtet); // Go to the opposite face. + if (dir == ACROSSFACE) { + if (checksubfaceflag) { + if (issubface(*searchtet)) { + if (sedge) { + return report_selfint_edge(startpt, endpt, sedge, searchtet, dir); + } else { + return 0; // Cannot flip a subface. + } + } + } + // Try to flip a crossing face. + if (removefacebyflips(searchtet, &fc)) { + continue; + } + } else if (dir == ACROSSEDGE) { + if (checksubsegflag) { + if (issubseg(*searchtet)) { + if (sedge) { + return report_selfint_edge(startpt, endpt, sedge, searchtet, dir); + } else { + return 0; // Cannot flip a segment. + } + } + } + // Try to flip an intersecting edge. + if (removeedgebyflips(searchtet, &fc) == 2) { + continue; + } + } + + // 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); + + // 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. + if (dir == DISJOINT) { + terminatetetgen(this, 2); + } + } else if (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 + } else { + terminatetetgen(this, 2); // Report a bug + } + + // 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 { + terminatetetgen(this, 2); // Report a bug + } + } + + // 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 (checksubfaceflag) { + if (issubface(*searchtet)) { + if (sedge) { + return report_selfint_edge(startpt, endpt, sedge, searchtet, dir); + } else { + return 0; // Cannot flip a subface. + } + } + } + if (removefacebyflips(searchtet, &fc)) { + success = 1; + break; // Loop I-I + } + } else if (dir == ACROSSEDGE) { + if (checksubsegflag) { + if (issubseg(*searchtet)) { + if (sedge) { + return report_selfint_edge(startpt, endpt, sedge, searchtet, dir); + } else { + return 0; // Cannot flip a segment. + } + } + } + if (removeedgebyflips(searchtet, &fc) == 2) { + success = 1; + break; // Loop I-I + } + } else if (dir == ACROSSVERT) { + if (sedge) { + // return report_selfint_edge(startpt, endpt, sedge, searchtet, dir); + terminatetetgen(this, 2); + } else { + return 0; + } + } else { + terminatetetgen(this, 2); + } + + // 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) { + if (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) { + // The original (intersecting) tet has been flipped. + searchtet->tet = NULL; + break; // Not find. + } + } + } + } else { + searchtet->tet = NULL; // 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; + } + + } // if (fullsearch) + + // The edge is missing. + break; // Loop I + + } // while (1) // Loop I + + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// add_steinerpt_in_schoenhardtpoly() Insert a Steiner point in a Schoen- // +// hardt polyhedron. // +// // +// '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. // +// // +/////////////////////////////////////////////////////////////////////////////// + +int tetgenmesh::add_steinerpt_in_schoenhardtpoly(triface* abtets, int n, int chkencflag) { + 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; + + pc = apex(abtets[0]); // pc = p0 + pd = oppo(abtets[n - 1]); // pd = p_(n-1) + + // Find an optimial point in edge [c,d]. It is visible by all outer faces + // of 'abtets', and it maxmizes the min volume. + + // 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; + } + + 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 (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; + } + + // 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; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// add_steinerpt_in_segment() Add a Steiner point inside a segment. // +// // +/////////////////////////////////////////////////////////////////////////////// + +int tetgenmesh::add_steinerpt_in_segment(face* misseg, int searchlevel) { + triface searchtet; + face *paryseg, candseg; + point startpt, endpt, pc, pd; + flipconstraints fc; + enum interresult dir; + REAL P[3], Q[3], tp, tq; + REAL len, smlen = 0, split = 0, split_q = 0; + int success; + int i; + + startpt = sorg(*misseg); + endpt = sdest(*misseg); + + fc.seg[0] = startpt; + fc.seg[1] = endpt; + fc.checkflipeligibility = 1; + fc.collectencsegflag = 1; + + point2tetorg(startpt, searchtet); + dir = finddirection(&searchtet, endpt); + // 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); + } else if (dir == ACROSSEDGE) { + // An edge is intersected with the segment. Try to flip it. + success = removeedgebyflips(&searchtet, &fc); + } + + 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; + } + } + } + } + + caveencseglist->restart(); + b->fliplinklevel = bak_fliplinklevel; + + if (split == 0) { + // Found no crossing segment. + return 0; + } + + face splitsh; + face splitseg; + point steinerpt, *parypt; + insertvertexflags ivf; + + 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]); + } + } + + // 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; + } + + 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 1; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// addsteiner4recoversegment() Add a Steiner point for recovering a seg. // +// // +/////////////////////////////////////////////////////////////////////////////// + +int tetgenmesh::addsteiner4recoversegment(face* misseg, int splitsegflag) { + triface *abtets, searchtet, spintet; + face splitsh; + face* paryseg; + point startpt, endpt; + point pa, pb, pd, steinerpt, *parypt; + enum interresult dir; + insertvertexflags ivf; + int types[2], poss[4]; + int n, endi, success; + int t1ver; + int i; + + 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); + + 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); + if (tri_edge_test(pa, pb, pd, startpt, endpt, NULL, 1, types, poss)) { + break; // Found the edge. + } + enextself(spintet); + eprevself(searchtet); + } + esymself(searchtet); + } + + 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. + } + n++; // Count a tet in the star. + fnextself(spintet); + if (spintet.tet == searchtet.tet) break; + } + + 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); + } + + success = 0; + + if (dir == ACROSSFACE) { + // Find a Steiner points inside the polyhedron. + if (add_steinerpt_in_schoenhardtpoly(abtets, endi, 0)) { + success = 1; + } + } else if (dir == ACROSSEDGE) { + // PLC check. + if (issubseg(searchtet)) { + terminatetetgen(this, 2); + } + 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 ((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); + } + } else { + terminatetetgen(this, 2); + } + + delete[] abtets; + + if (success) { + // Add the missing segment back to the recovering list. + subsegstack->newindex((void**)&paryseg); + *paryseg = *misseg; + return 1; + } + } // if (endi > 0) + + if (!splitsegflag) { + return 0; + } + + if (b->verbose > 2) { + printf(" Splitting segment (%d, %d)\n", pointmark(startpt), pointmark(endpt)); + } + steinerpt = NULL; + + 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); + } + + 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. + 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)) { + terminatetetgen(this, 2); + } + } // if (endi > 0) + + // Save this Steiner point (for removal). + // Re-use the array 'subvertstack'. + subvertstack->newindex((void**)&parypt); + *parypt = steinerpt; + + st_segref_count++; + if (steinerleft > 0) steinerleft--; + + return 1; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// recoversegments() Recover all segments. // +// // +// 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. // +// // +/////////////////////////////////////////////////////////////////////////////// + +int tetgenmesh::recoversegments(arraypool* misseglist, int fullsearch, int steinerflag) { + 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(" Recover segments [%s level = %2d] #: %ld.\n", + (b->fliplinklevel > 0) ? "fixed" : "auto", + (b->fliplinklevel > 0) ? b->fliplinklevel : autofliplinklevel, subsegstack->objects); + } + + // 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. + } + + 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, &sseg, &searchtet, 0)) { + success = 1; + } else { + // Try to recover it from the other direction. + if (recoveredgebyflips(endpt, startpt, &sseg, &searchtet, 0)) { + success = 1; + } + } + + if (!success && fullsearch) { + if (recoveredgebyflips(startpt, endpt, &sseg, &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; + } + } + if (!success) { + if (misseglist != NULL) { + // Save this segment. + misseglist->newindex((void**)&paryseg); + *paryseg = sseg; + } + } + } + + } // while (subsegstack->objects > 0l) + + if (steinerflag) { + if (b->verbose > 1) { + // 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); + } + } + } + + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// recoverfacebyflips() Recover a face by flips. // +// // +// 'pa', 'pb', and 'pc' are the three vertices of this face. This routine // +// tries to recover it in the tetrahedral mesh. It is assumed that the three // +// edges, i.e., pa->pb, pb->pc, and pc->pa all exist. // +// // +// If the face is recovered, it is returned by 'searchtet'. // +// // +// If 'searchsh' is not NULL, it is a subface to be recovered. Its vertices // +// must be pa, pb, and pc. It is mainly used to check self-intersections. // +// Another use of this subface is to split it when a Steiner point is found // +// inside this subface. // +// // +/////////////////////////////////////////////////////////////////////////////// + +int tetgenmesh::recoverfacebyflips(point pa, point pb, point pc, face* searchsh, + triface* searchtet) { + triface spintet, flipedge; + point pd, pe; + flipconstraints fc; + int types[2], poss[4], intflag; + int success; + int t1ver; + int i, j; + + 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); + finddirection(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. + flipedge.tet = NULL; + // 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 the assumption that all edges of the face exist, they can + // only intersect at a single point. + if (intflag == 2) { + // Go to the edge [d,e]. + edestoppo(spintet, flipedge); // [d,e,a,b] + if (searchsh != NULL) { + // Check the intersection type. + if ((types[0] == (int)ACROSSFACE) || + (types[0] == (int)ACROSSEDGE)) { + // Check if [e,d] is a segment. + if (issubseg(flipedge)) { + return report_selfint_face(pa, pb, pc, searchsh, &flipedge, + intflag, types, poss); + } else { + // Check if [e,d] is an edge of a subface. + triface chkface = flipedge; + while (1) { + if (issubface(chkface)) break; + fsymself(chkface); + if (chkface.tet == flipedge.tet) break; + } + if (issubface(chkface)) { + // Two subfaces are intersecting. + return report_selfint_face(pa, pb, pc, searchsh, + &chkface, intflag, types, + poss); + } + } + } else if (types[0] == TOUCHFACE) { + // This is possible when a Steiner point was added on it. + 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. A 1-to-3 flip. + 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). + 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. + return 1; + } else { + // Other cases may be due to a bug or a PLC error. + return report_selfint_face(pa, pb, pc, searchsh, &flipedge, + intflag, types, poss); + } + } else { + // The other intersection types: ACROSSVERT, TOUCHEDGE, + // SHAREVERTEX should not be possible or due to a PLC error. + return report_selfint_face(pa, pb, pc, searchsh, &flipedge, + intflag, types, poss); + } + } // if (searchsh != NULL) + } else { // intflag == 4. Coplanar case. + terminatetetgen(this, 2); + } + break; + } // if (intflag > 0) + } + fnextself(spintet); + if (spintet.tet == searchtet->tet) { + terminatetetgen(this, 2); + } + } // while (1) + // Try to flip the edge [d,e]. + if (removeedgebyflips(&flipedge, &fc) == 2) { + // A crossing edge is removed. + continue; + } + break; + } // while (1) + } // i + + return success; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// recoversubfaces() Recover all subfaces. // +// // +/////////////////////////////////////////////////////////////////////////////// + +int tetgenmesh::recoversubfaces(arraypool* misshlist, int steinerflag) { + triface searchtet, neightet, spintet; + face searchsh, neighsh, neineish, *parysh; + face bdsegs[3]; + point startpt, endpt, apexpt, *parypt; + point steinerpt; + insertvertexflags ivf; + int success; + int t1ver; + int i, j; + + 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); + } + + // 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))); + } + + // 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) { + terminatetetgen(this, 2); + } + } 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); + finddirection(&searchtet, endpt); + if (dest(searchtet) == endpt) { + success = 1; + } else { + // The edge is missing. Try to recover it. + if (recoveredgebyflips(startpt, endpt, &searchsh, &searchtet, 0)) { + success = 1; + } else { + if (recoveredgebyflips(endpt, startpt, &searchsh, &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); + ssdissolve(neineish); + spivot(neineish, neighsh); + if (neighsh.sh != NULL) { + ssdissolve(neighsh); + } + sstpivot1(bdsegs[j], searchtet); + 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)) { + terminatetetgen(this, 2); + } + // 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); + ssdissolve(neineish); + spivot(neineish, neighsh); + if (neighsh.sh != NULL) { + ssdissolve(neighsh); + } + sstpivot1(bdsegs[j], neightet); + 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)) { + terminatetetgen(this, 2); + } + // 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 (!success) { + if (misshlist != NULL) { + // Save this subface. + misshlist->newindex((void**)&parysh); + *parysh = searchsh; + } + } + + } // while (subfacstack->objects > 0l) + + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// 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. // +// // +/////////////////////////////////////////////////////////////////////////////// + +int tetgenmesh::getvertexstar(int fullstar, point searchpt, arraypool* tetlist, arraypool* vertlist, + arraypool* shlist) { + triface searchtet, neightet, *parytet; + face checksh, *parysh; + point pt, *parypt; + int collectflag; + int t1ver; + int i, j; + + 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; + } + } + + 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 (!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; + } + } + 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; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// getedge() Get a tetrahedron having the two endpoints. // +// // +// 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. // +// // +// This function is used for the case when the mesh is non-convex. Otherwise,// +// the function finddirection() should be faster than this. // +// // +/////////////////////////////////////////////////////////////////////////////// + +int tetgenmesh::getedge(point e1, point e2, triface* tedge) { + triface searchtet, neightet, *parytet; + point pt; + int done; + int i, j; + + 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; + } + } + } + + // 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); + 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; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// reduceedgesatvertex() Reduce the number of edges at a given vertex. // +// // +// 'endptlist' contains the endpoints of edges connecting at the vertex. // +// // +/////////////////////////////////////////////////////////////////////////////// + +int tetgenmesh::reduceedgesatvertex(point startpt, arraypool* endptlist) { + triface searchtet; + point *pendpt, *parypt; + enum interresult dir; + flipconstraints fc; + int reduceflag; + int count; + int n, i, j; + + fc.remvert = startpt; + fc.checkflipeligibility = 1; + + 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. + } + reduceflag = 0; + // Find the edge. + if (nonconvex) { + if (getedge(startpt, *pendpt, &searchtet)) { + dir = ACROSSVERT; + } else { + // The edge does not exist (was flipped). + dir = INTERSECT; + } + } 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; + } + } + } + } else { + // The edge has been flipped. + reduceflag = 1; + } + 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 + + if (count == 0) { + // No edge is reduced. + break; + } + + } // while (1) + + return (int)endptlist->objects; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// 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 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. // +// // +// Unless T is a Delaunay tetrahedralization, there is no guarantee that 'p' // +// can be successfully removed. // +// // +/////////////////////////////////////////////////////////////////////////////// + +int tetgenmesh::removevertexbyflips(point steinerpt) { + 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; + + vt = pointtype(steinerpt); + + if (vt == FREESEGVERTEX) { + sdecode(point2sh(steinerpt), leftseg); + leftseg.shver = 0; + if (sdest(leftseg) == steinerpt) { + senext(leftseg, rightseg); + spivotself(rightseg); + rightseg.shver = 0; + } else { + rightseg = leftseg; + senext2(rightseg, leftseg); + spivotself(leftseg); + leftseg.shver = 0; + } + lpt = sorg(leftseg); + rpt = sdest(rightseg); + if (b->verbose > 2) { + printf(" Removing Steiner point %d in segment (%d, %d).\n", pointmark(steinerpt), + pointmark(lpt), pointmark(rpt)); + } + } else if (vt == FREEFACETVERTEX) { + if (b->verbose > 2) { + printf(" Removing Steiner point %d in facet.\n", pointmark(steinerpt)); + } + } else if (vt == FREEVOLVERTEX) { + if (b->verbose > 2) { + printf(" Removing Steiner point %d in volume.\n", pointmark(steinerpt)); + } + } else 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; + } + 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); + } + 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) { + valence++; + fnextself(spintet); + if (spintet.tet == wrktets[i].tet) break; + } + if (valence == 3) { + // Found the edge. + searchtet = wrktets[i]; + break; + } + } + // Note, we do not detach the three subfaces at p. + // They will be removed within a 4-to-1 flip. + loc = ONFACE; + removeflag = 1; + } + // 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); + } + 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; + } + { + // [2017-10-15] Check if the tet is inverted? + point chkp1 = org(neightet); + point chkp2 = apex(neightet); + REAL chkori = orient3d(rpt, lpt, chkp1, chkp2); + if (chkori >= 0.0) { + // Either inverted or degenerated. + break; + } + } + fnextself(spintet); + if (spintet.tet == searchtet.tet) { + // 'searchtet' is [p,d,p1,p2]. + loc = ONEDGE; + removeflag = 1; + break; + } + } + } // if (vt == FREESEGVERTEX) + } + + 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. + sstbond1(rightseg, searchtet); + spintet = searchtet; + while (1) { + 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; + } + + 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; + } + 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; + } + } + } // i + } // if (checksubfaceflag) + } + + 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); + /* + { // Do not flip if there are wrong number of subfaces inside. + // Check if there are three subfaces at 'p'. + triface newface; face flipshs[3]; + int spivot = 0, scount = 0; + for (i = 0; i < 3; i++) { + fnext(fliptets[3], newface); // [a,b,p,d],[b,c,p,d],[c,a,p,d]. + tspivot(newface, flipshs[i]); + if (flipshs[i].sh != NULL) { + spivot = i; // Remember this subface. + scount++; + } + enextself(fliptets[3]); + } + if (scount > 0) { + // There are three subfaces connecting at p. + // Only do flip if a 3-to-1 flip is possible at p at the bottom face. + if (scount != 3) { + // Wrong number of subfaces. Do not flip. + delete [] fliptets; + return 0; + } + // [2018-03-07] an old fix, not 100% safe. + // if (scount < 3) { + // // The new subface is one of {[a,b,d], [b,c,d], [c,a,d]}. + // // assert(scount == 1); // spivot >= 0 + // if (scount != 1) { + // // Wrong number of subfaces. Do not flip. + // delete [] fliptets; + // return 0; + // } + //} + } + } + */ + if (vt == FREEFACETVERTEX) { + // [2018-03-08] Check if the last 4-to-1 flip is valid. + // fliptets[0],[1],[2] are [p,d,a,b],[p,d,b,c],[p,d,c,a] + triface checktet, chkface; + for (i = 0; i < 3; i++) { + enext(fliptets[i], checktet); + esymself(checktet); // [a,d,b,p],[b,d,c,p],[c,d,a,p] + int scount = 0; + int k; + for (k = 0; k < 3; k++) { + esym(checktet, chkface); + if (issubface(chkface)) scount++; + enextself(checktet); + } + if (scount == 3) { + break; // Found a tet which support a 3-to-1 flip. + } else if (scount == 2) { + // This is a strange configuration. Debug it. + // Do not do this flip. + delete[] fliptets; + return 0; + } + } + if (i == 3) { + // No tet in [p,d,a,b],[p,d,b,c],[p,d,c,a] support it. + int scount = 0; + for (i = 0; i < 3; i++) { + eprev(fliptets[i], checktet); + esymself(checktet); // [p,a,b,d],[p,b,c,d],[p,c,a,d] + if (issubface(chkface)) scount++; + } + if (scount != 3) { + // Do not do this flip. + delete[] fliptets; + return 0; + } + } + } // if (vt == FREEFACETVERTEX) + 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. + for (i = 3; i < 6; i++) { + if (issubface(fliptets[i])) valence++; + } + if (valence > 0) { + // 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]; + } + // [2018-03-08] Check if the last 4-to-1 flip is valid. + // fliptets[0],[1],[2] are [p,d,a,b],[p,d,b,c],[p,d,c,a] + triface checktet, chkface; + for (i = 0; i < 3; i++) { + enext(fliptets[i], checktet); + esymself(checktet); // [a,d,b,p],[b,d,c,p],[c,d,a,p] + int scount = 0; + int k; + for (k = 0; k < 3; k++) { + esym(checktet, chkface); + if (issubface(chkface)) scount++; + enextself(checktet); + } + if (scount == 3) { + break; // Found a tet which support a 3-to-1 flip. + } else if (scount == 2) { + // This is a strange configuration. Debug it. + // Do not do this flip. + delete[] fliptets; + return 0; + } + } + if (i == 3) { + // No tet in [p,d,a,b],[p,d,b,c],[p,d,c,a] support it. + int scount = 0; + for (i = 0; i < 3; i++) { + eprev(fliptets[i], checktet); + esymself(checktet); // [p,a,b,d],[p,b,c,d],[p,c,a,d] + if (issubface(chkface)) scount++; + } + if (scount != 3) { + // Do not do this flip. + delete[] fliptets; + return 0; + } + } + } // vt == FREEFACETVERTEX + // 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; + } + // 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]; + } + + 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; + // Insert the new segment. + point2tetorg(lpt, searchtet); + finddirection(&searchtet, rpt); + sstbond1(rightseg, searchtet); + spintet = searchtet; + while (1) { + 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); + } + 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); + } + 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++; + } + + return 1; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// suppressbdrysteinerpoint() Suppress a boundary Steiner point // +// // +/////////////////////////////////////////////////////////////////////////////// + +int tetgenmesh::suppressbdrysteinerpoint(point steinerpt) { + face parentsh, spinsh, *parysh; + face leftseg, rightseg; + point lpt = NULL, rpt = NULL; + int i; + + verttype vt = pointtype(steinerpt); + + if (vt == FREESEGVERTEX) { + sdecode(point2sh(steinerpt), leftseg); + leftseg.shver = 0; + if (sdest(leftseg) == steinerpt) { + senext(leftseg, rightseg); + spivotself(rightseg); + rightseg.shver = 0; + } else { + rightseg = leftseg; + senext2(rightseg, leftseg); + spivotself(leftseg); + leftseg.shver = 0; + } + 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); + if (parentsh.sh != NULL) { + // It is not a dangling segment. + 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; + } + + 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)); + 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)); + 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); + break; + } + } + } + } // j + if (j == cavetetlist->objects) { + break; // There is no intersection!! Debug is needed. + } + // 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; + } + // 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); + { + // [2017-10-15] Rounding + REAL lab = distance(pa, pb); + REAL lbc = distance(pb, pc); + REAL lca = distance(pc, pa); + REAL lv = (lab + lbc + lca) / 3.0; + REAL l3 = lv * lv * lv; + if (fabs(ori) / l3 < 1e-8) ori = 0.0; + } + 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; + } + // 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]); + } + } + delete[] newsteiners; + cavesegshlist->restart(); + return 0; + } + + // 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; + + 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 (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); + } + splitseg = NULL; + } + sremovevertex(steinerpt, &parentsh, splitseg, slawson); + + if (vt == FREESEGVERTEX) { + // The original segment is returned in 'rightseg'. + rightseg.shver = 0; + } + + // 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); + 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; + } + } + + // 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); + } else { + // Search for an open face at this edge. + spintet = neightet; + while (1) { + esym(spintet, searchtet); + fsym(searchtet, spintet); + if (spintet.tet == NULL) break; + } + // Found an open face at 'searchtet'. + neightet = searchtet; + } + } else { + // The edge (at 'newsh') is a dangling segment. + // Get an adjacent tet at this segment. + sstpivot1(checkseg, neightet); + if (org(neightet) != sdest(newsh)) esymself(neightet); + // Search for an open face at this edge. + spintet = neightet; + while (1) { + esym(spintet, searchtet); + fsym(searchtet, spintet); + if (spintet.tet == NULL) break; + } + // 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. + 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. + } + } + } + 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 (caveoldtetlist->objects > 0l) { + // Delete hull tets which contain 'steinerpt'. + for (i = 0; i < caveoldtetlist->objects; i++) { + parytet = (triface*)fastlookup(caveoldtetlist, i); + tetrahedrondealloc(parytet->tet); + } + // Must update the hullsize. + hullsize -= caveoldtetlist->objects; + caveoldtetlist->restart(); + } - oldsh = * (face *) fastlookup(missingshs, 0); + 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. - // 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)); - } - if (useinsertradius) { - setfacetindex(newsh, getfacetindex(oldsh)); - } - // Connect the new subface to adjacent tets. - tsbond(*midface, newsh); - fsym(*midface, neightet); - sesymself(newsh); - tsbond(neightet, newsh); - } + point* parypt; + int steinercount = 0; - // 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. - // 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); - } // while (1) - } // if (casout.sh == NULL) - enextself(*midface); - } // j - } // i + int bak_fliplinklevel = b->fliplinklevel; + b->fliplinklevel = 100000; // Unlimited flip level. - // Delete old subfaces. - for (i = 0; i < missingshs->objects; i++) { - parysh = (face *) fastlookup(missingshs, i); - shellfacedealloc(subfaces, parysh->sh); - } - } 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. - parysh = NULL; // Avoid a warning in MSVC - 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 { - terminatetetgen(this, 2); //assert(0); + // Try to remove newly added Steiner points. + for (i = 0; i < n; i++) { + if (newsteiners[i] != NULL) { + if (!removevertexbyflips(newsteiners[i])) { + if (b->supsteiner_level > 0) { // Not -Y/0 + // Save it in subvertstack for removal. + subvertstack->newindex((void**)&parypt); + *parypt = newsteiners[i]; + } + steinercount++; + } } - } else { - //terminatetetgen(this, 2); // Report a bug - } } - midfaces->restart(); - } else { - mflag = true; - } + b->fliplinklevel = bak_fliplinklevel; - // Delete the temp subfaces. - for (j = 0; j < 2; j++) { - cavshells = (j == 0 ? topshells : botshells); - if (cavshells != NULL) { - for (i = 0; i < cavshells->objects; i++) { - parysh = (face *) fastlookup(cavshells, i); - shellfacedealloc(subfaces, parysh->sh); - } + if (steinercount > 0) { + if (b->verbose > 2) { + printf(" Added %d interior Steiner points.\n", steinercount); + } } - } - topshells->restart(); - if (botshells != NULL) { - botshells->restart(); - } + delete[] newsteiners; - return mflag; + return 1; } /////////////////////////////////////////////////////////////////////////////// // // -// carvecavity() Delete old tets and outer new tets of the cavity. // +// suppresssteinerpoints() Suppress Steiner points. // +// // +// 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. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::carvecavity(arraypool *crosstets, arraypool *topnewtets, - arraypool *botnewtets) -{ - arraypool *newtets; - shellface *sptr, *ssptr; - triface *parytet, *pnewtet, newtet, neightet, spintet; - face checksh, *parysh; - face checkseg, *paryseg; - int t1ver; - int i, j; - - 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. - - // Collect all subfaces and segments which attached to the old tets. - for (i = 0; i < crosstets->objects; i++) { - parytet = (triface *) fastlookup(crosstets, i); - if ((sptr = (shellface*) parytet->tet[9]) != NULL) { - for (j = 0; j < 4; j++) { - if (sptr[j]) { - sdecode(sptr[j], checksh); - if (!sinfected(checksh)) { - sinfect(checksh); - cavetetshlist->newindex((void **) &parysh); - *parysh = checksh; - } +int tetgenmesh::suppresssteinerpoints() { + + if (!b->quiet) { + printf("Suppressing Steiner points ...\n"); + } + + point rempt, *parypt; + + 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++; + } + } + } + } // i + + if (suppcount > 0) { + if (b->verbose) { + printf(" Suppressed %d boundary Steiner points.\n", suppcount); } - } // j } - if ((ssptr = (shellface*) parytet->tet[8]) != NULL) { - for (j = 0; j < 6; j++) { - if (ssptr[j]) { - sdecode(ssptr[j], checkseg); - // Skip a deleted segment (was a faked segment) - if (checkseg.sh[3] != NULL) { - if (!sinfected(checkseg)) { - sinfect(checkseg); - cavetetseglist->newindex((void **) &paryseg); - *paryseg = checkseg; + + if (b->supsteiner_level > 0) { // -Y/1 + 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++; + } + } } - } } - } // 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); - 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 + if (remcount > 0) { + if (b->verbose) { + printf(" Removed %d interior Steiner points.\n", remcount); + } + } + + b->fliplinklevel = bak_fliplinklevel; - cavetetshlist->restart(); - cavetetseglist->restart(); + if (b->supsteiner_level > 1) { // -Y/2 + // Smooth interior Steiner points. + optparameters opm; + triface* parytet; + point* ppt; + REAL ori; + int smtcount, count, ivcount; + int nt, j; - // Delete the old tets in cavity. - for (i = 0; i < crosstets->objects; i++) { - parytet = (triface *) fastlookup(crosstets, i); - if (ishulltet(*parytet)) { - hullsize--; - } - tetrahedrondealloc(parytet->tet); - } + // Point smooth options. + opm.max_min_volume = 1; + opm.numofsearchdirs = 20; + opm.searchstep = 0.001; + opm.maxiter = 30; // Limit the maximum iterations. - crosstets->restart(); // crosstets will be re-used. + smtcount = 0; - // 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 < newtets->objects; i++) { - parytet = (triface *) fastlookup(newtets, i); - if (infected(*parytet)) { - crosstets->newindex((void **) &pnewtet); - *pnewtet = *parytet; - } - } // i - } - } // j + do { - // Now we collect all new tets in cavity. - for (i = 0; i < crosstets->objects; i++) { - parytet = (triface *) fastlookup(crosstets, i); - for (j = 0; j < 4; j++) { - decode(parytet->tet[j], neightet); - if (marktested(neightet)) { // Is it a new tet? - if (!infected(neightet)) { - // Find an interior tet. - //assert((point) neightet.tet[7] != dummypoint); // SELF_CHECK - infect(neightet); - crosstets->newindex((void **) &pnewtet); - *pnewtet = neightet; + 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"); } - } - } // j - } // i - - parytet = (triface *) fastlookup(crosstets, 0); - recenttet = *parytet; // Remember a live handle. - - // Delete outer new tets. - for (j = 0; j < 2; j++) { - newtets = (j == 0 ? topnewtets : botnewtets); - if (newtets != NULL) { - 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); + + if (b->verbose) { + if (smtcount > 0) { + printf(" Smoothed %d Steiner points.\n", smtcount); + } } - } - } - } + } // -Y2 + + subvertstack->restart(); - crosstets->restart(); - topnewtets->restart(); - if (botnewtets != NULL) { - botnewtets->restart(); - } + return 1; } /////////////////////////////////////////////////////////////////////////////// // // -// restorecavity() Reconnect old tets and delete new tets of the cavity. // +// recoverboundary() Recover segments and facets. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::restorecavity(arraypool *crosstets, arraypool *topnewtets, - arraypool *botnewtets, arraypool *missingshbds) -{ - triface *parytet, neightet, spintet; - face *parysh; - face checkseg; - point *ppt; - int t1ver; - int i, j; - - // Reconnect crossing tets to cavity boundary. - for (i = 0; i < crosstets->objects; i++) { - parytet = (triface *) fastlookup(crosstets, i); - parytet->ver = 0; - for (parytet->ver = 0; parytet->ver < 4; parytet->ver++) { - fsym(*parytet, neightet); - if (!infected(neightet)) { - // Restore the old connections of tets. - bond(*parytet, neightet); - } +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"); } - // Update the point-to-tet map. - parytet->ver = 0; - ppt = (point *) &(parytet->tet[4]); - for (j = 0; j < 4; j++) { - setpoint2tet(ppt[j], encode(*parytet)); - } - } - - // Uninfect all crossing tets. - for (i = 0; i < crosstets->objects; i++) { - parytet = (triface *) fastlookup(crosstets, i); - uninfect(*parytet); - } - - // Remember a live handle. - if (crosstets->objects > 0) { - recenttet = * (triface *) fastlookup(crosstets, 0); - } - - // Delete faked segments. - for (i = 0; i < missingshbds->objects; i++) { - parysh = (face *) fastlookup(missingshbds, i); - sspivot(*parysh, checkseg); - if (checkseg.sh[3] != NULL) { - if (sinfected(checkseg)) { - // It's a faked segment. Delete it. - sstpivot1(checkseg, neightet); - spintet = neightet; - while (1) { - tssdissolve1(spintet); - fnextself(spintet); - if (spintet.tet == neightet.tet) break; - } - shellfacedealloc(subsegs, checkseg.sh); - ssdissolve(*parysh); - //checkseg.sh = NULL; - } - } - } // i - - // Delete new tets. - for (i = 0; i < topnewtets->objects; i++) { - parytet = (triface *) fastlookup(topnewtets, i); - tetrahedrondealloc(parytet->tet); - } - - if (botnewtets != NULL) { - for (i = 0; i < botnewtets->objects; i++) { - parytet = (triface *) fastlookup(botnewtets, i); - tetrahedrondealloc(parytet->tet); - } - } - - crosstets->restart(); - topnewtets->restart(); - if (botnewtets != NULL) { - botnewtets->restart(); - } -} -/////////////////////////////////////////////////////////////////////////////// -// // -// flipcertify() Insert a crossing face into priority queue. // -// // -// A crossing face of a facet must have at least one top and one bottom ver- // -// tex of the facet. // -// // -/////////////////////////////////////////////////////////////////////////////// + if (b->verbose) { + printf(" Recovering segments.\n"); + } -void tetgenmesh::flipcertify(triface *chkface,badface **pqueue,point plane_pa, - point plane_pb, point plane_pc) -{ - badface *parybf, *prevbf, *nextbf; - triface neightet; - face checksh; - point p[5]; - REAL w[5]; - REAL insph, ori4; - int topi, boti; - int i; - - // Compute the flip time \tau. - fsym(*chkface, neightet); - - p[0] = org(*chkface); - p[1] = dest(*chkface); - p[2] = apex(*chkface); - p[3] = oppo(*chkface); - p[4] = oppo(neightet); - - // 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++; + // Segments will be introduced. + checksubsegflag = 1; + + misseglist = new arraypool(sizeof(face), 8); + bdrysteinerptlist = new arraypool(sizeof(point), 8); + + // In random order. + subsegs->traversalinit(); + for (i = 0; i < subsegs->items; i++) { + s = randomnation(i + 1); + // Move the s-th seg to the i-th. + subsegstack->newindex((void**)&paryseg); + *paryseg = *(face*)fastlookup(subsegstack, s); + // Put i-th seg to be the s-th. + searchseg.sh = shellfacetraverse(subsegs); + paryseg = (face*)fastlookup(subsegstack, s); + *paryseg = searchseg; } - if ((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]); - 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; // Test: omit this face. - } - - // 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]; - } else { - w[i] = 0; + + // The init number of missing segments. + ms = subsegs->items; + nit = 0; + if (b->fliplinklevel < 0) { + autofliplinklevel = 1; // Init value. } - } - 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 (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])); - } - - 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 { + // 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 (misseglist->objects >= ms) { + nit++; + if (nit >= 3) { + // break; + // Do the last round with unbounded flip link level. + b->fliplinklevel = 100000; + } + } else { + ms = misseglist->objects; + if (nit > 0) { + nit--; + } + } + for (i = 0; i < misseglist->objects; i++) { + subsegstack->newindex((void**)&paryseg); + *paryseg = *(face*)fastlookup(misseglist, i); + } + misseglist->restart(); + autofliplinklevel += b->fliplinklevelinc; + } + } else { + // All segments are recovered. break; - } } - //} // if (!b->flipinsert_random) - // Insert the new item between prev and next items. - if (prevbf == NULL) { - *pqueue = parybf; - } else { - prevbf->nextitem = parybf; - } - parybf->nextitem = nextbf; - } - } else if (ori4 == 0) { - - } -} + } // while (1) -/////////////////////////////////////////////////////////////////////////////// -// // -// flipinsertfacet() Insert a facet into a CDT by flips. // -// // -// 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. // -// // -/////////////////////////////////////////////////////////////////////////////// + if (b->verbose) { + printf(" %ld (%ld) segments are recovered (missing).\n", + subsegs->items - misseglist->objects, misseglist->objects); + } -void tetgenmesh::flipinsertfacet(arraypool *crosstets, arraypool *toppoints, - arraypool *botpoints, arraypool *midpoints) -{ - arraypool *crossfaces, *bfacearray; - triface fliptets[6], baktets[2], fliptet, newface; - triface neightet, *parytet; - 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; - - f23count = flip23count; - f32count = flip32count; - f44count = flip44count; - - // Get three affinely independent vertices in the missing region R. - calculateabovepoint(midpoints, &plane_pa, &plane_pb, &plane_pc); - - // 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); - } - } - for (i = 0; i < botpoints->objects; i++) { - parypt = (point *) fastlookup(botpoints, i); - if (!pmarktested(*parypt)) { - pmarktest3(*parypt); - } - } - - // Collect crossing faces. - crossfaces = cavetetlist; // Re-use array 'cavetetlist'. - - // 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); - } - - if (b->verbose > 1) { - printf(" Found %ld crossing faces.\n", crossfaces->objects); - } - - for (i = 0; i < crosstets->objects; i++) { - parytet = (triface *) fastlookup(crosstets, i); - unmarktest(*parytet); - uninfect(*parytet); - } - - // Initialize the priority queue. - pqueue = NULL; - - for (i = 0; i < crossfaces->objects; i++) { - parytet = (triface *) fastlookup(crossfaces, i); - flipcertify(parytet, &pqueue, plane_pa, plane_pb, plane_pc); - } - crossfaces->restart(); - - // The list for temporarily storing unflipable faces. - bfacearray = new arraypool(sizeof(triface), 4); - - - fcount = 0; // Count the number of flips. - - // Flip insert the facet. - while (pqueue != NULL) { - - // Pop a face from the priority queue. - popbf = pqueue; - bface = *popbf; - // 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); - 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); - } - flipflag = 0; + 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(); - // Check for which type of flip can we do. - convcount = 3; - copcount = 0; - for (i = 0; i < 3; i++) { - 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); - } + recoversegments(misseglist, 1, 0); - if (convcount == 3) { - // A 2-to-3 flip is found. - 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) { - //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); + if (misseglist->objects < ms) { + // The number of missing segments is reduced. + continue; + } else { + break; } + } + if (b->verbose) { + printf(" %ld (%ld) segments are recovered (missing).\n", + subsegs->items - misseglist->objects, misseglist->objects); + } + } - // Collect tets sharing at this edge. - 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]); - } - 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 { - // 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; - } - } // 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); - terminatetetgen(this, 2); //assert(0); - } - } - - delete bfacearray; - - // 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); - } - - 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); - } -} + 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(); -/////////////////////////////////////////////////////////////////////////////// -// // -// insertpoint_cdt() Insert a new point into a CDT. // -// // -/////////////////////////////////////////////////////////////////////////////// + recoversegments(misseglist, 1, 1); -int tetgenmesh::insertpoint_cdt(point newpt, triface *searchtet, face *splitsh, - face *splitseg, insertvertexflags *ivf, - arraypool *cavpoints, arraypool *cavfaces, - arraypool *cavshells, arraypool *newtets, - arraypool *crosstets, arraypool *misfaces) -{ - triface neightet, *parytet; - face checksh, *parysh, *parysh1; - face *paryseg, *paryseg1; - point *parypt; - int t1ver; - int i; - - if (b->verbose > 2) { - printf(" Insert point %d into CDT\n", pointmark(newpt)); - } - - if (!insertpoint(newpt, searchtet, NULL, NULL, ivf)) { - // Point is not inserted. Check ivf->iloc for reason. - return 0; - } - - - 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; - - 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; - } - } - - 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). - tsdissolve(neightet); - fsymself(neightet); - tsdissolve(neightet); - } + if (misseglist->objects < ms) { + // The number of missing segments is reduced. + continue; + } else { + break; + } + } + if (b->verbose) { + printf(" Added %ld Steiner points in volume.\n", st_volref_count); } - } - shellfacedealloc(subfaces, parysh->sh); - } - if (splitseg != NULL) { - // Delete the old segment in sC(p). - shellfacedealloc(subsegs, splitseg->sh); } - // Clear working lists. - caveshlist->restart(); - caveshbdlist->restart(); - cavesegshlist->restart(); - } // if ((splitsh != NULL) || (splitseg != NULL)) - - // Put all interior subfaces into stack for recovery. - // They were collected in carvecavity(). - // Note: Some collected subfaces may be deleted by sinsertvertex(). - for (i = 0; i < caveencshlist->objects; i++) { - parysh = (face *) fastlookup(caveencshlist, i); - if (parysh->sh[3] != NULL) { - subfacstack->newindex((void **) &parysh1); - *parysh1 = *parysh; - } - } - - // 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; - } - } - - caveencshlist->restart(); - caveencseglist->restart(); - - return 1; -} + 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(); -/////////////////////////////////////////////////////////////////////////////// -// // -// refineregion() Refine a missing region by inserting points. // -// // -// 'splitsh' represents an edge of the facet to be split. It must be not a // -// segment. -// // -// Assumption: The current mesh is a CDT and is convex. // -// // -/////////////////////////////////////////////////////////////////////////////// + recoversegments(misseglist, 1, 2); -void tetgenmesh::refineregion(face &splitsh, arraypool *cavpoints, - arraypool *cavfaces, arraypool *cavshells, - arraypool *newtets, arraypool *crosstets, - arraypool *misfaces) -{ - triface searchtet, spintet; - face splitseg, *paryseg; - point steinpt, pa, pb, refpt; - insertvertexflags ivf; - enum interresult dir; - long baknum = points->items; - int t1ver; - int i; - - // Do not split a segment. - for (i = 0; i < 3; i++) { - sspivot(splitsh, splitseg); - if (splitseg.sh == NULL) break; - senextself(splitsh); - } - - 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; - ivf.smlenflag = useinsertradius; // Return the closet mesh vertex. - - 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. - 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)) { - terminatetetgen(this, 2); - } - if (useinsertradius) { - save_segmentpoint_insradius(steinpt, ivf.parentpt, ivf.smlen); - } - st_segref_count++; - if (steinerleft > 0) steinerleft--; - } else { - terminatetetgen(this, 2); // assert(0); + 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); + } + } } - } else { - if (useinsertradius) { - save_facetpoint_insradius(steinpt, ivf.parentpt, ivf.smlen); + + 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; + } + } + 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 ((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))); + } + } + } + subvertstack->restart(); + } + + tv = clock(); + + if (b->verbose) { + printf(" Recovering facets.\n"); } - st_facref_count++; - if (steinerleft > 0) steinerleft--; - } - while (subsegstack->objects > 0l) { - // seglist is used as a stack. - subsegstack->objects--; - paryseg = (face *) fastlookup(subsegstack, subsegstack->objects); - splitseg = *paryseg; + // Subfaces will be introduced. + checksubfaceflag = 1; - // Check if this segment has been recovered. - sstpivot1(splitseg, searchtet); - if (searchtet.tet != NULL) continue; + misshlist = new arraypool(sizeof(face), 8); - // Search the segment. - dir = scoutsegment(sorg(splitseg), sdest(splitseg), &splitseg, &searchtet, - &refpt, NULL); - if (dir == SHAREEDGE) { - // Found this segment, insert it. - // 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 { - if ((dir == ACROSSFACE) || (dir == ACROSSEDGE)) { - // Split the segment. - makepoint(&steinpt, 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)) { - terminatetetgen(this, 2); - } - if (useinsertradius) { - save_segmentpoint_insradius(steinpt, ivf.parentpt, ivf.smlen); - } - st_segref_count++; - if (steinerleft > 0) steinerleft--; - } else { - terminatetetgen(this, 2); - } + // 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; } - } // while - if (b->verbose > 2) { - printf(" Added %ld Steiner points.\n", points->items - baknum); - } -} + ms = subfaces->items; + nit = 0; + b->fliplinklevel = -1; // Init. + if (b->fliplinklevel < 0) { + autofliplinklevel = 1; // Init value. + } -/////////////////////////////////////////////////////////////////////////////// -// // -// constrainedfacets() Recover constrained facets in a CDT. // -// // -// All unrecovered subfaces are queued in 'subfacestack'. // -// // -/////////////////////////////////////////////////////////////////////////////// + while (1) { + recoversubfaces(misshlist, 0); -void tetgenmesh::constrainedfacets() -{ - 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; - - // 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); - if (!smarktested(searchsh)) { - if (!isshtet(searchsh)) { - smarktest(searchsh); - tg_facfaces->newindex((void **) &parysh1); - *parysh1 = searchsh; + if (misshlist->objects > 0) { + if (b->fliplinklevel >= 0) { + break; + } else { + if (misshlist->objects >= ms) { + nit++; + if (nit >= 3) { + // break; + // Do the last round with unbounded flip link level. + b->fliplinklevel = 100000; + } + } else { + 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; } - } + } else { + // All subfaces are recovered. + break; } - 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); - } + } // while (1) - if (b->verbose > 1) { - printf(" Recovering facet #%d: %ld subfaces.\n", facetcount + 1, - tg_facfaces->objects); + if (b->verbose) { + printf(" %ld (%ld) subfaces are recovered (missing).\n", + subfaces->items - misshlist->objects, misshlist->objects); } - facetcount++; - while (tg_facfaces->objects > 0l) { - - tg_facfaces->objects--; - parysh = (face *) fastlookup(tg_facfaces, tg_facfaces->objects); - searchsh = *parysh; + 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(); - if (searchsh.sh[3] == NULL) continue; // It is dead. - if (isshtet(searchsh)) continue; // It is recovered. + recoversubfaces(NULL, 1); - searchtet.tet = NULL; - if (scoutsubface(&searchsh, &searchtet, 1)) continue; + if (b->verbose) { + printf(" Added %ld Steiner points in facets.\n", st_facref_count); + } + } - // The subface is missing. Form the missing region. - // Re-use 'tg_crosstets' for 'adjtets'. - formregion(&searchsh, tg_missingshs, tg_missingshbds, tg_missingshverts); + 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 (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(); + } - int searchflag = scoutcrossedge(searchtet, tg_missingshbds, tg_missingshs); - if (searchflag > 0) { - // 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); - // Put all subfaces in R back to tg_facfaces. - for (i = 0; i < tg_missingshs->objects; i++) { - parysh = (face *) fastlookup(tg_missingshs, i); - tg_facfaces->newindex((void **) &parysh1); - *parysh1 = *parysh; - } - success = 1; - // Clear working lists. - tg_crosstets->restart(); - tg_topfaces->restart(); - tg_botfaces->restart(); - tg_toppoints->restart(); - tg_botpoints->restart(); - } // b->flipinsert - - if (success) { - // Recover interior subfaces. - for (i = 0; i < caveencshlist->objects; i++) { - parysh = (face *) fastlookup(caveencshlist, i); - if (!scoutsubface(parysh, &searchtet, 1)) { - // 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), paryseg, - &searchtet, NULL, NULL); - if (dir != SHAREEDGE) { - terminatetetgen(this, 2); - } - // Insert this segment. - // 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); - } - caveencseglist->restart(); - } // success - remesh cavity - } // success - form cavity - else { - terminatetetgen(this, 2); // Report a bug. - } // Not success - form cavity - } else { - // Put all subfaces in R back to tg_facfaces. - for (i = 0; i < tg_missingshs->objects; i++) { - parysh = (face *) fastlookup(tg_missingshs, i); - tg_facfaces->newindex((void **) &parysh1); - *parysh1 = *parysh; - } - if (searchflag != -1) { - // Some edge(s) in the missing regions were flipped. - success = 1; - } else { - restorecavity(tg_crosstets, tg_topnewtets, tg_botnewtets, - tg_missingshbds); // Only remove fake segments. - // Choose an edge to split (set in recentsh) - recentsh = searchsh; - success = 0; // Do refineregion(); - } - } // if (scoutcrossedge) - - // 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); - } - } // 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; - encseglist = NULL; -} + if (bdrysteinerptlist->objects > 0) { + if (b->verbose) { + printf(" %ld Steiner points remained in boundary.\n", bdrysteinerptlist->objects); + } + } // if -/////////////////////////////////////////////////////////////////////////////// -// // -// constraineddelaunay() Create a constrained Delaunay tetrahedralization.// -// // -/////////////////////////////////////////////////////////////////////////////// + // Accumulate the dynamic memory. + totalworkmemory += + (misseglist->totalmemory + misshlist->totalmemory + bdrysteinerptlist->totalmemory); -void tetgenmesh::constraineddelaunay(clock_t& tv) -{ - face searchsh, *parysh; - face searchseg, *paryseg; - int s, i; - - // Statistics. - long bakfillregioncount; - long bakcavitycount, bakcavityexpcount; - long bakseg_ref_count; - - if (!b->quiet) { - printf("Constrained Delaunay...\n"); - } - - makesegmentendpointsmap(); - makefacetverticesmap(); - - if (b->verbose) { - printf(" Delaunizing segments.\n"); - } - - 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; - } - - // Recover non-Delaunay segments. - delaunizesegments(); - - if (b->verbose) { - printf(" Inserted %ld Steiner points.\n", st_segref_count); - } - - tv = clock(); - - if (b->verbose) { - printf(" Constraining facets.\n"); - } - - // Subfaces will be introduced. - checksubfaceflag = 1; - - bakfillregioncount = fillregioncount; - bakcavitycount = cavitycount; - bakcavityexpcount = cavityexpcount; - bakseg_ref_count = st_segref_count; - - // 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; - } - - // Recover facets. - constrainedfacets(); - - 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); - } - } + delete bdrysteinerptlist; + delete misseglist; + delete misshlist; } //// //// //// //// -//// constrained_cxx ////////////////////////////////////////////////////////// - //// steiner_cxx ////////////////////////////////////////////////////////////// + +//// reconstruct_cxx ////////////////////////////////////////////////////////// //// //// //// //// /////////////////////////////////////////////////////////////////////////////// // // -// checkflipeligibility() A call back function for boundary recovery. // -// // -// 'fliptype' indicates which elementary flip will be performed: 1 : 2-to-3, // -// and 2 : 3-to-2, respectively. // -// // -// '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'. // +// carveholes() Remove tetrahedra not in the mesh domain. // // // /////////////////////////////////////////////////////////////////////////////// -int tetgenmesh::checkflipeligibility(int fliptype, point pa, point pb, - point pc, point pd, point pe, - int level, int edgepivot, - flipconstraints* fc) -{ - point tmppts[3]; - enum interresult dir; - int types[2], poss[4]; - int intflag; - int rejflag = 0; - int i; - - if (fc->seg[0] != NULL) { - // A constraining edge is given (e.g., for edge recovery). - if (fliptype == 1) { - // A 2-to-3 flip: [a,b,c] => [e,d,a], [e,d,b], [e,d,c]. - 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; - } - } - } 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; - } - } - } - } // 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. - } +void tetgenmesh::carveholes() { + 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->quiet) { + if (b->convex) { + printf("Marking exterior tetrahedra ...\n"); + } else { + printf("Removing exterior tetrahedra ...\n"); } - } // 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. - // 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 { - // 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; + // 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.ver = 11; // The face opposite to dummypoint. + tetloop.tet = alltetrahedrontraverse(); + while (tetloop.tet != (tetrahedron*)NULL) { + 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; + } } - } } - } // 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. - 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; + 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); + } + *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; + } + } + 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 { - // Record the largest new angle. - if (cosmaxd < fc->cosdihed_out) { - fc->cosdihed_out = cosmaxd; - } + // 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"); + } + } + } // i + } // if (in->numberofholes > 0) + + if (b->hole_mesh && (b->hole_mesh_filename[0] != 0)) { + // A hole mesh (***.ele) is given. + // enum tetgenbehavior::objecttype object; + char filebasename[256]; + strcpy(filebasename, b->hole_mesh_filename); + // object = tetgenbehavior::MESH; + if (!strcmp(&filebasename[strlen(filebasename) - 4], ".ele")) { + filebasename[strlen(filebasename) - 4] = '\0'; + // object = tetgenbehavior::MESH; + } + bool hole_mesh_loaded = false; + tetgenio io; + if (io.load_node(filebasename)) { + if (io.load_tet(filebasename)) { + hole_mesh_loaded = true; } - } } - } else { // level > 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; - } + if (hole_mesh_loaded) { + if (b->verbose) { + printf(" Adding hole tets from the mesh %s\n", b->hole_mesh_filename); } - } - } else { - // 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; + int count = 0, hcount = 0, scount = 0; + int shift = io.firstnumber > 0 ? -1 : 0; + double *p1, *p2, *p3, *p4; + double searchpt[3]; + for (i = 0; i < io.numberoftetrahedra; i++) { + int* idx = &(io.tetrahedronlist[i * 4]); + p1 = &(io.pointlist[(idx[0] + shift) * 3]); + p2 = &(io.pointlist[(idx[1] + shift) * 3]); + p3 = &(io.pointlist[(idx[2] + shift) * 3]); + p4 = &(io.pointlist[(idx[3] + shift) * 3]); + for (j = 0; j < 3; j++) { + searchpt[j] = (p1[j] + p2[j] + p3[j] + p4[j]) / 4.; + } + // Search the point. + neightet.tet = NULL; + if (locate(searchpt, &neightet) != OUTSIDE) { + // The tet 'neightet' contain this point. + if (!infected(neightet)) { + infect(neightet); + tetarray->newindex((void**)&parytet); + *parytet = neightet; + count++; + // 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); + hcount++; + } else { + tetarray->newindex((void**)&parytet); + count++; + } + *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; + hcount++; + } + } + 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; + scount++; + } + } + } + } + } // i + if (b->verbose) { + printf(" Added %d hole tets, %d hull tet, %d hole subfaces\n", count, hcount, + scount); + } + } // if (hole_mesh_loaded) + } + + 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 { - // Record the largest new angle. - if (cosmaxd < fc->cosdihed_out) { - fc->cosdihed_out = cosmaxd; - } + 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; } - } - } // edgepivot - } // level + } } - } - return rejflag; -} + // 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). + 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 -/////////////////////////////////////////////////////////////////////////////// -// // -// removeedgebyflips() Attempt to remove an edge by flips. // -// // -// 'flipedge' is a non-convex or flat edge [a,b,#,#] to be removed. // -// // -// The return value is a positive integer, it indicates whether the edge is // -// removed or not. A value "2" means the edge is removed, otherwise, the // -// edge is not removed and the value (must >= 3) is the current number of // -// tets in the edge star. // -// // -/////////////////////////////////////////////////////////////////////////////// + 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; + } + } + } -int tetgenmesh::removeedgebyflips(triface *flipedge, flipconstraints* fc) -{ - 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; - } - } - return 0; - } - } - - // Count the number of tets at edge [a,b]. - n = 0; - spintet = *flipedge; - while (1) { - n++; - fnextself(spintet); - if (spintet.tet == flipedge->tet) break; - } - if (n < 3) { - // It is only possible when the mesh contains inverted tetrahedra. - terminatetetgen(this, 2); // Report a bug - } - - if ((b->flipstarsize > 0) && (n > b->flipstarsize)) { - // The star size exceeds the limit. - return 0; // Do not flip it. - } - - // 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; - } - - - // Try to flip the edge (level = 0, edgepivot = 0). - nn = flipnm(abtets, n, 0, 0, fc); - - - 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]; - } - - // 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; - - delete [] abtets; - - return nn; -} + // 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->supsteiner_level > 0)) { // -Y/1 + // 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(); + } -/////////////////////////////////////////////////////////////////////////////// -// // -// removefacebyflips() Remove a face by flips. // -// // -// Return 1 if the face is removed. Otherwise, return 0. // -// // -// ASSUMPTIONS: // -// - 'flipface' must not be a subface. // -// - 'flipface' must not be a hull face. // -// // -/////////////////////////////////////////////////////////////////////////////// + if (!b->convex && (tetarray->objects > 0l)) { // No -c option. + // Remove exterior tets. Hull tets are updated. + arraypool* newhullfacearray; + triface hulltet, casface; + face segloop, *paryseg; + point pa, pb, pc; + long delsegcount = 0l; -int tetgenmesh::removefacebyflips(triface *flipface, flipconstraints* fc) -{ - triface fliptets[3], flipedge; - point pa, pb, pc, pd, pe; - REAL ori; - int reducflag = 0; - - 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] - } - } else { - enext(*flipface, flipedge); // [b,c] - } - } else { - flipedge = *flipface; // [a,b] - } + // Collect segments which point to infected tets. Some segments + // may get deleted after the removal of exterior tets. + subsegs->traversalinit(); + segloop.sh = shellfacetraverse(subsegs); + while (segloop.sh != NULL) { + sstpivot1(segloop, neightet); + if (infected(neightet)) { + subsegstack->newindex((void**)&paryseg); + *paryseg = segloop; + } + segloop.sh = shellfacetraverse(subsegs); + } - 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; - } - } + newhullfacearray = new arraypool(sizeof(triface), 10); - // Face is not removed. - return 0; -} + // 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 + + // 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; + } + 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; + + for (i = 0; i < subfacstack->objects; i++) { + parysh = (face*)fastlookup(subfacstack, i); + if (i == 0) { + if (b->verbose) { + printf("Warning: Removed an exterior face (%d, %d, %d) #%d\n", + pointmark(sorg(*parysh)), pointmark(sdest(*parysh)), + pointmark(sapex(*parysh)), shellmark(*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; + } + if (casingin.sh != casingout.sh) { + // Update the link: ... -> casingin -> casingout ->... + sbond1(casingin, casingout); + } else { + // Only one subface at this edge is left. + sdissolve(casingout); + } + if (checkseg.sh != NULL) { + // Make sure the segment does not connect to a dead one. + ssbond(casingout, checkseg); + } + } else { + if (checkseg.sh != NULL) { + // if (checkseg.sh[3] != NULL) { + if (delsegcount == 0) { + if (b->verbose) { + printf("Warning: Removed an exterior segment (%d, %d) #%d\n", + pointmark(sorg(checkseg)), pointmark(sdest(checkseg)), + shellmark(checkseg)); + } + } + shellfacedealloc(subsegs, checkseg.sh); + delsegcount++; + } + } + senextself(*parysh); + } // j + // Delete this subface. + shellfacedealloc(subfaces, parysh->sh); + } // i + if (b->verbose) { + printf(" Deleted %ld subfaces.\n", subfacstack->objects); + } + subfacstack->restart(); + } // if (subfacstack->objects > 0l) + + if (subsegstack->objects > 0l) { + for (i = 0; i < subsegstack->objects; i++) { + paryseg = (face*)fastlookup(subsegstack, i); + if (paryseg->sh && (paryseg->sh[3] != NULL)) { + sstpivot1(*paryseg, neightet); + if (infected(neightet)) { + if (b->verbose) { + printf("Warning: Removed an exterior segment (%d, %d) #%d\n", + pointmark(sorg(*paryseg)), pointmark(sdest(*paryseg)), + shellmark(*paryseg)); + } + shellfacedealloc(subsegs, paryseg->sh); + delsegcount++; + } + } + } + subsegstack->restart(); + } // if (subsegstack->objects > 0l) -/////////////////////////////////////////////////////////////////////////////// -// // -// 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. // -// // -// The parameter 'sedge' is used to report self-intersection. If it is not // -// a NULL, it is EITHER a segment OR a subface that contains this edge. // -// // -// Note that this routine assumes that the tetrahedralization is convex. // -// // -/////////////////////////////////////////////////////////////////////////////// + if (delsegcount > 0) { + if (b->verbose) { + printf(" Deleted %ld segments.\n", delsegcount); + } + } -int tetgenmesh::recoveredgebyflips(point startpt, point endpt, face *sedge, - triface* searchtet, int fullsearch) -{ - flipconstraints fc; - enum interresult dir; + 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 { + st_volref_count--; + } + delsteinercount++; + if (steinerleft > 0) steinerleft++; + } + setpointtype(*parypt, UNUSEDVERTEX); + unuverts++; + } + } + + 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) - fc.seg[0] = startpt; - fc.seg[1] = endpt; - fc.checkflipeligibility = 1; + // Update the hull size. + hullsize += (newhullfacearray->objects - hullarray->objects); - // The mainloop of the edge reocvery. - while (1) { // Loop I + // 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(); - // 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 { - if (sedge) { - return report_selfint_edge(startpt, endpt, sedge, searchtet, dir); - } else { - return 0; + for (i = 0; i < hullarray->objects; i++) { + parytet = (triface*)fastlookup(hullarray, i); + tetrahedrondealloc(parytet->tet); } - } - } + hullarray->restart(); - // The edge is missing. + delete newhullfacearray; + } // if (!b->convex && (tetarray->objects > 0l)) - // Try to remove the first intersecting face/edge. - enextesymself(*searchtet); // Go to the opposite face. - if (dir == ACROSSFACE) { - if (checksubfaceflag) { - if (issubface(*searchtet)) { - if (sedge) { - return report_selfint_edge(startpt, endpt, sedge, searchtet, dir); - } else { - return 0; // Cannot flip a subface. - } - } - } - // Try to flip a crossing face. - if (removefacebyflips(searchtet, &fc)) { - continue; - } - } else if (dir == ACROSSEDGE) { - if (checksubsegflag) { - if (issubseg(*searchtet)) { - if (sedge) { - return report_selfint_edge(startpt, endpt, sedge, searchtet, dir); - } else { - return 0; // Cannot flip a segment. - } + if (b->convex && (tetarray->objects > 0l)) { // With -c option + // In this case, all exterior tets get a region marker '-1'. + int attrnum = numelemattrib - 1; + + for (i = 0; i < tetarray->objects; i++) { + parytet = (triface*)fastlookup(tetarray, i); + setelemattribute(parytet->tet, attrnum, -1); } - } - // Try to flip an intersecting edge. - if (removeedgebyflips(searchtet, &fc) == 2) { - continue; - } - } + tetarray->restart(); - // The edge is missing. + for (i = 0; i < hullarray->objects; i++) { + parytet = (triface*)fastlookup(hullarray, i); + uninfect(*parytet); + } + hullarray->restart(); - 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; + if (subfacstack->objects > 0l) { + for (i = 0; i < subfacstack->objects; i++) { + parysh = (face*)fastlookup(subfacstack, i); + suninfect(*parysh); + } + subfacstack->restart(); + } - // Loop through the sequence of intersecting faces/edges from - // 'startpt' to 'endpt'. - point2tetorg(startpt, *searchtet); - dir = finddirection(searchtet, endpt); + if (cavetetvertlist->objects > 0l) { + cavetetvertlist->restart(); + } + } // if (b->convex && (tetarray->objects > 0l)) - // 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. + 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); + } + 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; + } + } + } // k + } // j + regioncount++; + } // if (regiontets[i/5].tet != NULL) + } // i + } + + // Set attributes for all tetrahedra. + attr = maxattr + 1; + tetrahedrons->traversalinit(); + tetloop.tet = tetrahedrontraverse(); + while (tetloop.tet != (tetrahedron*)NULL) { + if (!infected(tetloop)) { + // An unmarked region. + tetarray->restart(); // Re-use this array. + infect(tetloop); + tetarray->newindex((void**)&parytet); + *parytet = tetloop; + // Find and mark all tets. + for (j = 0; j < tetarray->objects; j++) { + parytet = (triface*)fastlookup(tetarray, j); + tetloop = *parytet; + setelemattribute(tetloop.tet, attrnum, attr); + for (k = 0; k < 4; k++) { + decode(tetloop.tet[k], neightet); + // Is the adjacent tet already checked? + if (!infected(neightet)) { + // Is this side protected by a subface? + if (!issubface(neightet)) { + infect(neightet); + tetarray->newindex((void**)&parytet); + *parytet = neightet; + } + } + } // k + } // j + attr++; // Increase the attribute. + regioncount++; + } + tetloop.tet = tetrahedrontraverse(); + } + // Until here, every tet has a region attribute. - while (1) { // Loop I-I + // Uninfect processed tets. + tetrahedrons->traversalinit(); + tetloop.tet = tetrahedrontraverse(); + while (tetloop.tet != (tetrahedron*)NULL) { + uninfect(tetloop); + tetloop.tet = tetrahedrontraverse(); + } - // 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; + if (b->verbose) { + // assert(regioncount > 0); + if (regioncount > 1) { + printf(" Found %d subdomains.\n", regioncount); } else { - dir = DISJOINT; - pos = 0; - } - } // i - // There must be an intersection face/edge. - if (dir == DISJOINT) { - terminatetetgen(this, 2); - } - } else if (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 + printf(" Found %d domain.\n", regioncount); } - // No intersection. Rotate to the next tet at the edge. - fnextself(*searchtet); - } // while (1) // Loop I-I-I - } else { - terminatetetgen(this, 2); // Report a bug } + } // if (b->regionattrib) + + if (regiontets != NULL) { + delete[] regiontets; + } + delete tetarray; + delete hullarray; + + if (!b->convex) { // No -c option + // The mesh is non-convex now. + nonconvex = 1; - // Adjust to the intersecting edge/vertex. - for (i = 0; i < pos; i++) { - enextself(neightet); + // 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(); } - 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 { - terminatetetgen(this, 2); // Report a bug - } + flipconstraints fc; + fc.enqflag = 2; + long sliver_peel_count = lawsonflip3d(&fc); + + if (sliver_peel_count > 0l) { + if (b->verbose) { + printf(" Removed %ld hull slivers.\n", sliver_peel_count); + } } + unflipqueue->restart(); + } // if (!b->convex) +} - // The next to be flipped face/edge. - *searchtet = neightet; +// [2018-07-30] +// Search a face with given indices (i,j,k). +// This function is only called when the default fast search fails. +// It is possible when there are non-manifold edges on the hull. +// On finish, tetloop return this face if it exists, otherwise, return 0. +int tetgenmesh::search_face(point pi, point pj, point pk, triface& tetloop) { + pinfect(pi); + pinfect(pj); + pinfect(pk); - // Bakup this face (tetrahedron). - bakface.forg = org(*searchtet); - bakface.fdest = dest(*searchtet); - bakface.fapex = apex(*searchtet); - bakface.foppo = oppo(*searchtet); + int t1ver; + triface t, t1; + point *pts, toppo; + int pcount = 0; - // Try to flip this intersecting face/edge. - if (dir == ACROSSFACE) { - if (checksubfaceflag) { - if (issubface(*searchtet)) { - if (sedge) { - return report_selfint_edge(startpt,endpt,sedge,searchtet,dir); - } else { - return 0; // Cannot flip a subface. - } + t.ver = t1.ver = 0; + tetrahedrons->traversalinit(); + t.tet = tetrahedrontraverse(); + while (t.tet != NULL) { + pts = (point*)t.tet; + pcount = 0; + if (pinfected(pts[4])) pcount++; + if (pinfected(pts[5])) pcount++; + if (pinfected(pts[6])) pcount++; + if (pinfected(pts[7])) pcount++; + + if (pcount == 3) { + // Found a tet containing this face. + for (t.ver = 0; t.ver < 4; t.ver++) { + toppo = oppo(t); + if (!pinfected(toppo)) break; } - } - if (removefacebyflips(searchtet, &fc)) { - success = 1; - break; // Loop I-I - } - } else if (dir == ACROSSEDGE) { - if (checksubsegflag) { - if (issubseg(*searchtet)) { - if (sedge) { - return report_selfint_edge(startpt,endpt,sedge,searchtet,dir); - } else { - return 0; // Cannot flip a segment. - } + int ii; + for (ii = 0; ii < 3; ii++) { + if (org(t) == pi) break; + enextself(t); } - } - if (removeedgebyflips(searchtet, &fc) == 2) { - success = 1; - break; // Loop I-I - } - } else if (dir == ACROSSVERT) { - if (sedge) { - //return report_selfint_edge(startpt, endpt, sedge, searchtet, dir); - terminatetetgen(this, 2); - } else { - return 0; - } - } else { - terminatetetgen(this, 2); - } - - // 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) { - if (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) { - // The original (intersecting) tet has been flipped. - searchtet->tet = NULL; - break; // Not find. - } - } - } + if (dest(t) == pj) { } else { - searchtet->tet = NULL; // Not find. + eprevself(t); + fsymself(t); } - } else { - searchtet->tet = NULL; // Not find. - } - if (searchtet->tet == NULL) { - success = 0; // This face/edge has been destroyed. - break; // Loop I-I - } + break; } - } // while (1) // Loop I-I - - if (success) { - // One of intersecting faces/edges is flipped. - continue; - } - - } // if (fullsearch) - - // The edge is missing. - break; // Loop I + t.tet = tetrahedrontraverse(); + } - } // while (1) // Loop I + puninfect(pi); + puninfect(pj); + puninfect(pk); - return 0; + if (t.tet != NULL) { + tetloop = t; + return 1; + } else { + return 0; + } } -/////////////////////////////////////////////////////////////////////////////// -// // -// add_steinerpt_in_schoenhardtpoly() Insert a Steiner point in a Schoen- // -// hardt polyhedron. // -// // -// '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. // -// // -/////////////////////////////////////////////////////////////////////////////// +int tetgenmesh::search_edge(point p0, point p1, triface& tetloop) { + triface t; + int ii; -int tetgenmesh::add_steinerpt_in_schoenhardtpoly(triface *abtets, int n, - int chkencflag) -{ - 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; - - - pc = apex(abtets[0]); // pc = p0 - pd = oppo(abtets[n-1]); // pd = p_(n-1) - - - // Find an optimial point in edge [c,d]. It is visible by all outer faces - // of 'abtets', and it maxmizes the min volume. - - // 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; - } - - 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 (maxminvol < minvol) { - maxminvol = minvol; - maxidx = it; - } + tetrahedrons->traversalinit(); + t.tet = tetrahedrontraverse(); + while (t.tet != NULL) { + for (ii = 0; ii < 6; ii++) { + t.ver = edge2ver[ii]; + if (((org(t) == p0) && (dest(t) == p1)) || ((org(t) == p1) && (dest(t) == p0))) { + // Found the tet. + tetloop = t; + return 1; + } + } + t.tet = tetrahedrontraverse(); } - } // it - if (maxminvol <= 0) { - cavetetlist->restart(); + tetloop.tet = NULL; 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; - } - - - // 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; - } } /////////////////////////////////////////////////////////////////////////////// // // -// add_steinerpt_in_segment() Add a Steiner point inside a segment. // +// reconstructmesh() Reconstruct a tetrahedral mesh. // // // /////////////////////////////////////////////////////////////////////////////// -int tetgenmesh::add_steinerpt_in_segment(face* misseg, int searchlevel) -{ - triface searchtet; - face *paryseg, candseg; - point startpt, endpt, pc, pd; - flipconstraints fc; - enum interresult dir; - REAL P[3], Q[3], tp, tq; - REAL len, smlen = 0, split = 0, split_q = 0; - int success; - int i; - - startpt = sorg(*misseg); - endpt = sdest(*misseg); - - fc.seg[0] = startpt; - fc.seg[1] = endpt; - fc.checkflipeligibility = 1; - fc.collectencsegflag = 1; - - point2tetorg(startpt, searchtet); - dir = finddirection(&searchtet, endpt); - // 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); - } else if (dir == ACROSSEDGE) { - // An edge is intersected with the segment. Try to flip it. - success = removeedgebyflips(&searchtet, &fc); - } - - 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; - } - } - } - } - - caveencseglist->restart(); - b->fliplinklevel = bak_fliplinklevel; - - if (split == 0) { - // Found no crossing segment. - return 0; - } - - face splitsh; - face splitseg; - point steinerpt, *parypt; - insertvertexflags ivf; +void tetgenmesh::reconstructmesh() { + 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 cosang_tol, cosang; + REAL n1[3], n2[3]; + int eextras, marker = 0; + int bondflag; + int t1ver; + int idx, i, j, k; - 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]); + if (!b->quiet) { + printf("Reconstructing mesh ...\n"); } - } else { // b->addsteiner_algo == 2 - for (i = 0; i < 3; i++) { - P[i] = startpt[i] + split * (endpt[i] - startpt[i]); + + if (b->convex) { // -c option. + // Assume the mesh is convex. Exterior tets have region attribute -1. + if (!(in->numberoftetrahedronattributes > 0)) { + terminatetetgen(this, 2); + } + } else { + // Assume the mesh is non-convex. + nonconvex = 1; } - pc = sorg(candseg); - pd = sdest(candseg); - for (i = 0; i < 3; i++) { - Q[i] = pc[i] + split_q * (pd[i] - pc[i]); + + // 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. } - makepoint(&steinerpt, FREEVOLVERTEX); - for (i = 0; i < 3; i++) { - steinerpt[i] = 0.5 * (P[i] + Q[i]); + + // Allocate an array that maps each vertex to its adjacent tets. + ver2tetarray = new tetrahedron[in->numberofpoints + 1]; + unuverts = in->numberofpoints; // All vertices are unused yet. + // for (i = 0; i < in->numberofpoints + 1; i++) { + for (i = in->firstnumber; i < in->numberofpoints + in->firstnumber; i++) { + ver2tetarray[i] = NULL; } - } - // 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; - } + // 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++]]; + if (pointtype(p[j]) == UNUSEDVERTEX) { + setpointtype(p[j], VOLVERTEX); // initial type. + unuverts--; + } + } + // 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 - 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 1; -} + // Remember a tet of the mesh. + recenttet = tetloop; -/////////////////////////////////////////////////////////////////////////////// -// // -// addsteiner4recoversegment() Add a Steiner point for recovering a seg. // -// // -/////////////////////////////////////////////////////////////////////////////// + // Create hull tets, create the point-to-tet map, and clean up the + // temporary spaces used in each tet. + hullsize = tetrahedrons->items; -int tetgenmesh::addsteiner4recoversegment(face* misseg, int splitsegflag) -{ - triface *abtets, searchtet, spintet; - face splitsh; - face *paryseg; - point startpt, endpt; - point pa, pb, pd, steinerpt, *parypt; - enum interresult dir; - insertvertexflags ivf; - int types[2], poss[4]; - int n, endi, success; - int t1ver; - int i; - - 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); - - 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); - if (tri_edge_test(pa, pb, pd, startpt, endpt, NULL, 1, types, poss)) { - break; // Found the edge. - } - enextself(spintet); - eprevself(searchtet); - } - esymself(searchtet); - } - - 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. - } - n++; // Count a tet in the star. - fnextself(spintet); - if (spintet.tet == searchtet.tet) break; - } - - 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); + 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. + esym(hulltet, face1); + bond(face1, face2); + } + enextself(hulltet); + } + } + // 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(); } - success = 0; + hullsize = tetrahedrons->items - hullsize; - if (dir == ACROSSFACE) { - // Find a Steiner points inside the polyhedron. - if (add_steinerpt_in_schoenhardtpoly(abtets, endi, 0)) { - success = 1; - } - } else if (dir == ACROSSEDGE) { - // PLC check. - if (issubseg(searchtet)) { - terminatetetgen(this, 2); - } - 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 ((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++; - } + // 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; // The default marker. + } + if (marker != 0) { + idx = i * 3; + for (j = 0; j < 3; j++) { + p[j] = idx2verlist[in->trifacelist[idx++]]; + } + // Search the subface. + bondflag = 0; + neighsh.sh = NULL; + // 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; + } + fnextself(tetloop); + if (apex(tetloop) == q[2]) break; + } + } + if (!bondflag) { + if (neighsh.sh == NULL) { + if (b->verbose > 1) { + printf("Warning: Searching subface #%d [%d,%d,%d] mark=%d.\n", + i + in->firstnumber, pointmark(p[0]), pointmark(p[1]), + pointmark(p[2]), marker); + } + // Search it globally. + if (search_face(p[0], p[1], p[2], tetloop)) { + bondflag = 1; + } + } + } + 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); + } + setshellmark(subloop, marker); + // Insert the subface into the mesh. + tsbond(tetloop, subloop); + fsymself(tetloop); + sesymself(subloop); + tsbond(tetloop, subloop); + } else { + if (neighsh.sh != NULL) { + // The subface already exists. Only set its mark. + setshellmark(neighsh, marker); + } else { + if (!b->quiet) { + printf("Warning: Subface #%d [%d,%d,%d] mark=%d is not found.\n", + i + in->firstnumber, pointmark(p[0]), pointmark(p[1]), + pointmark(p[2]), marker); + } + } + } // 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. + } + } + } + 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, -1); // Default marker. + // Insert the subface into the mesh. + tsbond(tetloop, subloop); + sesymself(subloop); + tsbond(checktet, subloop); + } // if (bondflag) + } // if (neighsh.sh == NULL) } - } 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); - } - } else { - terminatetetgen(this, 2); + tetloop.tet = tetrahedrontraverse(); } - delete [] abtets; - - if (success) { - // Add the missing segment back to the recovering list. - subsegstack->newindex((void **) &paryseg); - *paryseg = *misseg; - return 1; + // 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) { + // Do not connect itself. + if (nextsh.sh != neighsh.sh) { + // Link neighsh <= nextsh. + sbond1(neighsh, nextsh); + neighsh = nextsh; + } + } + if (apex(tetloop) == q[2]) { + break; + } + } // while (1) + } // if (neighsh.sh == NULL) + senextself(subloop); + } + subloop.sh = shellfacetraverse(subfaces); } - } // if (endi > 0) - - if (!splitsegflag) { - return 0; - } - - if (b->verbose > 2) { - printf(" Splitting segment (%d, %d)\n", pointmark(startpt), - pointmark(endpt)); - } - steinerpt = NULL; - 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; + // 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; // Default marker. + } + 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. + bondflag = 0; + if (j == 2) { + if (getedge(p[0], p[1], &checktet)) { + bondflag = 1; + } else { + if (b->verbose > 1) { + printf("Warning: Searching segment #%d [%d,%d] mark=%d.\n", + i + in->firstnumber, pointmark(p[0]), pointmark(p[1]), marker); + } + // Search it globally. + if (search_edge(p[0], p[1], checktet)) { + bondflag = 1; + } + } + } + if (bondflag > 0) { + // 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, 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_separate_ang_tol' (degree). + cosang_tol = cos(b->facet_separate_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); + facenormal(p[0], p[1], p[2], n1, 1, NULL); + facenormal(p[0], p[1], p[3], n2, 1, NULL); + cosang = dot(n1, n2) / (sqrt(dot(n1, n1)) * sqrt(dot(n2, n2))); + // Rounding. + if (cosang > 1.0) + cosang = 1.0; + else if (cosang < -1.0) + cosang = -1.0; + if (cosang > cosang_tol) { + 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, -1); // Default 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); } - sesymself(*misseg); - } - + // 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); + } + } + } - 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]); - } + // 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; + } + } + } + } - // We need to locate the point. - 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)) { - terminatetetgen(this, 2); + delete[] idx2seglist; + delete[] segperverlist; } - } // if (endi > 0) - // Save this Steiner point (for removal). - // Re-use the array 'subvertstack'. - subvertstack->newindex((void **) &parypt); - *parypt = steinerpt; + // Set global flags. + checksubsegflag = 1; + checksubfaceflag = 1; - st_segref_count++; - if (steinerleft > 0) steinerleft--; - - return 1; + delete[] idx2verlist; + delete[] ver2tetarray; } /////////////////////////////////////////////////////////////////////////////// // // -// recoversegments() Recover all segments. // +// scoutpoint() Search a point in mesh. // // // -// All segments need to be recovered are in 'subsegstack'. // +// 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. // // // -// This routine first tries to recover each segment by only using flips. If // -// no flip is possible, and the flag 'steinerflag' is set, it then tries to // -// insert Steiner points near or in the segment. // +// If 'randflag' is used, randomly select a start searching tet. Otherwise, // +// start searching directly from 'searchtet'. // // // /////////////////////////////////////////////////////////////////////////////// -int tetgenmesh::recoversegments(arraypool *misseglist, int fullsearch, - int steinerflag) -{ - 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(" Recover segments [%s level = %2d] #: %ld.\n", - (b->fliplinklevel > 0) ? "fixed" : "auto", - (b->fliplinklevel > 0) ? b->fliplinklevel : autofliplinklevel, - subsegstack->objects); - } - - // 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. - } - - startpt = sorg(sseg); - endpt = sdest(sseg); +int tetgenmesh::scoutpoint(point searchpt, triface* searchtet, int randflag) { + point pa, pb, pc, pd; + enum locateresult loc = OUTSIDE; + REAL vol, ori1, ori2 = 0, ori3 = 0, ori4 = 0; + int t1ver; - if (b->verbose > 2) { - printf(" Recover segment (%d, %d).\n", pointmark(startpt), - pointmark(endpt)); + // Randomly select a good starting tet. + if (randflag) { + randomsample(searchpt, searchtet); + } else { + if (searchtet->tet == NULL) { + *searchtet = recenttet; + } } + loc = locate(searchpt, searchtet); - success = 0; - - if (recoveredgebyflips(startpt, endpt, &sseg, &searchtet, 0)) { - success = 1; - } else { - // Try to recover it from the other direction. - if (recoveredgebyflips(endpt, startpt, &sseg, &searchtet, 0)) { - success = 1; - } + if (loc == OUTSIDE) { + if (b->convex) { // -c option + // The point lies outside of the convex hull. + return (int)loc; + } + // 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 (!success && fullsearch) { - if (recoveredgebyflips(startpt, endpt, &sseg, &searchtet, fullsearch)) { - success = 1; - } + 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 (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); + 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]. + 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 { - 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; - } - } - if (!success) { - if (misseglist != NULL) { - // Save this segment. - misseglist->newindex((void **) &paryseg); - *paryseg = sseg; - } - } + loc = OUTSIDE; } - } // while (subsegstack->objects > 0l) - - if (steinerflag) { - if (b->verbose > 1) { - // 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); - } - } - } - - return 0; + return (int)loc; } /////////////////////////////////////////////////////////////////////////////// // // -// recoverfacebyflips() Recover a face by flips. // -// // -// 'pa', 'pb', and 'pc' are the three vertices of this face. This routine // -// tries to recover it in the tetrahedral mesh. It is assumed that the three // -// edges, i.e., pa->pb, pb->pc, and pc->pa all exist. // -// // -// If the face is recovered, it is returned by 'searchtet'. // +// getpointmeshsize() Interpolate the mesh size at given point. // // // -// If 'searchsh' is not NULL, it is a subface to be recovered. Its vertices // -// must be pa, pb, and pc. It is mainly used to check self-intersections. // -// Another use of this subface is to split it when a Steiner point is found // -// inside this subface. // +// 'iloc' indicates the location of the point w.r.t. 'searchtet'. The size // +// is obtained by linear interpolation on the vertices of the tet. // // // /////////////////////////////////////////////////////////////////////////////// -int tetgenmesh::recoverfacebyflips(point pa, point pb, point pc, - face *searchsh, triface* searchtet) -{ - triface spintet, flipedge; - point pd, pe; - flipconstraints fc; - int types[2], poss[4], intflag; - int success; - int t1ver; - int i, j; - - - 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); - finddirection(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. - flipedge.tet = NULL; - // 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 the assumption that all edges of the face exist, they can - // only intersect at a single point. - if (intflag == 2) { - // Go to the edge [d,e]. - edestoppo(spintet, flipedge); // [d,e,a,b] - if (searchsh != NULL) { - // Check the intersection type. - if ((types[0] == (int) ACROSSFACE) || - (types[0] == (int) ACROSSEDGE)) { - // Check if [e,d] is a segment. - if (issubseg(flipedge)) { - return report_selfint_face(pa, pb, pc, searchsh, &flipedge, - intflag, types, poss); - } else { - // Check if [e,d] is an edge of a subface. - triface chkface = flipedge; - while (1) { - if (issubface(chkface)) break; - fsymself(chkface); - if (chkface.tet == flipedge.tet) break; - } - if (issubface(chkface)) { - // Two subfaces are intersecting. - return report_selfint_face(pa, pb, pc,searchsh,&chkface, - intflag, types, poss); - } - } - } else if (types[0] == TOUCHFACE) { - // This is possible when a Steiner point was added on it. - 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. A 1-to-3 flip. - 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). - 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. - return 1; - } else { - // Other cases may be due to a bug or a PLC error. - return report_selfint_face(pa, pb, pc, searchsh, &flipedge, - intflag, types, poss); - } - } else { - // The other intersection types: ACROSSVERT, TOUCHEDGE, - // SHAREVERTEX should not be possible or due to a PLC error. - return report_selfint_face(pa, pb, pc, searchsh, &flipedge, - intflag, types, poss); - } - } // if (searchsh != NULL) - } else { // intflag == 4. Coplanar case. - terminatetetgen(this, 2); +REAL tetgenmesh::getpointmeshsize(point searchpt, triface* searchtet, int iloc) { + point *pts, pa, pb, pc; + REAL volume, vol[4], wei[4]; + REAL size; + int i; + + size = 0; + + if (iloc == (int)INTETRAHEDRON) { + pts = (point*)&(searchtet->tet[4]); + // 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++) { + wei[i] = fabs(vol[i] / volume); + size += (wei[i] * pts[i][pointmtrindex]); } - break; - } // if (intflag > 0) } - fnextself(spintet); - if (spintet.tet == searchtet->tet) { - terminatetetgen(this, 2); - } - } // while (1) - // Try to flip the edge [d,e]. - if (removeedgebyflips(&flipedge, &fc) == 2) { - // A crossing edge is removed. - continue; - } - break; - } // while (1) - } // i - - return success; + } 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]; + } + } + + return size; } - + /////////////////////////////////////////////////////////////////////////////// // // -// recoversubfaces() Recover all subfaces. // +// interpolatemeshsize() Interpolate the mesh size from a background mesh // +// (source) to the current mesh (destination). // // // /////////////////////////////////////////////////////////////////////////////// -int tetgenmesh::recoversubfaces(arraypool *misshlist, int steinerflag) -{ - triface searchtet, neightet, spintet; - face searchsh, neighsh, neineish, *parysh; - face bdsegs[3]; - point startpt, endpt, apexpt, *parypt; - point steinerpt; - insertvertexflags ivf; - int success; - int t1ver; - int i, j; +void tetgenmesh::interpolatemeshsize() { + triface searchtet; + point ploop; + REAL minval = 0.0, maxval = 0.0; + int iloc; + int count; - 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); - } + if (!b->quiet) { + printf("Interpolating mesh size ...\n"); + } - // Loop until 'subfacstack' is empty. - while (subfacstack->objects > 0l) { + 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)); + } + } + ploop = pointtraverse(); + } - subfacstack->objects--; - parysh = (face *) fastlookup(subfacstack, subfacstack->objects); - searchsh = *parysh; + if (b->verbose) { + printf(" Interoplated %d points.\n", count); + if (nonregularcount > 0l) { + printf(" Performed %ld brute-force searches.\n", nonregularcount); + } + printf(" Size rangle [%.17g, %.17g].\n", minval, maxval); + } - if (searchsh.sh[3] == NULL) continue; // Skip a dead subface. + bgm->samples = baksmaples; + nonregularcount = bak_nonregularcount; +} - stpivot(searchsh, neightet); - if (neightet.tet != NULL) continue; // Skip a recovered subface. +/////////////////////////////////////////////////////////////////////////////// +// // +// insertconstrainedpoints() Insert a list of points into the mesh. // +// // +// Assumption: The bounding box of the insert point set should be no larger // +// than the bounding box of the mesh. (Required by point sorting). // +// // +/////////////////////////////////////////////////////////////////////////////// +void tetgenmesh::insertconstrainedpoints(point* insertarray, int arylen, int rejflag) { + triface searchtet, spintet; + face splitsh; + face splitseg; + insertvertexflags ivf; + flipconstraints fc; + int randflag = 0; + int t1ver; + int i; - if (b->verbose > 2) { - printf(" Recover subface (%d, %d, %d).\n",pointmark(sorg(searchsh)), - pointmark(sdest(searchsh)), pointmark(sapex(searchsh))); + if (b->verbose) { + printf(" Inserting %d constrained points\n", arylen); } - // 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) { - terminatetetgen(this, 2); - } - } 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); - finddirection(&searchtet, endpt); - if (dest(searchtet) == endpt) { - success = 1; - } else { - // The edge is missing. Try to recover it. - if (recoveredgebyflips(startpt, endpt, &searchsh, &searchtet, 0)) { - success = 1; - } else { - if (recoveredgebyflips(endpt, startpt, &searchsh, &searchtet, 0)) { - success = 1; - } - } + if (b->no_sort) { // -b/1 option. + if (b->verbose) { + printf(" Using the input order.\n"); } - 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); - ssdissolve(neineish); - spivot(neineish, neighsh); - if (neighsh.sh != NULL) { - ssdissolve(neighsh); - } - sstpivot1(bdsegs[j], searchtet); + } 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) { - tssdissolve1(spintet); - fnextself(spintet); - if (spintet.tet == searchtet.tet) break; + if (issubface(spintet)) { + tspivot(spintet, splitsh); + setpointtype(insertarray[i], FREEFACETVERTEX); + // ivf.rejflag |= 1; + break; + } + 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]); + } else if (ivf.iloc == (int)ONFACE) { + if (issubface(searchtet)) { + tspivot(searchtet, splitsh); + setpointtype(insertarray[i], FREEFACETVERTEX); + // ivf.rejflag |= 1; } - - 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)) { - terminatetetgen(this, 2); - } - // 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); - ssdissolve(neineish); - spivot(neineish, neighsh); - if (neighsh.sh != NULL) { - ssdissolve(neighsh); + // 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(); } - sstpivot1(bdsegs[j], neightet); - spintet = neightet; - while (1) { - tssdissolve1(spintet); - fnextself(spintet); - if (spintet.tet == neightet.tet) break; + // 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++; } - shellfacedealloc(subsegs, bdsegs[j].sh); + } else { + // Point is not inserted. + // pointdealloc(insertarray[i]); + setpointtype(insertarray[i], UNUSEDVERTEX); + unuverts++; + encseglist->restart(); + encshlist->restart(); } - } // j + } // i - 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; - } + delete encseglist; + delete encshlist; - 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)) { - terminatetetgen(this, 2); - } - // 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) { + 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); + } } - if (!success) { - if (misshlist != NULL) { - // Save this subface. - misshlist->newindex((void **) &parysh); - *parysh = searchsh; - } + nonregularcount = bak_nonregularcount; + samples = baksmaples; +} + +void tetgenmesh::insertconstrainedpoints(tetgenio* addio) { + point *insertarray, newpt; + REAL x, y, z, w; + int index, attribindex, mtrindex; + int arylen, i, j; + + if (!b->quiet) { + printf("Inserting constrained points ...\n"); + } + + insertarray = new point[addio->numberofpoints]; + arylen = 0; + index = 0; + attribindex = 0; + mtrindex = 0; + + for (i = 0; i < addio->numberofpoints; i++) { + x = addio->pointlist[index++]; + y = addio->pointlist[index++]; + z = addio->pointlist[index++]; + // Test if this point lies inside the bounding box. + if ((x < xmin) || (x > xmax) || (y < ymin) || (y > ymax) || (z < zmin) || (z > zmax)) { + if (b->verbose) { + printf("Warning: Point #%d lies outside the bounding box. Ignored\n", + i + in->firstnumber); + } + continue; + } + makepoint(&newpt, UNUSEDVERTEX); + newpt[0] = x; + newpt[1] = y; + newpt[2] = z; + // Read the point attributes. (Including point weights.) + for (j = 0; j < addio->numberofpointattributes; j++) { + newpt[3 + j] = addio->pointattributelist[attribindex++]; + } + // Read the point metric tensor. + for (j = 0; j < addio->numberofpointmtrs; j++) { + newpt[pointmtrindex + j] = addio->pointmtrlist[mtrindex++]; + } + if (b->weighted) { // -w option + if (addio->numberofpointattributes > 0) { + // The first point attribute is its weight. + w = newpt[3]; + } else { + // No given weight available. Default choose the maximum + // absolute value among its coordinates. + w = fabs(x); + if (w < fabs(y)) w = fabs(y); + if (w < fabs(z)) w = fabs(z); + } + if (b->weighted_param == 0) { + newpt[3] = x * x + y * y + z * z - w; // Weighted DT. + } else { // -w1 option + newpt[3] = w; // Regular tetrahedralization. + } + } + insertarray[arylen] = newpt; + arylen++; + } // i + + // Insert the points. + int rejflag = 0; // Do not check encroachment. + if (b->metric) { // -m option. + rejflag |= 4; // Reject it if it lies in some protecting balls. } - } // while (subfacstack->objects > 0l) + insertconstrainedpoints(insertarray, arylen, rejflag); - return 0; + delete[] insertarray; } /////////////////////////////////////////////////////////////////////////////// // // -// 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. // +// meshcoarsening() Deleting (selected) vertices. // // // /////////////////////////////////////////////////////////////////////////////// -int tetgenmesh::getvertexstar(int fullstar, point searchpt, arraypool* tetlist, - arraypool* vertlist, arraypool* shlist) -{ - triface searchtet, neightet, *parytet; - face checksh, *parysh; - point pt, *parypt; - int collectflag; - int t1ver; - int i, j; - - 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; +void tetgenmesh::collectremovepoints(arraypool* remptlist) { + point ptloop, *parypt; + verttype vt; + + // 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) { + // Do not remove a boundary vertex + vt = pointtype(ptloop); + if ((vt == RIDGEVERTEX) || (vt == ACUTEVERTEX) || (vt == FACETVERTEX) || + (vt == FREEFACETVERTEX) || (vt == FREESEGVERTEX) || (vt == UNUSEDVERTEX)) { + ptloop = pointtraverse(); + continue; + } + 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; + } + } + cavetetvertlist->restart(); + cavetetlist->restart(); + if (smlen < ptloop[pointmtrindex]) { + pinfect(ptloop); + remptlist->newindex((void**)&parypt); + *parypt = ptloop; + } + } + ptloop = pointtraverse(); + } + if (b->verbose > 1) { + printf(" Coarsen %ld oversized points.\n", remptlist->objects); + } } - } - 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 (!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 '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 (!fullstar) { - collectflag = 0; + if (b->verbose > 1) { + printf(" Coarsen %ld marked points.\n", remptlist->objects - bak_count); } - } - 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 (in->pointmarkerlist != NULL) - if (vertlist != NULL) { - for (i = 0; i < vertlist->objects; i++) { - parypt = (point *) fastlookup(vertlist, i); - puninfect(*parypt); + if (b->coarsen_param > 0) { // -R1/# + // Remove a coarsen_percent number of interior points. + 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; } - } - if (shlist != NULL) { - for (i = 0; i < shlist->objects; i++) { - parysh = (face *) fastlookup(shlist, i); - suninfect(*parysh); + // Unmark all collected vertices. + for (int i = 0; i < remptlist->objects; i++) { + parypt = (point*)fastlookup(remptlist, i); + puninfect(*parypt); } - } - - return (int) tetlist->objects; } -/////////////////////////////////////////////////////////////////////////////// -// // -// getedge() Get a tetrahedron having the two endpoints. // -// // -// 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. // -// // -// This function is used for the case when the mesh is non-convex. Otherwise,// -// the function finddirection() should be faster than this. // -// // -/////////////////////////////////////////////////////////////////////////////// - -int tetgenmesh::getedge(point e1, point e2, triface *tedge) -{ - triface searchtet, neightet, *parytet; - point pt; - int done; - int i, j; - - 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; - } - } - } +void tetgenmesh::meshcoarsening() { + arraypool* remptlist; - // 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; + if (!b->quiet) { + printf("Mesh coarsening ...\n"); } - } + // Collect the set of points to be removed + remptlist = new arraypool(sizeof(point*), 10); + collectremovepoints(remptlist); - // Go to the link face of e1. - point2tetorg(e1, searchtet); - enextesymself(searchtet); - 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; + if (remptlist->objects == 0l) { + delete remptlist; + return; } - 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(); + if (b->verbose) { + if (remptlist->objects > 0l) { + printf(" Removing %ld points...\n", remptlist->objects); + } + } - return done; -} + point *parypt, *plastpt; + long ms = remptlist->objects; + int nit = 0; + int bak_fliplinklevel = b->fliplinklevel; + b->fliplinklevel = -1; + autofliplinklevel = 1; // Init value. + int i; -/////////////////////////////////////////////////////////////////////////////// -// // -// reduceedgesatvertex() Reduce the number of edges at a given vertex. // -// // -// 'endptlist' contains the endpoints of edges connecting at the vertex. // -// // -/////////////////////////////////////////////////////////////////////////////// + while (1) { -int tetgenmesh::reduceedgesatvertex(point startpt, arraypool* endptlist) -{ - triface searchtet; - point *pendpt, *parypt; - enum interresult dir; - flipconstraints fc; - int reduceflag; - int count; - int n, i, j; - - - fc.remvert = startpt; - fc.checkflipeligibility = 1; - - 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. - } - reduceflag = 0; - // Find the edge. - if (nonconvex) { - if (getedge(startpt, *pendpt, &searchtet)) { - dir = ACROSSVERT; - } else { - // The edge does not exist (was flipped). - dir = INTERSECT; + 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); + } + + // Remove the list of points. + for (i = 0; i < remptlist->objects; i++) { + parypt = (point*)fastlookup(remptlist, i); + if (removevertexbyflips(*parypt)) { + // Move the last entry to the current place. + plastpt = (point*)fastlookup(remptlist, remptlist->objects - 1); + *parypt = *plastpt; + remptlist->objects--; + i--; + } } - } 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; + + 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; } - } else { - // The edge has been flipped. - reduceflag = 1; - } - 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 + } // while (1) - if (count == 0) { - // No edge is reduced. - break; + if (remptlist->objects > 0l) { + if (b->verbose) { + printf(" %ld points are not removed !\n", remptlist->objects); + } } - } // while (1) - - return (int) endptlist->objects; + b->fliplinklevel = bak_fliplinklevel; + delete remptlist; } +//// //// +//// //// +//// reconstruct_cxx ////////////////////////////////////////////////////////// + +//// refine_cxx /////////////////////////////////////////////////////////////// +//// //// +//// //// + /////////////////////////////////////////////////////////////////////////////// // // -// 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 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. // +// makefacetverticesmap() Create a map from facet to its vertices. // // // -// Unless T is a Delaunay tetrahedralization, there is no guarantee that 'p' // -// can be successfully removed. // +// All facets will be indexed (starting from 0). The map is saved in two // +// global arrays: 'idx2facetlist' and 'facetverticeslist'. // // // /////////////////////////////////////////////////////////////////////////////// -int tetgenmesh::removevertexbyflips(point steinerpt) -{ - 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; - - vt = pointtype(steinerpt); - - - if (vt == FREESEGVERTEX) { - sdecode(point2sh(steinerpt), leftseg); - leftseg.shver = 0; - if (sdest(leftseg) == steinerpt) { - senext(leftseg, rightseg); - spivotself(rightseg); - rightseg.shver = 0; - } else { - rightseg = leftseg; - senext2(rightseg, leftseg); - spivotself(leftseg); - leftseg.shver = 0; - } - 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)); +void tetgenmesh::makefacetverticesmap() { + arraypool *facetvertexlist, *vertlist, **paryvertlist; + face subloop, neighsh, *parysh, *parysh1; + point pa, *ppt, *parypt; + verttype vt; + int facetindex, totalvertices; + int i, j, k; + if (b->verbose) { + printf(" Creating the facet vertices map.\n"); } - } 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; - } - 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); - } - 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) { - valence++; - fnextself(spintet); - if (spintet.tet == wrktets[i].tet) break; - } - if (valence == 3) { - // Found the edge. - searchtet = wrktets[i]; - break; - } - } - // Note, we do not detach the three subfaces at p. - // They will be removed within a 4-to-1 flip. - loc = ONFACE; - removeflag = 1; - } - //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); - } - 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; - } - { - // [2017-10-15] Check if the tet is inverted? - point chkp1 = org(neightet); - point chkp2 = apex(neightet); - REAL chkori = orient3d(rpt, lpt, chkp1, chkp2); - if (chkori >= 0.0) { - // Either inverted or degenerated. - break; - } - } - fnextself(spintet); - if (spintet.tet == searchtet.tet) { - // 'searchtet' is [p,d,p1,p2]. - loc = ONEDGE; - removeflag = 1; - break; - } - } - } // if (vt == FREESEGVERTEX) - } - - 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. - sstbond1(rightseg, searchtet); - spintet = searchtet; - while (1) { - 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; - } + facetvertexlist = new arraypool(sizeof(arraypool*), 10); + facetindex = totalvertices = 0; - 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; - } - 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); + 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]; + } } - sesymself(spinsh); - stpivot(spinsh, neightet); - if (neightet.tet != NULL) { - tsdissolve(neightet); + 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); + 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); + } + } // i + totalvertices += (int)vertlist->objects; + // Uninfect facet vertices. + for (k = 0; k < vertlist->objects; k++) { + parypt = (point*)fastlookup(vertlist, k); + puninfect(*parypt); } - stdissolve(spinsh); - spivotself(spinsh); // Go to the next subface. - if (spinsh.sh == parentsh.sh) break; - } + caveshlist->restart(); + // Save this vertex list. + facetvertexlist->newindex((void**)&paryvertlist); + *paryvertlist = vertlist; + facetindex++; } - } // i - } // if (checksubfaceflag) - } + subloop.sh = shellfacetraverse(subfaces); + } - 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); - /* - { // Do not flip if there are wrong number of subfaces inside. - // Check if there are three subfaces at 'p'. - triface newface; face flipshs[3]; - int spivot = 0, scount = 0; - for (i = 0; i < 3; i++) { - fnext(fliptets[3], newface); // [a,b,p,d],[b,c,p,d],[c,a,p,d]. - tspivot(newface, flipshs[i]); - if (flipshs[i].sh != NULL) { - spivot = i; // Remember this subface. - scount++; - } - enextself(fliptets[3]); - } - if (scount > 0) { - // There are three subfaces connecting at p. - // Only do flip if a 3-to-1 flip is possible at p at the bottom face. - if (scount != 3) { - // Wrong number of subfaces. Do not flip. - delete [] fliptets; - return 0; - } - // [2018-03-07] an old fix, not 100% safe. - // if (scount < 3) { - // // The new subface is one of {[a,b,d], [b,c,d], [c,a,d]}. - // // assert(scount == 1); // spivot >= 0 - // if (scount != 1) { - // // Wrong number of subfaces. Do not flip. - // delete [] fliptets; - // return 0; - // } - //} - } + // All subfaces are infected. Uninfect them. + subfaces->traversalinit(); + subloop.sh = shellfacetraverse(subfaces); + while (subloop.sh != NULL) { + suninfect(subloop); + subloop.sh = shellfacetraverse(subfaces); } - */ - if (vt == FREEFACETVERTEX) { - // [2018-03-08] Check if the last 4-to-1 flip is valid. - // fliptets[0],[1],[2] are [p,d,a,b],[p,d,b,c],[p,d,c,a] - triface checktet, chkface; - for (i = 0; i < 3; i++) { - enext(fliptets[i], checktet); - esymself(checktet); // [a,d,b,p],[b,d,c,p],[c,d,a,p] - int scount = 0; int k; - for (k = 0; k < 3; k++) { - esym(checktet, chkface); - if (issubface(chkface)) scount++; - enextself(checktet); - } - if (scount == 3) { - break; // Found a tet which support a 3-to-1 flip. - } else if (scount == 2) { - // This is a strange configuration. Debug it. - // Do not do this flip. - delete [] fliptets; - return 0; - } - } - if (i == 3) { - // No tet in [p,d,a,b],[p,d,b,c],[p,d,c,a] support it. - int scount = 0; - for (i = 0; i < 3; i++) { - eprev(fliptets[i], checktet); - esymself(checktet); // [p,a,b,d],[p,b,c,d],[p,c,a,d] - if (issubface(chkface)) scount++; - } - if (scount != 3) { - // Do not do this flip. - delete [] fliptets; - return 0; - } - } - } // if (vt == FREEFACETVERTEX) - 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. - for (i = 3; i < 6; i++) { - if (issubface(fliptets[i])) valence++; - } - if (valence > 0) { - // 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]; - } - // [2018-03-08] Check if the last 4-to-1 flip is valid. - // fliptets[0],[1],[2] are [p,d,a,b],[p,d,b,c],[p,d,c,a] - triface checktet, chkface; - for (i = 0; i < 3; i++) { - enext(fliptets[i], checktet); - esymself(checktet); // [a,d,b,p],[b,d,c,p],[c,d,a,p] - int scount = 0; int k; - for (k = 0; k < 3; k++) { - esym(checktet, chkface); - if (issubface(chkface)) scount++; - enextself(checktet); - } - if (scount == 3) { - break; // Found a tet which support a 3-to-1 flip. - } else if (scount == 2) { - // This is a strange configuration. Debug it. - // Do not do this flip. - delete [] fliptets; - return 0; - } - } - if (i == 3) { - // No tet in [p,d,a,b],[p,d,b,c],[p,d,c,a] support it. - int scount = 0; - for (i = 0; i < 3; i++) { - eprev(fliptets[i], checktet); - esymself(checktet); // [p,a,b,d],[p,b,c,d],[p,c,a,d] - if (issubface(chkface)) scount++; - } - if (scount != 3) { - // Do not do this flip. - delete [] fliptets; - return 0; - } - } - } // vt == FREEFACETVERTEX - // 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; - } - // 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]; - } - - 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; - // Insert the new segment. - point2tetorg(lpt, searchtet); - finddirection(&searchtet, rpt); - sstbond1(rightseg, searchtet); - spintet = searchtet; - while (1) { - tssbond1(spintet, rightseg); - fnextself(spintet); - if (spintet.tet == searchtet.tet) break; + + if (b->verbose) { + printf(" Found %ld facets.\n", facetvertexlist->objects); } - 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); - } - 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); - } - spivotself(spinsh); - if (spinsh.sh == parentsh.sh) break; - } - } - } // if (checksubfaceflag) + idx2facetlist = new int[facetindex + 1]; + facetverticeslist = new point[totalvertices]; - // Clear the set of new subfaces. - caveshbdlist->restart(); - } // if (vt == FREESEGVERTEX) + totalworkmemory += ((facetindex + 1) * sizeof(int) + totalvertices * sizeof(point*)); - // 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--; + 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++; + } } - if (steinerleft > 0) steinerleft++; - } - return 1; + // Free the lists. + for (i = 0; i < facetvertexlist->objects; i++) { + paryvertlist = (arraypool**)fastlookup(facetvertexlist, i); + vertlist = *paryvertlist; + delete vertlist; + } + delete facetvertexlist; } /////////////////////////////////////////////////////////////////////////////// // // -// suppressbdrysteinerpoint() Suppress a boundary Steiner point // +// Check whether two segments, or a segment and a facet, or two facets are // +// adjacent to each other. // // // /////////////////////////////////////////////////////////////////////////////// -int tetgenmesh::suppressbdrysteinerpoint(point steinerpt) -{ - face parentsh, spinsh, *parysh; - face leftseg, rightseg; - point lpt = NULL, rpt = NULL; - int i; - - verttype vt = pointtype(steinerpt); - - if (vt == FREESEGVERTEX) { - sdecode(point2sh(steinerpt), leftseg); - leftseg.shver = 0; - if (sdest(leftseg) == steinerpt) { - senext(leftseg, rightseg); - spivotself(rightseg); - rightseg.shver = 0; - } else { - rightseg = leftseg; - senext2(rightseg, leftseg); - spivotself(leftseg); - leftseg.shver = 0; - } - 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); - if (parentsh.sh != NULL) { - // It is not a dangling segment. - 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; - } - - 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)); - 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)); - 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); - break; - } - } - } - } // j - if (j == cavetetlist->objects) { - break; // There is no intersection!! Debug is needed. - } - // 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; - } - // 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); - { - // [2017-10-15] Rounding - REAL lab = distance(pa, pb); - REAL lbc = distance(pb, pc); - REAL lca = distance(pc, pa); - REAL lv = (lab + lbc + lca) / 3.0; - REAL l3 = lv*lv*lv; - if (fabs(ori) / l3 < 1e-8) ori = 0.0; - } - 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; - } - // 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 +int tetgenmesh::segsegadjacent(face* seg1, face* seg2) { + int segidx1 = getfacetindex(*seg1); + int segidx2 = getfacetindex(*seg2); - if (i < cavesegshlist->objects) { - // Failed to suppress the vertex. - for (; i > 0; i--) { - if (newsteiners[i - 1] != NULL) { - pointdealloc(newsteiners[i - 1]); - } - } - delete [] newsteiners; - cavesegshlist->restart(); - return 0; - } - - // 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; - - 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; + 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; } - 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 (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); - } - splitseg = NULL; - } - sremovevertex(steinerpt, &parentsh, splitseg, slawson); - - if (vt == FREESEGVERTEX) { - // The original segment is returned in 'rightseg'. - rightseg.shver = 0; - } - - // 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); - 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; - } - } - - // 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); - } else { - // Search for an open face at this edge. - spintet = neightet; - while (1) { - esym(spintet, searchtet); - fsym(searchtet, spintet); - if (spintet.tet == NULL) break; - } - // Found an open face at 'searchtet'. - neightet = searchtet; - } - } else { - // The edge (at 'newsh') is a dangling segment. - // Get an adjacent tet at this segment. - sstpivot1(checkseg, neightet); - if (org(neightet) != sdest(newsh)) esymself(neightet); - // Search for an open face at this edge. - spintet = neightet; - while (1) { - esym(spintet, searchtet); - fsym(searchtet, spintet); - if (spintet.tet == NULL) break; - } - // 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. - 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. - } - } - } - 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 (caveoldtetlist->objects > 0l) { - // Delete hull tets which contain 'steinerpt'. - for (i = 0; i < caveoldtetlist->objects; i++) { - parytet = (triface *) fastlookup(caveoldtetlist, i); - tetrahedrondealloc(parytet->tet); + return 0; +} + +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++; } - // Must update the hullsize. - hullsize -= caveoldtetlist->objects; - caveoldtetlist->restart(); - } - 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. + puninfect(pa); + puninfect(pb); + + return count == 1; +} +int tetgenmesh::facetfacetadjacent(face* subsh1, face* subsh2) { + int count = 0, i; - point *parypt; - int steinercount = 0; + int fidx1 = getfacetindex(*subsh1); + int fidx2 = getfacetindex(*subsh2); - int bak_fliplinklevel = b->fliplinklevel; - b->fliplinklevel = 100000; // Unlimited flip level. + if (fidx1 == fidx2) return 0; - // Try to remove newly added Steiner points. - for (i = 0; i < n; i++) { - if (newsteiners[i] != NULL) { - if (!removevertexbyflips(newsteiners[i])) { - if (b->supsteiner_level > 0) { // Not -Y/0 - // Save it in subvertstack for removal. - subvertstack->newindex((void **) &parypt); - *parypt = newsteiners[i]; - } - steinercount++; - } + for (i = idx2facetlist[fidx1]; i < idx2facetlist[fidx1 + 1]; i++) { + pinfect(facetverticeslist[i]); } - } - b->fliplinklevel = bak_fliplinklevel; - - if (steinercount > 0) { - if (b->verbose > 2) { - printf(" Added %d interior Steiner points.\n", steinercount); + for (i = idx2facetlist[fidx2]; i < idx2facetlist[fidx2 + 1]; i++) { + if (pinfected(facetverticeslist[i])) count++; } - } - delete [] newsteiners; + // Uninfect the vertices. + for (i = idx2facetlist[fidx1]; i < idx2facetlist[fidx1 + 1]; i++) { + puninfect(facetverticeslist[i]); + } - return 1; + return count > 0; } - /////////////////////////////////////////////////////////////////////////////// // // -// suppresssteinerpoints() Suppress Steiner points. // +// save_segmentpoint_insradius(), save_facetpoint_insradius() // // // -// 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. // +// Determine and save the relaxed insertion radius of a Steiner point on a // +// segment or a facet. By default, it is the closet distance to the parent // +// point of this Steiner point. But may be larger than it. // // // /////////////////////////////////////////////////////////////////////////////// -int tetgenmesh::suppresssteinerpoints() -{ - - if (!b->quiet) { - printf("Suppressing Steiner points ...\n"); - } - - point rempt, *parypt; - - 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++; +void tetgenmesh::save_segmentpoint_insradius(point segpt, point parentpt, REAL r) { + REAL rv = r, rp; + if (pointtype(parentpt) == FREESEGVERTEX) { + face parentseg1, parentseg2; + sdecode(point2sh(segpt), parentseg1); + sdecode(point2sh(parentpt), parentseg2); + if (segsegadjacent(&parentseg1, &parentseg2)) { + rp = getpointinsradius(parentpt); + if (rv < rp) { + // The relaxed insertion radius of 'newpt'. + rv = rp; + } + } + } else if (pointtype(parentpt) == FREEFACETVERTEX) { + face parentseg, parentsh; + sdecode(point2sh(segpt), parentseg); + sdecode(point2sh(parentpt), parentsh); + if (segfacetadjacent(&parentseg, &parentsh)) { + rp = getpointinsradius(parentpt); + if ((sqrt(2.0) * rv) < rp) { // if (rv < rp) { + // The relaxed insertion radius of 'newpt'. + rv = rp / sqrt(2.0); // rv = rp; + } } - } - } - } // i - - if (suppcount > 0) { - if (b->verbose) { - printf(" Suppressed %d boundary Steiner points.\n", suppcount); } - } + setpointinsradius(segpt, rv); +} - if (b->supsteiner_level > 0) { // -Y/1 - 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++; - } +void tetgenmesh::save_facetpoint_insradius(point facpt, point parentpt, REAL r) { + REAL rv = r, rp; + if (pointtype(parentpt) == FREESEGVERTEX) { + face parentseg, parentsh; + sdecode(point2sh(parentpt), parentseg); + sdecode(point2sh(facpt), parentsh); + if (segfacetadjacent(&parentseg, &parentsh)) { + rp = getpointinsradius(parentpt); + if (rv < (sqrt(2.0) * rp)) { + rv = sqrt(2.0) * rp; // The relaxed insertion radius of 'newpt'. + } + } + } else if (pointtype(parentpt) == FREEFACETVERTEX) { + face parentsh1, parentsh2; + sdecode(point2sh(parentpt), parentsh1); + sdecode(point2sh(facpt), parentsh2); + if (facetfacetadjacent(&parentsh1, &parentsh2)) { + rp = getpointinsradius(parentpt); + if (rv < rp) { + rv = rp; // The relaxed insertion radius of 'newpt'. + } } - } } - } + setpointinsradius(facpt, rv); +} - if (remcount > 0) { - if (b->verbose) { - printf(" Removed %d interior Steiner points.\n", remcount); +/////////////////////////////////////////////////////////////////////////////// +// // +// enqueuesubface() Queue a subface or a subsegment for encroachment chk. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::enqueuesubface(memorypool* pool, face* chkface) { + if (!smarktest2ed(*chkface)) { + smarktest2(*chkface); // Only queue it once. + face* queface = (face*)pool->alloc(); + *queface = *chkface; } - } +} - b->fliplinklevel = bak_fliplinklevel; +/////////////////////////////////////////////////////////////////////////////// +// // +// enqueuetetrahedron() Queue a tetrahedron for quality check. // +// // +/////////////////////////////////////////////////////////////////////////////// - if (b->supsteiner_level > 1) { // -Y/2 - // Smooth interior Steiner points. - optparameters opm; - triface *parytet; - point *ppt; - REAL ori; - int smtcount, count, ivcount; - int nt, j; +void tetgenmesh::enqueuetetrahedron(triface* chktet) { + if (!marktest2ed(*chktet)) { + marktest2(*chktet); // Only queue it once. + triface* quetet = (triface*)badtetrahedrons->alloc(); + *quetet = *chktet; + } +} - // Point smooth options. - opm.max_min_volume = 1; - opm.numofsearchdirs = 20; - opm.searchstep = 0.001; - opm.maxiter = 30; // Limit the maximum iterations. +/////////////////////////////////////////////////////////////////////////////// +// // +// checkseg4encroach() Check if an edge is encroached upon by a point. // +// // +/////////////////////////////////////////////////////////////////////////////// - smtcount = 0; +int tetgenmesh::checkseg4encroach(point pa, point pb, point checkpt) { + // Check if the point lies inside the diametrical sphere of this seg. + REAL v1[3], v2[3]; + + 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 { + return 1; // NO protecting ball. Encroached. + } + } else { + return 1; // Inside! Encroached. + } + } - do { + return 0; +} - nt = 0; +/////////////////////////////////////////////////////////////////////////////// +// // +// 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. // +// // +// 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. // +// // +/////////////////////////////////////////////////////////////////////////////// - while (1) { - count = 0; - ivcount = 0; // Clear the inverted count. +int tetgenmesh::checkseg4split(face* chkseg, point& encpt, int& qflag) { + REAL ccent[3], len, r; + int i; - 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 + point forg = sorg(*chkseg); + point fdest = sdest(*chkseg); - smtcount += count; + // Initialize the return values. + encpt = NULL; + qflag = 0; - if (count == 0) { - // No point has been smoothed. - break; + 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; } + } - nt++; - if (nt > 2) { - break; // Already three iterations. + if (b->fixedvolume) { + if ((len * len * len) > b->maxvolume) { + qflag = 1; + return 1; } - } // 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; + 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; } - } + } - break; - } while (1); // Additional loop for (ivcount > 0) + // 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; - if (ivcount > 0) { - printf("BUG Report! The mesh contain inverted elements.\n"); - } + 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; + } + } + } + fnextself(spintet); + if (spintet.tet == searchtet.tet) break; + } // while (1) - if (b->verbose) { - if (smtcount > 0) { - printf(" Smoothed %d Steiner points.\n", smtcount); - } + if (encpt != NULL) { + return 1; } - } // -Y2 - - subvertstack->restart(); - return 1; + return 0; // No need to split it. } /////////////////////////////////////////////////////////////////////////////// // // -// recoverboundary() Recover segments and facets. // +// 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::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(" Recovering segments.\n"); - } - - // Segments will be introduced. - checksubsegflag = 1; - - misseglist = new arraypool(sizeof(face), 8); - bdrysteinerptlist = new arraypool(sizeof(point), 8); - - // In random order. - subsegs->traversalinit(); - for (i = 0; i < subsegs->items; i++) { - s = randomnation(i + 1); - // Move the s-th seg to the i-th. - subsegstack->newindex((void **) &paryseg); - *paryseg = * (face *) fastlookup(subsegstack, s); - // Put i-th seg to be the s-th. - searchseg.sh = shellfacetraverse(subsegs); - paryseg = (face *) fastlookup(subsegstack, s); - *paryseg = searchseg; - } - - // The init number of missing segments. - ms = subsegs->items; - nit = 0; - if (b->fliplinklevel < 0) { - autofliplinklevel = 1; // Init value. - } +int tetgenmesh::splitsegment(face* splitseg, point encpt, REAL rrp, point encpt1, point encpt2, + int qflag, int chkencflag) { - // First, trying to recover segments by only doing flips. - while (1) { - recoversegments(misseglist, 0, 0); + if (!qflag && smarktest3ed(*splitseg)) { + // Do not try to re-split a marked segment. + return 0; + } - if (misseglist->objects > 0) { - if (b->fliplinklevel >= 0) { - break; - } else { - if (misseglist->objects >= ms) { - nit++; - if (nit >= 3) { - //break; - // Do the last round with unbounded flip link level. - b->fliplinklevel = 100000; - } + 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 (spinsh.sh == NULL) break; // It belongs to only one facet. + } + if ((!spinsh.sh) || (areabound(spinsh) == 0)) { + // All facets at this seg are not allowed to be split. + return 0; // Do not split it. + } + } } else { - ms = misseglist->objects; - if (nit > 0) { - nit--; - } - } - for (i = 0; i < misseglist->objects; i++) { - subsegstack->newindex((void **) &paryseg); - *paryseg = * (face *) fastlookup(misseglist, i); + return 0; // Do not split this segment. } - misseglist->restart(); - autofliplinklevel+=b->fliplinklevelinc; - } - } else { - // All segments are recovered. - break; - } - } // while (1) - - 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 { - break; - } - } - if (b->verbose) { - printf(" %ld (%ld) segments are recovered (missing).\n", - subsegs->items - misseglist->objects, misseglist->objects); - } - } - - 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(); - - recoversegments(misseglist, 1, 1); - - if (misseglist->objects < ms) { - // The number of missing segments is reduced. - continue; - } else { - break; - } - } - if (b->verbose) { - printf(" Added %ld Steiner points in volume.\n", st_volref_count); - } - } + } // if (b->nobisect) - 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(); + triface searchtet; + face searchsh; + point newpt; + insertvertexflags ivf; - recoversegments(misseglist, 1, 2); + makepoint(&newpt, FREESEGVERTEX); + getsteinerptonsegment(splitseg, encpt, newpt); - 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); - } + if (!qflag && !b->cdtrefine) { + // Do not insert the point if it encroaches upon an adjacent segment. + face parentsh; + spivot(*splitseg, parentsh); + if (parentsh.sh != NULL) { + face spinsh, neighsh; + face neighseg; + spinsh = parentsh; + while (1) { + for (int i = 0; i < 2; i++) { + if (i == 0) { + senext(spinsh, neighsh); + } else { + senext2(spinsh, neighsh); + } + if (isshsubseg(neighsh)) { + sspivot(neighsh, neighseg); + if (checkseg4encroach(sorg(neighseg), sdest(neighseg), newpt)) { + pointdealloc(newpt); + return 0; // Do not split this segment. + } + } + } // i + spivotself(spinsh); + if (spinsh.sh == NULL) break; + if (spinsh.sh == parentsh.sh) break; + } // while (1) + } } - } + // Split the segment by the Bowyer-Watson algorithm. + sstpivot1(*splitseg, searchtet); + ivf.iloc = (int)ONEDGE; + ivf.bowywat = 3; // Use Bowyer-Watson, preserve subsegments and subfaces; + 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; // Return the closet mesh vertex. - 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; - } - } - 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 (insertpoint(newpt, &searchtet, &searchsh, splitseg, &ivf)) { + st_segref_count++; + if (steinerleft > 0) steinerleft--; + if (useinsertradius) { + save_segmentpoint_insradius(newpt, ivf.parentpt, ivf.smlen); + } + if (flipstack != NULL) { + flipconstraints fc; + fc.chkencflag = chkencflag; + fc.enqflag = 2; + lawsonflip3d(&fc); + unflipqueue->restart(); } - 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))); + return 1; + } else { + // Point is not inserted. + if (ivf.iloc == (int)NEARVERTEX) { + terminatetetgen(this, 2); } - } + pointdealloc(newpt); + // Mark this segment to avoid splitting in the future. + smarktest3(*splitseg); + return 0; } - subvertstack->restart(); - } - +} - tv = clock(); +/////////////////////////////////////////////////////////////////////////////// +// // +// repairencsegs() Repair encroached (sub) segments. // +// // +/////////////////////////////////////////////////////////////////////////////// - if (b->verbose) { - printf(" Recovering facets.\n"); - } +void tetgenmesh::repairencsegs(int chkencflag) { + face* bface; + point encpt = NULL; + int qflag = 0; + + // 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); + } + } + } + // Remove this entry from list. + bface->shver = -1; // Signal it as a deleted element. + badsubsegs->dealloc((void*)bface); + } + bface = (face*)badsubsegs->traverse(); + } + } - // Subfaces will be introduced. - checksubfaceflag = 1; + if (badsubsegs->items > 0) { + if (b->verbose) { + printf("The desired number of Steiner points is reached.\n"); + } + 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(); + } +} - misshlist = new arraypool(sizeof(face), 8); +/////////////////////////////////////////////////////////////////////////////// +// // +// checkfac4encroach() Check if a subface is encroached by a point. // +// // +/////////////////////////////////////////////////////////////////////////////// - // 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; - } +int tetgenmesh::checkfac4encroach(point pa, point pb, point pc, point checkpt, REAL* cent, + REAL* r) { + REAL rd, len; + int encroached = 0; - ms = subfaces->items; - nit = 0; - b->fliplinklevel = -1; // Init. - if (b->fliplinklevel < 0) { - autofliplinklevel = 1; // Init value. - } + circumsphere(pa, pb, pc, NULL, cent, &rd); + if (rd == 0) { + terminatetetgen(this, 2); + } - while (1) { - recoversubfaces(misshlist, 0); + if (b->use_equatorial_lens) { + REAL normal[3], fcenter[3]; + REAL xta, yta, zta; + REAL multiplier; - if (misshlist->objects > 0) { - if (b->fliplinklevel >= 0) { - break; - } else { - if (misshlist->objects >= ms) { - nit++; - if (nit >= 3) { - //break; - // Do the last round with unbounded flip link level. - b->fliplinklevel = 100000; - } - } else { - ms = misshlist->objects; - if (nit > 0) { - nit--; - } + fcenter[0] = cent[0] - pc[0]; + fcenter[1] = cent[1] - pc[1]; + fcenter[2] = cent[2] - pc[2]; + + // Get the normal of the oriented face [a->b->c], without normalized. + facenormal(pa, pb, pc, normal, 1, NULL); + multiplier = + 0.985 * + sqrt((fcenter[0] * fcenter[0] + fcenter[1] * fcenter[1] + fcenter[2] * fcenter[2]) / + (3.0 * (normal[0] * normal[0] + normal[1] * normal[1] + normal[2] * normal[2]))); + xta = checkpt[0] - pc[0]; + yta = checkpt[1] - pc[1]; + zta = checkpt[2] - pc[2]; + // Make sure that the normal is pointing to "checkpt". + if ((xta * normal[0] + yta * normal[1] + zta * normal[2]) < 0) { + // Reverse the normal direction. + normal[0] = -normal[0]; + normal[1] = -normal[1]; + normal[2] = -normal[2]; } - for (i = 0; i < misshlist->objects; i++) { - subfacstack->newindex((void **) &parysh); - *parysh = * (face *) fastlookup(misshlist, i); + + if (xta * xta + yta * yta + zta * zta <= + 2.0 * (xta * (fcenter[0] - multiplier * normal[0]) + + yta * (fcenter[1] - multiplier * normal[1]) + + zta * (fcenter[2] - multiplier * normal[2]))) { + encroached = 1; } - misshlist->restart(); - autofliplinklevel+=b->fliplinklevelinc; - } } else { - // All subfaces are recovered. - break; + len = distance(cent, checkpt); + if ((fabs(len - rd) / rd) < b->epsilon) len = rd; // Rounding. + if (len < rd) { + encroached = 1; + } + } + + if (encroached) { + // 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. + } } - } // while (1) - if (b->verbose) { - printf(" %ld (%ld) subfaces are recovered (missing).\n", - subfaces->items - misshlist->objects, misshlist->objects); - } + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// checkfac4split() Check if a subface needs to be split. // +// // +// 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). // +// // +// Return 1 if it needs to be split, otherwise, return 0. // +// 'chkfac' represents its longest edge. // +// // +/////////////////////////////////////////////////////////////////////////////// + +int tetgenmesh::checkfac4split(face* chkfac, point& encpt, int& qflag, REAL* cent) { + point pa, pb, pc; + REAL area, rd, len; + REAL A[4][4], rhs[4], D; + int indx[4]; + int i; + + encpt = NULL; + qflag = 0; - 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(); + pa = sorg(*chkfac); + pb = sdest(*chkfac); + pc = sapex(*chkfac); - recoversubfaces(NULL, 1); + // 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 (b->verbose) { - printf(" Added %ld Steiner points in facets.\n", st_facref_count); - } - } + area = 0.5 * sqrt(dot(A[2], A[2])); // The area of [a,b,c]. + // Compute the right hand side vector b (3x1). + rhs[0] = 0.5 * dot(A[0], A[0]); // edge [a,b] + rhs[1] = 0.5 * dot(A[1], A[1]); // edge [a,c] + rhs[2] = 0.0; - 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 (b->verbose) { - if (st_facref_count < bak_facref_count) { - printf(" Removed %ld Steiner points in facets.\n", - bak_facref_count - st_facref_count); - } + // 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. + terminatetetgen(this, 2); } - subvertstack->restart(); - } + 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 (bdrysteinerptlist->objects > 0) { - if (b->verbose) { - printf(" %ld Steiner points remained in boundary.\n", - bdrysteinerptlist->objects); + if (checkconstraints && (areabound(*chkfac) > 0.0)) { + // Check if the subface has too big area. + if (area > areabound(*chkfac)) { + qflag = 1; + return 1; + } } - } // if - - - // Accumulate the dynamic memory. - totalworkmemory += (misseglist->totalmemory + misshlist->totalmemory + - bdrysteinerptlist->totalmemory); - - delete bdrysteinerptlist; - delete misseglist; - delete misshlist; -} - -//// //// -//// //// -//// steiner_cxx ////////////////////////////////////////////////////////////// - - -//// reconstruct_cxx ////////////////////////////////////////////////////////// -//// //// -//// //// - -/////////////////////////////////////////////////////////////////////////////// -// // -// carveholes() Remove tetrahedra not in the mesh domain. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::carveholes() -{ - 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->quiet) { - if (b->convex) { - printf("Marking exterior tetrahedra ...\n"); - } else { - printf("Removing exterior tetrahedra ...\n"); - } - } - - // 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.ver = 11; // The face opposite to dummypoint. - tetloop.tet = alltetrahedrontraverse(); - while (tetloop.tet != (tetrahedron *) NULL) { - 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; + if (b->fixedvolume) { + if ((area * sqrt(area)) > b->maxvolume) { + qflag = 1; + return 1; } - } } - 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); - } - *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; - } - } - 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 (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; } - } - } // 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"); } - } - } // i - } // if (in->numberofholes > 0) - - if (b->hole_mesh && (b->hole_mesh_filename[0] != 0)) { - // A hole mesh (***.ele) is given. - //enum tetgenbehavior::objecttype object; - char filebasename[256]; - strcpy(filebasename, b->hole_mesh_filename); - //object = tetgenbehavior::MESH; - if (!strcmp(&filebasename[strlen(filebasename) - 4], ".ele")) { - filebasename[strlen(filebasename) - 4] = '\0'; - //object = tetgenbehavior::MESH; - } - bool hole_mesh_loaded = false; - tetgenio io; - if (io.load_node(filebasename)) { - if (io.load_tet(filebasename)) { - hole_mesh_loaded = true; - } - } - if (hole_mesh_loaded) { - if (b->verbose) { - printf(" Adding hole tets from the mesh %s\n", b->hole_mesh_filename); - } - int count = 0, hcount = 0, scount = 0; - int shift = io.firstnumber > 0 ? -1 : 0; - double *p1, *p2, *p3, *p4; - double searchpt[3]; - for (i = 0; i < io.numberoftetrahedra; i++) { - int *idx = &(io.tetrahedronlist[i * 4]); - p1 = &(io.pointlist[(idx[0]+shift)*3]); - p2 = &(io.pointlist[(idx[1]+shift)*3]); - p3 = &(io.pointlist[(idx[2]+shift)*3]); - p4 = &(io.pointlist[(idx[3]+shift)*3]); - for (j = 0; j < 3; j++) { - searchpt[j] = (p1[j]+p2[j]+p3[j]+p4[j])/4.; - } - // Search the point. - neightet.tet = NULL; - if (locate(searchpt, &neightet) != OUTSIDE) { - // The tet 'neightet' contain this point. - if (!infected(neightet)) { - infect(neightet); - tetarray->newindex((void **) &parytet); - *parytet = neightet; - count++; - // 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); - hcount++; - } else { - tetarray->newindex((void **) &parytet); - count++; - } - *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; - hcount++; - } - } - 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; - scount++; - } + fsymself(adjtet); + if (!ishulltet(adjtet)) { + volbnd = volumebound(adjtet.tet); + if ((volbnd > 0) && (area * sqrt(area)) > volbnd) { + qflag = 1; + return 1; } - } } - } // i - if (b->verbose) { - printf(" Added %d hole tets, %d hull tet, %d hole subfaces\n", - count, hcount, scount); - } - } // if (hole_mesh_loaded) - } - - 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; - } - } - } - - // 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). - 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 - - 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; - } - } - } - - // 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->supsteiner_level > 0)) { // -Y/1 - // 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(); - } - if (!b->convex && (tetarray->objects > 0l)) { // No -c option. - // Remove exterior tets. Hull tets are updated. - arraypool *newhullfacearray; - triface hulltet, casface; - face segloop, *paryseg; - point pa, pb, pc; - long delsegcount = 0l; - - // Collect segments which point to infected tets. Some segments - // may get deleted after the removal of exterior tets. - subsegs->traversalinit(); - segloop.sh = shellfacetraverse(subsegs); - while (segloop.sh != NULL) { - sstpivot1(segloop, neightet); - if (infected(neightet)) { - subsegstack->newindex((void **) &paryseg); - *paryseg = segloop; - } - segloop.sh = shellfacetraverse(subsegs); + 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; + } } - newhullfacearray = new arraypool(sizeof(triface), 10); - - // 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 + triface searchtet; + REAL smlen = 0; - // 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; - } - 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 + // Check if this subface is locally encroached. + for (i = 0; i < 2; i++) { + stpivot(*chkfac, searchtet); + if (!ishulltet(searchtet)) { + int encroached = 0; - 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; + len = distance(oppo(searchtet), cent); + if ((fabs(len - rd) / rd) < b->epsilon) len = rd; // Rounding. - for (i = 0; i < subfacstack->objects; i++) { - parysh = (face *) fastlookup(subfacstack, i); - if (i == 0) { - if (b->verbose) { - printf("Warning: Removed an exterior face (%d, %d, %d) #%d\n", - pointmark(sorg(*parysh)), pointmark(sdest(*parysh)), - pointmark(sapex(*parysh)), shellmark(*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; - } - if (casingin.sh != casingout.sh) { - // Update the link: ... -> casingin -> casingout ->... - sbond1(casingin, casingout); + if (b->use_equatorial_lens) { + point tettapex = oppo(searchtet); + REAL normal[3], fcenter[3]; + REAL xta, yta, zta; + REAL multiplier; + // Get the normal of the oriented face [a->b->c], without normalized. + point fa = org(searchtet); + point fb = dest(searchtet); + point fc = apex(searchtet); + + fcenter[0] = cent[0] - fc[0]; + fcenter[1] = cent[1] - fc[1]; + fcenter[2] = cent[2] - fc[2]; + + facenormal(fa, fb, fc, normal, 1, NULL); + multiplier = 0.985 * sqrt((fcenter[0] * fcenter[0] + fcenter[1] * fcenter[1] + + fcenter[2] * fcenter[2]) / + (3.0 * (normal[0] * normal[0] + normal[1] * normal[1] + + normal[2] * normal[2]))); + xta = tettapex[0] - fc[0]; + yta = tettapex[1] - fc[1]; + zta = tettapex[2] - fc[2]; + if (xta * xta + yta * yta + zta * zta <= + 2.0 * (xta * (fcenter[0] - multiplier * normal[0]) + + yta * (fcenter[1] - multiplier * normal[1]) + + zta * (fcenter[2] - multiplier * normal[2]))) { + encroached = 1; + } } else { - // Only one subface at this edge is left. - sdissolve(casingout); - } - if (checkseg.sh != NULL) { - // Make sure the segment does not connect to a dead one. - ssbond(casingout, checkseg); - } - } else { - if (checkseg.sh != NULL) { - //if (checkseg.sh[3] != NULL) { - if (delsegcount == 0) { - if (b->verbose) { - printf("Warning: Removed an exterior segment (%d, %d) #%d\n", - pointmark(sorg(checkseg)), pointmark(sdest(checkseg)), - shellmark(checkseg)); + if (len < rd) { + encroached = 1; } - } - shellfacedealloc(subsegs, checkseg.sh); - delsegcount++; - } - } - senextself(*parysh); - } // j - // Delete this subface. - shellfacedealloc(subfaces, parysh->sh); - } // i - if (b->verbose) { - printf(" Deleted %ld subfaces.\n", subfacstack->objects); - } - subfacstack->restart(); - } // if (subfacstack->objects > 0l) - - if (subsegstack->objects > 0l) { - for (i = 0; i < subsegstack->objects; i++) { - paryseg = (face *) fastlookup(subsegstack, i); - if (paryseg->sh && (paryseg->sh[3] != NULL)) { - sstpivot1(*paryseg, neightet); - if (infected(neightet)) { - if (b->verbose) { - printf("Warning: Removed an exterior segment (%d, %d) #%d\n", - pointmark(sorg(*paryseg)), pointmark(sdest(*paryseg)), - shellmark(*paryseg)); } - shellfacedealloc(subsegs, paryseg->sh); - delsegcount++; - } - } - } - subsegstack->restart(); - } // if (subsegstack->objects > 0l) - - if (delsegcount > 0) { - if (b->verbose) { - printf(" Deleted %ld segments.\n", delsegcount); - } - } - - 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 { - st_volref_count--; + if (encroached) { + if (smlen == 0) { + smlen = len; + encpt = oppo(searchtet); + } else { + if (len < smlen) { + smlen = len; + encpt = oppo(searchtet); + } + } + // return 1; } - delsteinercount++; - if (steinerleft > 0) steinerleft++; - } - setpointtype(*parypt, UNUSEDVERTEX); - unuverts++; - } - } - - 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) - - // Update the hull size. - hullsize += (newhullfacearray->objects - hullarray->objects); - - // 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(); - - for (i = 0; i < hullarray->objects; i++) { - parytet = (triface *) fastlookup(hullarray, i); - tetrahedrondealloc(parytet->tet); + sesymself(*chkfac); } - hullarray->restart(); - delete newhullfacearray; - } // if (!b->convex && (tetarray->objects > 0l)) - - if (b->convex && (tetarray->objects > 0l)) { // With -c option - // In this case, all exterior tets get a region marker '-1'. - int attrnum = numelemattrib - 1; + return encpt != NULL; // return 0; +} - for (i = 0; i < tetarray->objects; i++) { - parytet = (triface *) fastlookup(tetarray, i); - setelemattribute(parytet->tet, attrnum, -1); - } - tetarray->restart(); +/////////////////////////////////////////////////////////////////////////////// +// // +// splitsubface() Split a subface. // +// // +// 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'. // +// // +/////////////////////////////////////////////////////////////////////////////// - for (i = 0; i < hullarray->objects; i++) { - parytet = (triface *) fastlookup(hullarray, i); - uninfect(*parytet); - } - hullarray->restart(); +int tetgenmesh::splitsubface(face* splitfac, point encpt, point encpt1, int qflag, REAL* ccent, + int chkencflag) { - if (subfacstack->objects > 0l) { - for (i = 0; i < subfacstack->objects; i++) { - parysh = (face *) fastlookup(subfacstack, i); - suninfect(*parysh); - } - subfacstack->restart(); + if (!qflag && smarktest3ed(*splitfac)) { + // Do not try to re-split a marked subface. + return 0; } - if (cavetetvertlist->objects > 0l) { - cavetetvertlist->restart(); - } - } // if (b->convex && (tetarray->objects > 0l)) + 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) - 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); - } - 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; + if (useinsertradius) { + if (encpt != NULL) { + REAL rp; // Insertion radius of newpt. + REAL rv = distance(encpt, ccent); + 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. + 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) { + return 0; // Reject the insertion of newpt. + } } - } - } // k - } // j - regioncount++; - } // if (regiontets[i/5].tet != NULL) - } // i - } - - // Set attributes for all tetrahedra. - attr = maxattr + 1; - tetrahedrons->traversalinit(); - tetloop.tet = tetrahedrontraverse(); - while (tetloop.tet != (tetrahedron *) NULL) { - if (!infected(tetloop)) { - // An unmarked region. - tetarray->restart(); // Re-use this array. - infect(tetloop); - tetarray->newindex((void **) &parytet); - *parytet = tetloop; - // Find and mark all tets. - for (j = 0; j < tetarray->objects; j++) { - parytet = (triface *) fastlookup(tetarray, j); - tetloop = *parytet; - setelemattribute(tetloop.tet, attrnum, attr); - for (k = 0; k < 4; k++) { - decode(tetloop.tet[k], neightet); - // Is the adjacent tet already checked? - if (!infected(neightet)) { - // Is this side protected by a subface? - if (!issubface(neightet)) { - infect(neightet); - tetarray->newindex((void **) &parytet); - *parytet = neightet; - } } - } // k - } // j - attr++; // Increase the attribute. - regioncount++; - } - tetloop.tet = tetrahedrontraverse(); - } - // Until here, every tet has a region attribute. + } + } // if (useinsertradius) - // Uninfect processed tets. - tetrahedrons->traversalinit(); - tetloop.tet = tetrahedrontraverse(); - while (tetloop.tet != (tetrahedron *) NULL) { - uninfect(tetloop); - tetloop.tet = tetrahedrontraverse(); - } + face searchsh; + insertvertexflags ivf; + point newpt; + int i; - 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) - - if (regiontets != NULL) { - delete [] regiontets; - } - delete tetarray; - delete hullarray; - - 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(); + // Initialize the inserting point. + makepoint(&newpt, FREEFACETVERTEX); + // Split the subface at its circumcenter. + for (i = 0; i < 3; i++) + newpt[i] = ccent[i]; + + // Search a subface which contains 'newpt'. + searchsh = *splitfac; + // Calculate an above point. It lies above the plane containing + // the subface [a,b,c], and save it in dummypoint. Moreover, + // the vector cent->dummypoint is the normal of the plane. + calculateabovepoint4(newpt, sorg(*splitfac), sdest(*splitfac), sapex(*splitfac)); + // 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))) { + // Point location failed. + pointdealloc(newpt); + // Mark this subface to avoid splitting in the future. + smarktest3(*splitfac); + return 0; } - flipconstraints fc; - fc.enqflag = 2; - long sliver_peel_count = lawsonflip3d(&fc); + triface searchtet; - if (sliver_peel_count > 0l) { - if (b->verbose) { - printf(" Removed %ld hull slivers.\n", sliver_peel_count); - } + // Insert the point. + stpivot(searchsh, searchtet); + ivf.bowywat = 3; // Use Bowyer-Watson. Preserve subsegments and subfaces; + ivf.lawson = 2; + ivf.rejflag = 1; // Do check the encroachment of segments. + if (b->metric) { + ivf.rejflag |= 4; // Do check encroachment of protecting balls. } - unflipqueue->restart(); - } // if (!b->convex) -} + 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 = *splitfac; + ivf.smlenflag = useinsertradius; // Update the insertion radius. -// [2018-07-30] -// Search a face with given indices (i,j,k). -// This function is only called when the default fast search fails. -// It is possible when there are non-manifold edges on the hull. -// On finish, tetloop return this face if it exists, otherwise, return 0. -int tetgenmesh::search_face(point pi, point pj, point pk, triface &tetloop) -{ - pinfect(pi); - pinfect(pj); - pinfect(pk); - - int t1ver; - triface t, t1; - point *pts, toppo; - int pcount = 0; - - t.ver = t1.ver = 0; - tetrahedrons->traversalinit(); - t.tet = tetrahedrontraverse(); - while (t.tet != NULL) { - pts = (point *) t.tet; - pcount = 0; - if (pinfected(pts[4])) pcount++; - if (pinfected(pts[5])) pcount++; - if (pinfected(pts[6])) pcount++; - if (pinfected(pts[7])) pcount++; - - if (pcount == 3) { - // Found a tet containing this face. - for (t.ver = 0; t.ver < 4; t.ver++) { - toppo = oppo(t); - if (!pinfected(toppo)) break; - } - int ii; - for (ii = 0; ii < 3; ii++) { - if (org(t) == pi) break; - enextself(t); - } - if (dest(t) == pj) { - } else { - eprevself(t); - fsymself(t); - } - break; + if (insertpoint(newpt, &searchtet, &searchsh, NULL, &ivf)) { + st_facref_count++; + if (steinerleft > 0) steinerleft--; + if (useinsertradius) { + save_facetpoint_insradius(newpt, ivf.parentpt, ivf.smlen); + } // 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. + face* paryseg; + int splitflag = 0; + for (i = 0; i < encseglist->objects; i++) { + paryseg = (face*)fastlookup(encseglist, i); + if (splitsegment(paryseg, NULL, 0.0, encpt, encpt1, qflag, chkencflag | 1)) { + splitflag = 1; // A point is inserted on a segment. + break; + } + } // i + encseglist->restart(); + if (splitflag) { + // Some segments may need to be repaired. + if (badsubsegs->items > 0) { + repairencsegs(chkencflag | 1); + } + return 1; + } + } else { + if (ivf.iloc == (int)NEARVERTEX) { + terminatetetgen(this, 2); + } + } + // Mark this subface to avoid splitting in the future. + smarktest3(*splitfac); + return 0; } - t.tet = tetrahedrontraverse(); - } - - puninfect(pi); - puninfect(pj); - puninfect(pk); - - if (t.tet != NULL) { - tetloop = t; - return 1; - } else { - return 0; - } } -int tetgenmesh::search_edge(point p0, point p1, triface &tetloop) -{ - triface t; - int ii; - - tetrahedrons->traversalinit(); - t.tet = tetrahedrontraverse(); - while (t.tet != NULL) { - for (ii = 0; ii < 6; ii++) { - t.ver = edge2ver[ii]; - if (((org(t) == p0) && (dest(t) == p1)) || - ((org(t) == p1) && (dest(t) == p0))) { - // Found the tet. - tetloop = t; - return 1; - } +/////////////////////////////////////////////////////////////////////////////// +// // +// repairencfacs() Repair encroached subfaces. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::repairencfacs(int chkencflag) { + face* bface; + point encpt = NULL; + int qflag = 0; + REAL ccent[3]; + + // 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(); + } } - t.tet = tetrahedrontraverse(); - } - tetloop.tet = NULL; - return 0; + if (badsubfacs->items > 0) { + if (steinerleft == 0) { + if (b->verbose) { + printf("The desired number of Steiner points is reached.\n"); + } + } else { + terminatetetgen(this, 2); + } + 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(); + } } /////////////////////////////////////////////////////////////////////////////// // // -// reconstructmesh() Reconstruct a tetrahedral mesh. // +// checktet4split() Check if the tet needs to be split. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::reconstructmesh() -{ - 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 cosang_tol, cosang; - REAL n1[3], n2[3]; - int eextras, marker = 0; - int bondflag; - int t1ver; - int idx, i, j, k; - - if (!b->quiet) { - printf("Reconstructing mesh ...\n"); - } - - if (b->convex) { // -c option. - // Assume the mesh is convex. Exterior tets have region attribute -1. - if (!(in->numberoftetrahedronattributes > 0)) { - terminatetetgen(this, 2); - } - } else { - // Assume the mesh is non-convex. - nonconvex = 1; - } - - // Create a map from indices to vertices. - makeindex2pointmap(idx2verlist); - // 'idx2verlist' has length 'in->numberofpoints + 1'. - if (in->firstnumber == 1) { - idx2verlist[0] = dummypoint; // Let 0th-entry be dummypoint. - } - - // Allocate an array that maps each vertex to its adjacent tets. - ver2tetarray = new tetrahedron[in->numberofpoints + 1]; - unuverts = in->numberofpoints; // All vertices are unused yet. - //for (i = 0; i < in->numberofpoints + 1; i++) { - for (i = in->firstnumber; i < in->numberofpoints + in->firstnumber; i++) { - ver2tetarray[i] = NULL; - } - - // 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++]]; - if (pointtype(p[j]) == UNUSEDVERTEX) { - setpointtype(p[j], VOLVERTEX); // initial type. - unuverts--; - } - } - // 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 - - // Remember a tet of the mesh. - recenttet = tetloop; - - // 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. - esym(hulltet, face1); - bond(face1, face2); - } - enextself(hulltet); +int tetgenmesh::checktet4split(triface* chktet, int& qflag, REAL* ccent) { + 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->convex) { // -c + // Skip this tet if it lies in the exterior. + if (elemattribute(chktet->tet, numelemattrib - 1) == -1.0) { + return 0; } - } - // 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; // The default marker. - } - if (marker != 0) { - idx = i * 3; - for (j = 0; j < 3; j++) { - p[j] = idx2verlist[in->trifacelist[idx++]]; - } - // Search the subface. - bondflag = 0; - neighsh.sh = NULL; - // 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; + + qflag = 0; + + pd = (point)chktet->tet[7]; + if (pd == dummypoint) { + return 0; // Do not split a hull tet. + } + + 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]; + + // 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 (!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; + } + + // 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; } - fnextself(tetloop); - if (apex(tetloop) == q[2]) break; - } } - if (!bondflag) { - if (neighsh.sh == NULL) { - if (b->verbose > 1) { - printf("Warning: Searching subface #%d [%d,%d,%d] mark=%d.\n", - i + in->firstnumber, pointmark(p[0]), pointmark(p[1]), - pointmark(p[2]), marker); + if (!qflag && b->varvolume) { + volbnd = volumebound(chktet->tet); + if ((volbnd > 0.0) && (vol > volbnd)) { + qflag = 1; } - // Search it globally. - if (search_face(p[0], p[1], p[2], tetloop)) { - bondflag = 1; - } - } } - 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); - } - setshellmark(subloop, marker); - // Insert the subface into the mesh. - tsbond(tetloop, subloop); - fsymself(tetloop); - sesymself(subloop); - tsbond(tetloop, subloop); - } else { - if (neighsh.sh != NULL) { - // The subface already exists. Only set its mark. - setshellmark(neighsh, marker); - } else { - if (!b->quiet) { - printf("Warning: Subface #%d [%d,%d,%d] mark=%d is not found.\n", - i + in->firstnumber, pointmark(p[0]), pointmark(p[1]), - pointmark(p[2]), marker); - } - } - } // 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. - } - } + 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 (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, -1); // 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) { - // Do not connect itself. - if (nextsh.sh != neighsh.sh) { - // Link neighsh <= nextsh. - sbond1(neighsh, nextsh); - neighsh = nextsh; + 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 (apex(tetloop) == q[2]) { - break; - } - } // while (1) - } // if (neighsh.sh == NULL) - senextself(subloop); + } } - subloop.sh = shellfacetraverse(subfaces); - } + 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; + } + } - // 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; // Default marker. - } - if (marker != 0) { - // Insert a segment. - idx = i * 2; - for (j = 0; j < 2; j++) { - p[j] = idx2verlist[in->edgelist[idx++]]; + 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; + } + } } - // 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; + // 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; + } + } else { + smrrv = rrv; + } } - // Search the segment. - bondflag = 0; - if (j == 2) { - if (getedge(p[0], p[1], &checktet)) { - bondflag = 1; - } else { - if (b->verbose > 1) { - printf("Warning: Searching segment #%d [%d,%d] mark=%d.\n", - i + in->firstnumber, pointmark(p[0]), pointmark(p[1]), marker); + if (smrrv > 0) { + // To avoid rounding error, round smrrv before doing comparison. + if ((fabs(smrrv - smlen) / smlen) < b->epsilon) { + smrrv = smlen; } - // Search it globally. - if (search_edge(p[0], p[1], checktet)) { - bondflag = 1; + if (smrrv > smlen) { + return 0; } - } - } - if (bondflag > 0) { - // 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, 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_separate_ang_tol' (degree). - cosang_tol = cos(b->facet_separate_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); - facenormal(p[0], p[1], p[2], n1, 1, NULL); - facenormal(p[0], p[1], p[3], n2, 1, NULL); - cosang = dot(n1, n2) / (sqrt(dot(n1, n1)) * sqrt(dot(n2, n2))); - // Rounding. - if (cosang > 1.0) cosang = 1.0; - else if (cosang < -1.0) cosang = -1.0; - if (cosang > cosang_tol) { - bondflag = 1; - } + } // if (useinsertradius) + + // 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 (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, -1); // Default 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++; - } + D = rd / smlen; + if (D > b->minratio) { + // A bad radius-edge ratio. + return 1; } - } - 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; - } + // 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); + } + 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])); + if (L[i] == 0) { + terminatetetgen(this, 2); + } + 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 [] idx2seglist; - delete [] segperverlist; - } - - - // Set global flags. - checksubsegflag = 1; - checksubfaceflag = 1; - - delete [] idx2verlist; - delete [] ver2tetarray; + return 0; } /////////////////////////////////////////////////////////////////////////////// // // -// scoutpoint() Search a point in mesh. // -// // -// 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 'randflag' is used, randomly select a start searching tet. Otherwise, // -// start searching directly from 'searchtet'. // +// splittetrahedron() Split a tetrahedron. // // // /////////////////////////////////////////////////////////////////////////////// -int tetgenmesh::scoutpoint(point searchpt, triface *searchtet, int randflag) -{ - point pa, pb, pc, pd; - enum locateresult loc = OUTSIDE; - REAL vol, ori1, ori2 = 0, ori3 = 0, ori4 = 0; - int t1ver; +int tetgenmesh::splittetrahedron(triface* splittet, int qflag, REAL* ccent, int chkencflag) { + triface searchtet; + face* paryseg; + point newpt, *ppt; + badface* bface; + insertvertexflags ivf; + int splitflag = 0; + int i; + REAL rv = 0.; // Insertion radius of 'newpt'. - // Randomly select a good starting tet. - if (randflag) { - randomsample(searchpt, searchtet); - } else { - if (searchtet->tet == NULL) { - *searchtet = recenttet; + makepoint(&newpt, FREEVOLVERTEX); + for (i = 0; i < 3; i++) + newpt[i] = ccent[i]; + + // Locate the new point. Starting from an interior point 'q' of the + // splittet. We perform a walk from q to the 'newpt', stop walking + // either we hit a subface or enter OUTSIDE. + searchtet = *splittet; + ivf.iloc = (int)OUTSIDE; + ivf.iloc = locate(newpt, &searchtet, 1); // 'chkencflag' = 1. + + if ((ivf.iloc == (int)OUTSIDE) || (ivf.iloc == (int)ENCSUBFACE)) { + // The circumcenter 'c' is not visible from 'q' (the interior of the tet). + // iffalse + if (b->verbose > 2) { + printf(" New point %d is blocked by a polygon.\n", pointmark(newpt)); + } + // \fi + pointdealloc(newpt); // Do not insert this vertex. + if (b->nobisect) return 0; // -Y option. + // There must be a polygon that blocks the visibility. + // Search a subpolygon that contains the proj(c). + face searchsh; + REAL prjpt[3]; + locateresult sloc = OUTSIDE; + tspivot(searchtet, searchsh); + ppt = (point*)&(searchsh.sh[3]); + projpt2face(ccent, ppt[0], ppt[1], ppt[2], prjpt); + // Locate proj(c) on polygon. + sloc = slocate(prjpt, &searchsh, 0, 0, 1); + if ((sloc == ONEDGE) || (sloc == ONFACE)) { + // Found a subface/edge containing proj(c). + // Check if 'c' encoraches upon this subface. + REAL fcent[3], r = 0; + ppt = (point*)&(searchsh.sh[3]); + if (checkfac4encroach(ppt[0], ppt[1], ppt[2], ccent, fcent, &r)) { + // Encroached. Split this subface. + splitflag = + splitsubface(&searchsh, NULL, org(*splittet), qflag, fcent, chkencflag | 2); + if (splitflag) { + // Some subfaces may need to be repaired. + repairencfacs(chkencflag | 2); + } + } + } else if ((sloc == OUTSIDE) || (sloc == ENCSEGMENT)) { + // Hit a segment. We should split it. + // To be done... + // printf("hit segment, split it.\n"); // For debug only + } + if (splitflag) { + // Queue the tet if it is still alive. + if ((splittet->tet != NULL) && (splittet->tet[4] != NULL)) { + enqueuetetrahedron(splittet); + } + } + return splitflag; } - } - loc = locate(searchpt, searchtet); - if (loc == OUTSIDE) { - if (b->convex) { // -c option - // The point lies outside of the convex hull. - return (int) loc; + // 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. } - // 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); + 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 (useinsertradius) { + // Need to save insertion radius for this new point. + ivf.smlenflag = 1; // Return the shortest edge length after inserting + // the new vertex. [2016-09-19] } - } - 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]. - 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] - } + if (insertpoint(newpt, &searchtet, NULL, NULL, &ivf)) { + // Vertex is inserted. + st_volref_count++; + if (steinerleft > 0) steinerleft--; + if (useinsertradius) { + setpointinsradius(newpt, ivf.smlen); + setpoint2ppt(newpt, ivf.parentpt); } - } 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; - } + if (flipstack != NULL) { + flipconstraints fc; + fc.chkencflag = chkencflag; + fc.enqflag = 2; + lawsonflip3d(&fc); + unflipqueue->restart(); } - } - } 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; - } + return 1; + } else { + // Point is not inserted. + pointdealloc(newpt); + // Check if there are encroached segments/subfaces. + if (ivf.iloc == (int)ENCSEGMENT) { + 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. + if (badsubsegs->items > 0) { + repairencsegs(chkencflag | 3); + } + // Some subfaces may need to be repaired. + if (badsubfacs->items > 0) { + repairencfacs(chkencflag | 2); + } + } + } else if (ivf.iloc == (int)ENCSUBFACE) { + 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) { + // Some subfaces may need to be repaired. + if (badsubfacs->items > 0) { + repairencfacs(chkencflag | 2); + } + } + } else { + if (ivf.iloc == (int)NEARVERTEX) { + terminatetetgen(this, 2); + } } - } 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; - } + if (splitflag) { + // Queue the tet if it is still alive. + if ((splittet->tet != NULL) && (splittet->tet[4] != NULL)) { + enqueuetetrahedron(splittet); + } } 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 (int) loc; + // assert(0); // If no small angle, why can this happen? + } + return splitflag; + } } /////////////////////////////////////////////////////////////////////////////// // // -// getpointmeshsize() Interpolate the mesh size at given point. // -// // -// 'iloc' indicates the location of the point w.r.t. 'searchtet'. The size // -// is obtained by linear interpolation on the vertices of the tet. // +// repairbadtets() Repair bad quality tetrahedra. // // // /////////////////////////////////////////////////////////////////////////////// -REAL tetgenmesh::getpointmeshsize(point searchpt, triface *searchtet, int iloc) -{ - point *pts, pa, pb, pc; - REAL volume, vol[4], wei[4]; - REAL size; - int i; - - size = 0; - - if (iloc == (int) INTETRAHEDRON) { - pts = (point *) &(searchtet->tet[4]); - // 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++) { - wei[i] = fabs(vol[i] / volume); - size += (wei[i] * pts[i][pointmtrindex]); - } - } - } 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]; +void tetgenmesh::repairbadtets(int chkencflag) { + triface* bface; + REAL ccent[3]; + int qflag = 0; + + // 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(); + } } - } - return size; + if (badtetrahedrons->items > 0) { + if (steinerleft == 0) { + if (b->verbose) { + printf("The desired number of Steiner points is reached.\n"); + } + } else { + terminatetetgen(this, 2); // 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(); + } + // Clear the pool. + badtetrahedrons->restart(); + } } /////////////////////////////////////////////////////////////////////////////// // // -// interpolatemeshsize() Interpolate the mesh size from a background mesh // -// (source) to the current mesh (destination). // +// delaunayrefinement() Refine the mesh by Delaunay refinement. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::interpolatemeshsize() -{ - triface searchtet; - point ploop; - REAL minval = 0.0, maxval = 0.0; - int iloc; - int count; - - if (!b->quiet) { - printf("Interpolating mesh size ...\n"); - } - - long bak_nonregularcount = nonregularcount; - nonregularcount = 0l; // Count the number of (slow) global searches. - long baksmaples = bgm->samples; - bgm->samples = 3l; - count = 0; // Count the number of interpolated points. - - // Interpolate sizes for all points in the current mesh. - 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)); - } - } - ploop = pointtraverse(); - } - - if (b->verbose) { - printf(" Interoplated %d points.\n", count); - if (nonregularcount > 0l) { - printf(" Performed %ld brute-force searches.\n", nonregularcount); - } - printf(" Size rangle [%.17g, %.17g].\n", minval, maxval); - } +void tetgenmesh::delaunayrefinement() { + triface checktet; + face checksh; + face checkseg; + long steinercount; + int chkencflag; - bgm->samples = baksmaples; - nonregularcount = bak_nonregularcount; -} + long bak_segref_count, bak_facref_count, bak_volref_count; + long bak_flipcount = flip23count + flip32count + flip44count; -/////////////////////////////////////////////////////////////////////////////// -// // -// insertconstrainedpoints() Insert a list of points into the mesh. // -// // -// Assumption: The bounding box of the insert point set should be no larger // -// than the bounding box of the mesh. (Required by point sorting). // -// // -/////////////////////////////////////////////////////////////////////////////// + if (!b->quiet) { + printf("Refining mesh...\n"); + } -void tetgenmesh::insertconstrainedpoints(point *insertarray, int arylen, - int rejflag) -{ - triface searchtet, spintet; - face splitsh; - face splitseg; - insertvertexflags ivf; - flipconstraints fc; - int randflag = 0; - int t1ver; - int i; - - if (b->verbose) { - printf(" Inserting %d constrained points\n", arylen); - } - - if (b->no_sort) { // -b/1 option. if (b->verbose) { - printf(" Using the input order.\n"); + printf(" Min radius-edge ratio = %g.\n", b->minratio); + printf(" Min dihedral angle = %g.\n", b->mindihedral); + // printf(" Min Edge length = %g.\n", b->minedgelength); } - } else { - if (b->verbose) { - printf(" Permuting vertices.\n"); + + 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. + } } - 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; - } - } - - // 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(); - } - } // i - - delete encseglist; - delete encshlist; - - if (b->verbose) { - printf(" Inserted %ld (%ld, %ld, %ld) vertices.\n", - st_segref_count + st_facref_count + st_volref_count - - (bak_seg_count + bak_fac_count + bak_vol_count), - st_segref_count - bak_seg_count, st_facref_count - bak_fac_count, - st_volref_count - bak_vol_count); - if (nonregularcount > 0l) { - printf(" Performed %ld brute-force searches.\n", nonregularcount); - } - } - - nonregularcount = bak_nonregularcount; - samples = baksmaples; -} -void tetgenmesh::insertconstrainedpoints(tetgenio *addio) -{ - point *insertarray, newpt; - REAL x, y, z, w; - int index, attribindex, mtrindex; - int arylen, i, j; - - if (!b->quiet) { - printf("Inserting constrained points ...\n"); - } - - insertarray = new point[addio->numberofpoints]; - arylen = 0; - index = 0; - attribindex = 0; - mtrindex = 0; - - 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 (useinsertradius) { + if ((b->plc && b->nobisect) || b->refine) { // '-pY' or '-r' option. + makesegmentendpointsmap(); + makefacetverticesmap(); + } } - 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. - } - - insertconstrainedpoints(insertarray, arylen, rejflag); - - delete [] insertarray; -} -/////////////////////////////////////////////////////////////////////////////// -// // -// meshcoarsening() Deleting (selected) vertices. // -// // -/////////////////////////////////////////////////////////////////////////////// + encseglist = new arraypool(sizeof(face), 8); + encshlist = new arraypool(sizeof(badface), 8); -void tetgenmesh::collectremovepoints(arraypool *remptlist) -{ - point ptloop, *parypt; - verttype vt; + // if (!b->nobisect) { // if no '-Y' option + if (!b->nobisect || checkconstraints) { + if (b->verbose) { + printf(" Splitting encroached subsegments.\n"); + } - // 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) { - // Do not remove a boundary vertex - vt = pointtype(ptloop); - if ((vt == RIDGEVERTEX) || (vt == ACUTEVERTEX) || (vt == FACETVERTEX) || - (vt == FREEFACETVERTEX) || (vt == FREESEGVERTEX) || (vt == UNUSEDVERTEX)) { - ptloop = pointtraverse(); - continue; - } - 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; - } + chkencflag = 1; // Only check encroaching subsegments. + steinercount = points->items; + + // Initialize the pool of encroached subsegments. + badsubsegs = new memorypool(sizeof(face), b->shellfaceperblock, sizeof(void*), 0); + + // Add all segments into the pool. + subsegs->traversalinit(); + checkseg.sh = shellfacetraverse(subsegs); + while (checkseg.sh != (shellface*)NULL) { + enqueuesubface(badsubsegs, &checkseg); + checkseg.sh = shellfacetraverse(subsegs); } - cavetetvertlist->restart(); - cavetetlist->restart(); - if (smlen < ptloop[pointmtrindex]) { - pinfect(ptloop); - remptlist->newindex((void **) &parypt); - *parypt = ptloop; + + // Split all encroached segments. + repairencsegs(chkencflag); + + if (b->verbose) { + printf(" Added %ld Steiner points.\n", points->items - steinercount); } - } - ptloop = pointtraverse(); - } - if (b->verbose > 1) { - printf(" Coarsen %ld oversized points.\n", remptlist->objects); - } - } - // 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) + if (b->reflevel > 1) { // '-D2' option + if (b->verbose) { + printf(" Splitting encroached subfaces.\n"); + } - if (b->coarsen_param > 0) { // -R1/# - // Remove a coarsen_percent number of interior points. - 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; - } - - // Unmark all collected vertices. - for (int i = 0; i < remptlist->objects; i++) { - parypt = (point *) fastlookup(remptlist, i); - puninfect(*parypt); - } -} + chkencflag = 2; // Only check encroaching subfaces. + steinercount = points->items; + bak_segref_count = st_segref_count; + bak_facref_count = st_facref_count; -void tetgenmesh::meshcoarsening() -{ - arraypool *remptlist; + // Initialize the pool of encroached subfaces. + badsubfacs = new memorypool(sizeof(face), b->shellfaceperblock, sizeof(void*), 0); - if (!b->quiet) { - printf("Mesh coarsening ...\n"); - } + // Add all subfaces into the pool. + subfaces->traversalinit(); + checksh.sh = shellfacetraverse(subfaces); + while (checksh.sh != (shellface*)NULL) { + enqueuesubface(badsubfacs, &checksh); + checksh.sh = shellfacetraverse(subfaces); + } - // Collect the set of points to be removed - remptlist = new arraypool(sizeof(point *), 10); - collectremovepoints(remptlist); + // Split all encroached subfaces. + repairencfacs(chkencflag); - if (remptlist->objects == 0l) { - delete remptlist; - return; - } + 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->verbose) { - if (remptlist->objects > 0l) { - printf(" Removing %ld points...\n", remptlist->objects); - } - } + if (b->reflevel > 2) { // '-D3' option (The default option) + if (b->verbose) { + printf(" Splitting bad quality tets.\n"); + } - point *parypt, *plastpt; - long ms = remptlist->objects; - int nit = 0; - int bak_fliplinklevel = b->fliplinklevel; - b->fliplinklevel = -1; - autofliplinklevel = 1; // Init value. - int i; + 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; - 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); - } + // The cosine value of the min dihedral angle (-qq) for tetrahedra. + cosmindihed = cos(b->mindihedral / 180.0 * PI); - // Remove the list of points. - for (i = 0; i < remptlist->objects; i++) { - parypt = (point *) fastlookup(remptlist, i); - if (removevertexbyflips(*parypt)) { - // Move the last entry to the current place. - plastpt = (point *) fastlookup(remptlist, remptlist->objects - 1); - *parypt = *plastpt; - remptlist->objects--; - i--; - } - } + // 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(); + } - 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) + // Split all bad quality tetrahedra. + repairbadtets(chkencflag); + + 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 (b->reflevel > 2) - if (remptlist->objects > 0l) { if (b->verbose) { - printf(" %ld points are not removed !\n", remptlist->objects); + if (flip23count + flip32count + flip44count > bak_flipcount) { + printf(" Performed %ld flips.\n", + flip23count + flip32count + flip44count - bak_flipcount); + } + } + + if (steinerleft == 0) { + if (!b->quiet) { + printf("\nWarnning: "); + printf("The desired number of Steiner points (%d) is reached.\n\n", b->steinerleft); + } } - } - b->fliplinklevel = bak_fliplinklevel; - delete remptlist; + delete encseglist; + delete encshlist; + encseglist = NULL; + encshlist = NULL; + + if (!b->nobisect || checkconstraints) { + totalworkmemory += (badsubsegs->maxitems * badsubsegs->itembytes); + delete badsubsegs; + badsubsegs = NULL; + if (b->reflevel > 1) { + totalworkmemory += (badsubfacs->maxitems * badsubfacs->itembytes); + delete badsubfacs; + badsubfacs = NULL; + } + } + if (b->reflevel > 2) { + totalworkmemory += (badtetrahedrons->maxitems * badtetrahedrons->itembytes); + delete badtetrahedrons; + badtetrahedrons = NULL; + } } //// //// //// //// -//// reconstruct_cxx ////////////////////////////////////////////////////////// - //// refine_cxx /////////////////////////////////////////////////////////////// + +//// optimize_cxx ///////////////////////////////////////////////////////////// //// //// //// //// /////////////////////////////////////////////////////////////////////////////// // // -// makefacetverticesmap() Create a map from facet to its vertices. // -// // -// All facets will be indexed (starting from 0). The map is saved in two // -// global arrays: 'idx2facetlist' and 'facetverticeslist'. // +// lawsonflip3d() A three-dimensional Lawson's algorithm. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::makefacetverticesmap() -{ - arraypool *facetvertexlist, *vertlist, **paryvertlist; - face subloop, neighsh, *parysh, *parysh1; - point pa, *ppt, *parypt; - verttype vt; - int facetindex, totalvertices; - int i, j, k; - - 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]; - } - } - 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); - 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; +long tetgenmesh::lawsonflip3d(flipconstraints* fc) { + triface fliptets[5], neightet, hulltet; + face checksh, casingout; + badface *popface, *bface; + point pd, pe, *pts; + REAL sign, ori; + REAL vol, len3; + long flipcount, totalcount = 0l; + long sliver_peels = 0l; + int t1ver; + int i; + + while (1) { + + if (b->verbose > 2) { + printf(" Lawson flip %ld faces.\n", flippool->items); + } + flipcount = 0l; + + while (flipstack != (badface*)NULL) { + // Pop a face from the stack. + popface = flipstack; + fliptets[0] = popface->tt; + flipstack = flipstack->nextitem; // The next top item in stack. + flippool->dealloc((void*)popface); + + // Skip it if it is a dead tet (destroyed by previous flips). + if (isdeadtet(fliptets[0])) continue; + // Skip it if it is not the same tet as we saved. + if (!facemarked(fliptets[0])) continue; + + unmarkface(fliptets[0]); + + if (ishulltet(fliptets[0])) continue; + + fsym(fliptets[0], fliptets[1]); + if (ishulltet(fliptets[1])) { + if (nonconvex) { + // Check if 'fliptets[0]' it is a hull sliver. + tspivot(fliptets[0], checksh); + for (i = 0; i < 3; i++) { + if (!isshsubseg(checksh)) { + spivot(checksh, casingout); + // assert(casingout.sh != NULL); + if (sorg(checksh) != sdest(casingout)) sesymself(casingout); + stpivot(casingout, neightet); + if (neightet.tet == fliptets[0].tet) { + // Found a hull sliver 'neightet'. Let it be [e,d,a,b], where + // [e,d,a] and [d,e,b] are hull faces. + edestoppo(neightet, hulltet); // [a,b,e,d] + fsymself(hulltet); // [b,a,e,#] + if (oppo(hulltet) == dummypoint) { + pe = org(neightet); + if ((pointtype(pe) == FREEFACETVERTEX) || + (pointtype(pe) == FREESEGVERTEX)) { + removevertexbyflips(pe); + } + } else { + eorgoppo(neightet, hulltet); // [b,a,d,e] + fsymself(hulltet); // [a,b,d,#] + if (oppo(hulltet) == dummypoint) { + pd = dest(neightet); + if ((pointtype(pd) == FREEFACETVERTEX) || + (pointtype(pd) == FREESEGVERTEX)) { + removevertexbyflips(pd); + } + } else { + // Perform a 3-to-2 flip to remove the sliver. + fliptets[0] = neightet; // [e,d,a,b] + fnext(fliptets[0], fliptets[1]); // [e,d,b,c] + fnext(fliptets[1], fliptets[2]); // [e,d,c,a] + flip32(fliptets, 1, fc); + // Update counters. + flip32count--; + flip22count--; + sliver_peels++; + if (fc->remove_ndelaunay_edge) { + // Update the volume (must be decreased). + // assert(fc->tetprism_vol_sum <= 0); + tetprism_vol_sum += fc->tetprism_vol_sum; + fc->tetprism_vol_sum = 0.0; // Clear it. + } + } + } + break; + } // if (neightet.tet == fliptets[0].tet) + } // if (!isshsubseg(checksh)) + senextself(checksh); + } // i + } // if (nonconvex) + continue; + } + + 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]); + + // Use the length of the edge [d,e] as a reference to determine + // a nearly degenerated new tet. + len3 = distance(pd, pe); + len3 = (len3 * len3 * len3); + int round_flag = 0; // [2017-10-20] + // 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) { + // Avoid creating a nearly degenerated new tet at boundary. + // Re-use fliptets[2], fliptets[3]; + esym(fliptets[0], fliptets[2]); + esym(fliptets[1], fliptets[3]); + if (issubface(fliptets[2]) || issubface(fliptets[3])) { + vol = orient3dfast(org(fliptets[0]), dest(fliptets[0]), pd, pe); + if ((fabs(vol) / len3) < b->epsilon) { + ori = 0.0; // Do rounding. + round_flag = 1; // [2017-10-20] + } + } + } // Rounding check + if (ori <= 0) break; + enextself(fliptets[0]); + eprevself(fliptets[1]); } - } - sinfect(neighsh); - caveshlist->newindex((void **) &parysh1); - *parysh1 = neighsh; + + 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 (round_flag == 1) { + continue; // [2017-10-20] + } + 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. + } + /////// Debug + // if (checkmesh(0) > 0) { + // assert(0); + //} + 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); } - } - senextself(*parysh); } - } // 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); - } + // Accumulate the counter of flips. + totalcount += flipcount; - // All subfaces are infected. Uninfect them. - subfaces->traversalinit(); - subloop.sh = shellfacetraverse(subfaces); - while (subloop.sh != NULL) { - suninfect(subloop); - subloop.sh = shellfacetraverse(subfaces); - } - - if (b->verbose) { - printf(" Found %ld facets.\n", facetvertexlist->objects); - } - - 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++; - } - } - - // Free the lists. - for (i = 0; i < facetvertexlist->objects; i++) { - paryvertlist = (arraypool **) fastlookup(facetvertexlist, i); - vertlist = *paryvertlist; - delete vertlist; - } - delete facetvertexlist; + // Return if no unflippable faces left. + if (unflipqueue->objects == 0l) break; + // Return if no flip has been performed. + if (flipcount == 0l) break; + + // Try to flip the unflippable faces. + for (i = 0; i < unflipqueue->objects; i++) { + bface = (badface*)fastlookup(unflipqueue, i); + if (!isdeadtet(bface->tt) && (org(bface->tt) == bface->forg) && + (dest(bface->tt) == bface->fdest) && (apex(bface->tt) == bface->fapex)) { + flippush(flipstack, &(bface->tt)); + } + } + unflipqueue->restart(); + + } // 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; } /////////////////////////////////////////////////////////////////////////////// // // -// Check whether two segments, or a segment and a facet, or two facets are // -// adjacent to each other. // +// recoverdelaunay() Recovery the locally Delaunay property. // // // /////////////////////////////////////////////////////////////////////////////// -int tetgenmesh::segsegadjacent(face *seg1, face *seg2) -{ - int segidx1 = getfacetindex(*seg1); - int segidx2 = getfacetindex(*seg2); +void tetgenmesh::recoverdelaunay() { + arraypool *flipqueue, *nextflipqueue, *swapqueue; + triface tetloop, neightet, *parytet; + badface *bface, *parybface; + point* ppt; + flipconstraints fc; + int i, j; - if (segidx1 == segidx2) return 0; + if (!b->quiet) { + printf("Recovering Delaunayness...\n"); + } - point pa1 = segmentendpointslist[segidx1 * 2]; - point pb1 = segmentendpointslist[segidx1 * 2 + 1]; - point pa2 = segmentendpointslist[segidx2 * 2]; - point pb2 = segmentendpointslist[segidx2 * 2 + 1]; + tetprism_vol_sum = 0.0; // Initialize it. - if ((pa1 == pa2) || (pa1 == pb2) || (pb1 == pa2) || (pb1 == pb2)) { - return 1; - } - return 0; -} + // 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); + } + } + ppt = (point*)&(tetloop.tet[4]); + tetprism_vol_sum += tetprismvol(ppt[0], ppt[1], ppt[2], ppt[3]); + tetloop.tet = tetrahedrontraverse(); + } -int tetgenmesh::segfacetadjacent(face *subseg, face *subsh) -{ - int segidx = getfacetindex(*subseg); - point pa = segmentendpointslist[segidx * 2]; - point pb = segmentendpointslist[segidx * 2 + 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); + } - pinfect(pa); - pinfect(pb); + if (b->verbose > 1) { + printf(" Recover Delaunay [Lawson] : %ld\n", flippool->items); + } - int fidx = getfacetindex(*subsh); - int count = 0, i; + // First only use the basic Lawson's flip. + fc.remove_ndelaunay_edge = 1; + fc.enqflag = 2; - for (i = idx2facetlist[fidx]; i < idx2facetlist[fidx+1]; i++) { - if (pinfected(facetverticeslist[i])) count++; - } + lawsonflip3d(&fc); - puninfect(pa); - puninfect(pb); + if (b->verbose > 1) { + printf(" obj (after Lawson) = %.17g\n", tetprism_vol_sum); + } - return count == 1; -} + if (unflipqueue->objects == 0l) { + return; // The mesh is Delaunay. + } -int tetgenmesh::facetfacetadjacent(face *subsh1, face *subsh2) -{ - int count = 0, i; + fc.unflip = 1; // Unflip if the edge is not flipped. + fc.collectnewtets = 1; // new tets are returned in 'cavetetlist'. + fc.enqflag = 0; - int fidx1 = getfacetindex(*subsh1); - int fidx2 = getfacetindex(*subsh2); + autofliplinklevel = 1; // Init level. + b->fliplinklevel = -1; // No fixed level. - if (fidx1 == fidx2) return 0; + // For efficiency reason, we limit the maximium size of the edge star. + int bakmaxflipstarsize = b->flipstarsize; + b->flipstarsize = 10; // default - for (i = idx2facetlist[fidx1]; i < idx2facetlist[fidx1+1]; i++) { - pinfect(facetverticeslist[i]); - } + flipqueue = new arraypool(sizeof(badface), 10); + nextflipqueue = new arraypool(sizeof(badface), 10); - for (i = idx2facetlist[fidx2]; i < idx2facetlist[fidx2+1]; i++) { - if (pinfected(facetverticeslist[i])) count++; - } + // Swap the two flip queues. + swapqueue = flipqueue; + flipqueue = unflipqueue; + unflipqueue = swapqueue; - // Uninfect the vertices. - for (i = idx2facetlist[fidx1]; i < idx2facetlist[fidx1+1]; i++) { - puninfect(facetverticeslist[i]); - } + while (flipqueue->objects > 0l) { - return count > 0; -} + if (b->verbose > 1) { + printf(" Recover Delaunay [level = %2d] #: %ld.\n", autofliplinklevel, + flipqueue->objects); + } -/////////////////////////////////////////////////////////////////////////////// -// // -// save_segmentpoint_insradius(), save_facetpoint_insradius() // -// // -// Determine and save the relaxed insertion radius of a Steiner point on a // -// segment or a facet. By default, it is the closet distance to the parent // -// point of this Steiner point. But may be larger than it. // -// // -/////////////////////////////////////////////////////////////////////////////// + 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 { + // Unable to remove this edge. Save it. + nextflipqueue->newindex((void**)&parybface); + *parybface = *bface; + // Normally, it should be zero. + // assert(fc.tetprism_vol_sum == 0.0); + // However, due to rounding errors, a tiny value may appear. + fc.tetprism_vol_sum = 0.0; + } + } + } // i -void tetgenmesh::save_segmentpoint_insradius(point segpt,point parentpt,REAL r) -{ - REAL rv = r, rp; - if (pointtype(parentpt) == FREESEGVERTEX) { - face parentseg1, parentseg2; - sdecode(point2sh(segpt), parentseg1); - sdecode(point2sh(parentpt), parentseg2); - if (segsegadjacent(&parentseg1, &parentseg2)) { - rp = getpointinsradius(parentpt); - if (rv < rp) { - // The relaxed insertion radius of 'newpt'. - rv = rp; - } - } - } else if (pointtype(parentpt) == FREEFACETVERTEX) { - face parentseg, parentsh; - sdecode(point2sh(segpt), parentseg); - sdecode(point2sh(parentpt), parentsh); - if (segfacetadjacent(&parentseg, &parentsh)) { - rp = getpointinsradius(parentpt); - if ((sqrt(2.0) * rv) < rp) { // if (rv < rp) { - // The relaxed insertion radius of 'newpt'. - rv = rp / sqrt(2.0); // rv = rp; - } - } - } - setpointinsradius(segpt, rv); -} + if (b->verbose > 1) { + printf(" obj (after level %d) = %.17g.\n", autofliplinklevel, tetprism_vol_sum); + } + flipqueue->restart(); -void tetgenmesh::save_facetpoint_insradius(point facpt,point parentpt,REAL r) -{ - REAL rv = r, rp; - if (pointtype(parentpt) == FREESEGVERTEX) { - face parentseg, parentsh; - sdecode(point2sh(parentpt), parentseg); - sdecode(point2sh(facpt), parentsh); - if (segfacetadjacent(&parentseg, &parentsh)) { - rp = getpointinsradius(parentpt); - if (rv < (sqrt(2.0) * rp)) { - rv = sqrt(2.0) * rp; // The relaxed insertion radius of 'newpt'. - } - } - } else if (pointtype(parentpt) == FREEFACETVERTEX) { - face parentsh1, parentsh2; - sdecode(point2sh(parentpt), parentsh1); - sdecode(point2sh(facpt), parentsh2); - if (facetfacetadjacent(&parentsh1, &parentsh2)) { - rp = getpointinsradius(parentpt); - if (rv < rp) { - rv = rp; // The relaxed insertion radius of 'newpt'. - } - } - } - setpointinsradius(facpt, rv); -} + // Swap the two flip queues. + swapqueue = flipqueue; + flipqueue = nextflipqueue; + nextflipqueue = swapqueue; -/////////////////////////////////////////////////////////////////////////////// -// // -// enqueuesubface() Queue a subface or a subsegment for encroachment chk. // -// // -/////////////////////////////////////////////////////////////////////////////// + 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) -void tetgenmesh::enqueuesubface(memorypool *pool, face *chkface) -{ - if (!smarktest2ed(*chkface)) { - smarktest2(*chkface); // Only queue it once. - face *queface = (face *) pool->alloc(); - *queface = *chkface; - } -} + if (flipqueue->objects > 0l) { + if (b->verbose > 1) { + printf(" %ld non-Delaunay edges remained.\n", flipqueue->objects); + } + } -/////////////////////////////////////////////////////////////////////////////// -// // -// enqueuetetrahedron() Queue a tetrahedron for quality check. // -// // -/////////////////////////////////////////////////////////////////////////////// + if (b->verbose) { + printf(" Final obj = %.17g\n", tetprism_vol_sum); + } -void tetgenmesh::enqueuetetrahedron(triface *chktet) -{ - if (!marktest2ed(*chktet)) { - marktest2(*chktet); // Only queue it once. - triface *quetet = (triface *) badtetrahedrons->alloc(); - *quetet = *chktet; - } + b->flipstarsize = bakmaxflipstarsize; + delete flipqueue; + delete nextflipqueue; } /////////////////////////////////////////////////////////////////////////////// // // -// checkseg4encroach() Check if an edge is encroached upon by a point. // +// gettetrahedron() Get a tetrahedron which have the given vertices. // // // /////////////////////////////////////////////////////////////////////////////// -int tetgenmesh::checkseg4encroach(point pa, point pb, point checkpt) -{ - // Check if the point lies inside the diametrical sphere of this seg. - REAL v1[3], v2[3]; - - 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 { - return 1; // NO protecting ball. Encroached. - } - } else { - return 1; // Inside! Encroached. +int tetgenmesh::gettetrahedron(point pa, point pb, point pc, point pd, triface* searchtet) { + triface spintet; + int t1ver; + + 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 (apex(*searchtet) == pc) { + if (oppo(*searchtet) == pd) { + return 1; + } else { + fsymself(*searchtet); + if (oppo(*searchtet) == pd) { + return 1; + } + } + } } - } - return 0; + return 0; } /////////////////////////////////////////////////////////////////////////////// // // -// 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. // -// // -// 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. // +// improvequalitybyflips() Improve the mesh quality by flips. // // // /////////////////////////////////////////////////////////////////////////////// -int tetgenmesh::checkseg4split(face *chkseg, point& encpt, int& qflag) -{ - REAL ccent[3], len, r; - int i; - - point forg = sorg(*chkseg); - point fdest = sdest(*chkseg); - - // Initialize the return values. - encpt = NULL; - qflag = 0; - - 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; +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); } - } - } else { - encpt = eapex; - break; + + 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++; + } + } + } // i + if (!remflag) { + // An unremoved bad tet. Queue it again. + unflipqueue->newindex((void**)&parybface); + *parybface = *bface; + } + } // 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); } - } - } - fnextself(spintet); - if (spintet.tet == searchtet.tet) break; - } // while (1) + totalremcount += remcount; - if (encpt != NULL) { - return 1; - } + if (unflipqueue->objects > 0l) { + // if (autofliplinklevel >= b->optmaxfliplevel) { + if (autofliplinklevel >= b->optlevel) { + break; + } + autofliplinklevel += b->fliplinklevelinc; + // b->flipstarsize = 10 + (1 << (b->optlevel - 1)); + } + + // Swap the two flip queues. + swapqueue = flipqueue; + flipqueue = unflipqueue; + unflipqueue = swapqueue; + } // while (flipqueues->objects > 0) - return 0; // No need to split it. + // Restore original flip edge options. + autofliplinklevel = bakautofliplinklevel; + b->fliplinklevel = bakfliplinklevel; + b->flipstarsize = bakmaxflipstarsize; + + delete flipqueue; + delete nextflipqueue; + + return totalremcount; } /////////////////////////////////////////////////////////////////////////////// // // -// splitsegment() Split a segment. // +// 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. // +// // +// '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). // // // -// 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. // +// '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. // // // /////////////////////////////////////////////////////////////////////////////// -int tetgenmesh::splitsegment(face *splitseg, point encpt, REAL rrp, - point encpt1, point encpt2, int qflag, - int chkencflag) -{ - - if (!qflag && smarktest3ed(*splitseg)) { - // Do not try to re-split a marked segment. - return 0; - } +int tetgenmesh::smoothpoint(point smtpt, arraypool* linkfacelist, int ccw, optparameters* opm) { + 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 (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 (spinsh.sh == NULL) break; // It belongs to only one facet. - } - if ((!spinsh.sh) || (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. + // Decide the number of moving directions. + numdirs = (int)linkfacelist->objects; + if (numdirs > opm->numofsearchdirs) { + numdirs = opm->numofsearchdirs; // Maximum search directions. } - } // if (b->nobisect) - triface searchtet; - face searchsh; - point newpt; - insertvertexflags ivf; + // Set the initial value. + opm->imprval = opm->initval; + iter = 0; - makepoint(&newpt, FREESEGVERTEX); - getsteinerptonsegment(splitseg, encpt, newpt); + for (i = 0; i < 3; i++) { + bestpt[i] = startpt[i] = smtpt[i]; + } - if (!qflag && !b->cdtrefine) { - // Do not insert the point if it encroaches upon an adjacent segment. - face parentsh; - spivot(*splitseg, parentsh); - if (parentsh.sh != NULL) { - face spinsh, neighsh; - face neighseg; - spinsh = parentsh; - while (1) { - for (int i = 0; i < 2; i++) { - if (i == 0) { - senext(spinsh, neighsh); - } else { - senext2(spinsh, neighsh); - } - if (isshsubseg(neighsh)) { - sspivot(neighsh, neighseg); - if (checkseg4encroach(sorg(neighseg), sdest(neighseg), newpt)) { - pointdealloc(newpt); - return 0; // Do not split this segment. + // Iterate until the obj function is not improved. + while (1) { + + // Find the best next location. + oldval = opm->imprval; + + 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 { + pb = org(*parytet); + pa = dest(*parytet); + } + 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->min_max_aspectratio) { + val = 1.0 / 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 - spivotself(spinsh); - if (spinsh.sh == NULL) break; - if (spinsh.sh == parentsh.sh) break; - } // while (1) - } - } - - // Split the segment by the Bowyer-Watson algorithm. - sstpivot1(*splitseg, searchtet); - ivf.iloc = (int) ONEDGE; - ivf.bowywat = 3; // Use Bowyer-Watson, preserve subsegments and subfaces; - 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; // Return the closet mesh vertex. - - - if (insertpoint(newpt, &searchtet, &searchsh, splitseg, &ivf)) { - st_segref_count++; - if (steinerleft > 0) steinerleft--; - if (useinsertradius) { - save_segmentpoint_insradius(newpt, ivf.parentpt, ivf.smlen); - } - if (flipstack != NULL) { - flipconstraints fc; - fc.chkencflag = chkencflag; - fc.enqflag = 2; - lawsonflip3d(&fc); - unflipqueue->restart(); - } - return 1; - } else { - // Point is not inserted. - if (ivf.iloc == (int) NEARVERTEX) { - terminatetetgen(this, 2); - } - pointdealloc(newpt); - // Mark this segment to avoid splitting in the future. - smarktest3(*splitseg); - return 0; - } -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// repairencsegs() Repair encroached (sub) segments. // -// // -/////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::repairencsegs(int chkencflag) -{ - face *bface; - point encpt = NULL; - int qflag = 0; - - // 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); + 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->min_max_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. + terminatetetgen(this, 2); } - } } - // Remove this entry from list. - bface->shver = -1; // Signal it as a deleted element. - badsubsegs->dealloc((void *) bface); - } - bface = (face *) badsubsegs->traverse(); - } - } - if (badsubsegs->items > 0) { - if (b->verbose) { - printf("The desired number of Steiner points is reached.\n"); - } - 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); - } + 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; } - } - bface = (face *) badsubsegs->traverse(); - } - badsubsegs->restart(); - } -} -/////////////////////////////////////////////////////////////////////////////// -// // -// checkfac4encroach() Check if a subface is encroached by a point. // -// // -/////////////////////////////////////////////////////////////////////////////// + } // while (1) -int tetgenmesh::checkfac4encroach(point pa, point pb, point pc, point checkpt, - REAL* cent, REAL* r) -{ - REAL rd, len; - int encroached = 0; - - circumsphere(pa, pb, pc, NULL, cent, &rd); - if (rd == 0) { - terminatetetgen(this, 2); - } - - if (b->use_equatorial_lens) { - REAL normal[3], fcenter[3]; - REAL xta, yta, zta; - REAL multiplier; - - fcenter[0] = cent[0] - pc[0]; - fcenter[1] = cent[1] - pc[1]; - fcenter[2] = cent[2] - pc[2]; - - // Get the normal of the oriented face [a->b->c], without normalized. - facenormal(pa, pb, pc, normal, 1, NULL); - multiplier = 0.985 * sqrt((fcenter[0]*fcenter[0] + fcenter[1]*fcenter[1] + - fcenter[2]*fcenter[2]) / - (3.0 * (normal[0] * normal[0] + normal[1] * normal[1] + - normal[2] * normal[2]))); - xta = checkpt[0] - pc[0]; - yta = checkpt[1] - pc[1]; - zta = checkpt[2] - pc[2]; - // Make sure that the normal is pointing to "checkpt". - if ((xta * normal[0] + yta * normal[1] + zta * normal[2]) < 0) { - // Reverse the normal direction. - normal[0] = -normal[0]; - normal[1] = -normal[1]; - normal[2] = -normal[2]; - } - - if (xta * xta + yta * yta + zta * zta <= - 2.0 * (xta * (fcenter[0] - multiplier * normal[0]) + - yta * (fcenter[1] - multiplier * normal[1]) + - zta * (fcenter[2] - multiplier * normal[2]))) { - encroached = 1; - } - } else { - len = distance(cent, checkpt); - if ((fabs(len - rd) / rd) < b->epsilon) len = rd; // Rounding. - if (len < rd) { - encroached = 1; - } - } - - if (encroached) { - // 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. + 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 0; + return iter; } /////////////////////////////////////////////////////////////////////////////// // // -// checkfac4split() Check if a subface needs to be split. // -// // -// 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). // -// // -// Return 1 if it needs to be split, otherwise, return 0. // -// 'chkfac' represents its longest edge. // +// improvequalitysmoothing() Improve mesh quality by smoothing. // // // /////////////////////////////////////////////////////////////////////////////// -int tetgenmesh::checkfac4split(face *chkfac, point& encpt, int& qflag, - REAL *cent) -{ - point pa, pb, pc; - REAL area, rd, len; - REAL A[4][4], rhs[4], D; - int indx[4]; - int i; - - encpt = NULL; - qflag = 0; - - pa = sorg(*chkfac); - pb = sdest(*chkfac); - pc = sapex(*chkfac); - - // 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) - - area = 0.5 * sqrt(dot(A[2], A[2])); // The area of [a,b,c]. - - // Compute the right hand side vector b (3x1). - rhs[0] = 0.5 * dot(A[0], A[0]); // edge [a,b] - rhs[1] = 0.5 * dot(A[1], A[1]); // edge [a,c] - 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. - terminatetetgen(this, 2); - } - - lu_solve(A, 3, indx, rhs, 0); - cent[0] = pa[0] + rhs[0]; - cent[1] = pa[1] + rhs[1]; - cent[2] = pa[2] + rhs[2]; - rd = sqrt(rhs[0] * rhs[0] + rhs[1] * rhs[1] + rhs[2] * rhs[2]); - - if (checkconstraints && (areabound(*chkfac) > 0.0)) { - // Check if the subface has too big area. - if (area > areabound(*chkfac)) { - qflag = 1; - return 1; - } - } - - if (b->fixedvolume) { - if ((area * sqrt(area)) > b->maxvolume) { - qflag = 1; - return 1; - } - } - - if (b->varvolume) { - triface adjtet; - REAL volbnd; - int t1ver; +long tetgenmesh::improvequalitybysmoothing(optparameters* opm) { + arraypool *flipqueue, *swapqueue; + triface* parytet; + badface *bface, *parybface; + point* ppt; + long totalsmtcount, smtcount; + int smtflag; + int iter, i, j, k; - stpivot(*chkfac, adjtet); - if (!ishulltet(adjtet)) { - volbnd = volumebound(adjtet.tet); - if ((volbnd > 0) && (area * sqrt(area)) > volbnd) { - qflag = 1; - return 1; - } - } - fsymself(adjtet); - if (!ishulltet(adjtet)) { - volbnd = volumebound(adjtet.tet); - if ((volbnd > 0) && (area * sqrt(area)) > volbnd) { - qflag = 1; - return 1; - } - } - } + // assert(unflipqueue->objects > 0l); + flipqueue = new arraypool(sizeof(badface), 10); - 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; - } - } + // Swap the two flip queues. + swapqueue = flipqueue; + flipqueue = unflipqueue; + unflipqueue = swapqueue; - triface searchtet; - REAL smlen = 0; + totalsmtcount = 0l; + iter = 0; - // Check if this subface is locally encroached. - for (i = 0; i < 2; i++) { - stpivot(*chkfac, searchtet); - if (!ishulltet(searchtet)) { - int encroached = 0; + while (flipqueue->objects > 0l) { - len = distance(oppo(searchtet), cent); - if ((fabs(len - rd) / rd) < b->epsilon) len = rd;// Rounding. + smtcount = 0l; + + 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); + // 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 - if (b->use_equatorial_lens) { - point tettapex = oppo(searchtet); - REAL normal[3], fcenter[3]; - REAL xta, yta, zta; - REAL multiplier; - // Get the normal of the oriented face [a->b->c], without normalized. - point fa = org(searchtet); - point fb = dest(searchtet); - point fc = apex(searchtet); - - fcenter[0] = cent[0] - fc[0]; - fcenter[1] = cent[1] - fc[1]; - fcenter[2] = cent[2] - fc[2]; - - facenormal(fa, fb, fc, normal, 1, NULL); - multiplier = 0.985 * sqrt((fcenter[0]*fcenter[0] + fcenter[1]*fcenter[1] + - fcenter[2]*fcenter[2]) / - (3.0 * (normal[0] * normal[0] + normal[1] * normal[1] + - normal[2] * normal[2]))); - xta = tettapex[0] - fc[0]; - yta = tettapex[1] - fc[1]; - zta = tettapex[2] - fc[2]; - if (xta * xta + yta * yta + zta * zta <= - 2.0 * (xta * (fcenter[0] - multiplier * normal[0]) + - yta * (fcenter[1] - multiplier * normal[1]) + - zta * (fcenter[2] - multiplier * normal[2]))) { - encroached = 1; + flipqueue->restart(); + + // Unmark the tets in unflipqueue. + for (i = 0; i < unflipqueue->objects; i++) { + bface = (badface*)fastlookup(unflipqueue, i); + unmarktest(bface->tt); } - } else { - if (len < rd) { - encroached = 1; + + if (b->verbose > 1) { + printf(" Smooth %ld points.\n", smtcount); } - } + totalsmtcount += smtcount; - if (encroached) { - if (smlen == 0) { - smlen = len; - encpt = oppo(searchtet); + if (smtcount == 0l) { + // No point has been smoothed. + break; } else { - if (len < smlen) { - smlen = len; - encpt = oppo(searchtet); - } + iter++; + if (iter == 2) { // if (iter >= b->optpasses) { + break; + } } - //return 1; - } - } - sesymself(*chkfac); - } - return encpt != NULL; //return 0; + // Swap the two flip queues. + swapqueue = flipqueue; + flipqueue = unflipqueue; + unflipqueue = swapqueue; + } // while + + delete flipqueue; + + return totalsmtcount; } /////////////////////////////////////////////////////////////////////////////// // // -// splitsubface() Split a subface. // -// // -// 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'. // +// splitsliver() Split a sliver. // // // /////////////////////////////////////////////////////////////////////////////// -int tetgenmesh::splitsubface(face *splitfac, point encpt, point encpt1, - int qflag, REAL *ccent, int chkencflag) -{ +int tetgenmesh::splitsliver(triface* slitet, REAL cosd, int chkencflag) { + 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 (!qflag && smarktest3ed(*splitfac)) { - // Do not try to re-split a marked subface. - return 0; - } + // '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]. - 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; + // Do not split a segment. + if (issubseg(searchtet)) { + return 0; } - } // if (b->nobisect) - if (useinsertradius) { - if (encpt != NULL) { - REAL rp; // Insertion radius of newpt. - REAL rv = distance(encpt, ccent); - 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. - 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) { - return 0; // Reject the insertion of newpt. - } - } - } - } - } // if (useinsertradius) - - face searchsh; - insertvertexflags ivf; - point newpt; - int i; - - // Initialize the inserting point. - makepoint(&newpt, FREEFACETVERTEX); - // Split the subface at its circumcenter. - for (i = 0; i < 3; i++) newpt[i] = ccent[i]; - - // Search a subface which contains 'newpt'. - searchsh = *splitfac; - // Calculate an above point. It lies above the plane containing - // the subface [a,b,c], and save it in dummypoint. Moreover, - // the vector cent->dummypoint is the normal of the plane. - calculateabovepoint4(newpt, sorg(*splitfac), sdest(*splitfac), - sapex(*splitfac)); - // 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))) { - // Point location failed. - pointdealloc(newpt); - // Mark this subface to avoid splitting in the future. - smarktest3(*splitfac); - return 0; - } - - - triface searchtet; - - // Insert the point. - stpivot(searchsh, searchtet); - ivf.bowywat = 3; // Use Bowyer-Watson. Preserve subsegments and subfaces; - 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 = *splitfac; - ivf.smlenflag = useinsertradius; // Update the insertion radius. - - - if (insertpoint(newpt, &searchtet, &searchsh, NULL, &ivf)) { - st_facref_count++; - if (steinerleft > 0) steinerleft--; - if (useinsertradius) { - save_facetpoint_insradius(newpt, ivf.parentpt, ivf.smlen); - } // if (useinsertradius) - if (flipstack != NULL) { - flipconstraints fc; - fc.chkencflag = chkencflag; - fc.enqflag = 2; - lawsonflip3d(&fc); - unflipqueue->restart(); + // 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; } - return 1; - } else { - // Point was not inserted. - pointdealloc(newpt); - if (ivf.iloc == (int) ENCSEGMENT) { - // Select an encroached segment and split it. - face *paryseg; - int splitflag = 0; - for (i = 0; i < encseglist->objects; i++) { - paryseg = (face *) fastlookup(encseglist, i); - if (splitsegment(paryseg, NULL, 0.0, encpt, encpt1, qflag, - chkencflag | 1)) { - splitflag = 1; // A point is inserted on a segment. - break; - } - } // i - encseglist->restart(); - if (splitflag) { - // Some segments may need to be repaired. - if (badsubsegs->items > 0) { - repairencsegs(chkencflag | 1); - } - return 1; - } - } else { - if (ivf.iloc == (int) NEARVERTEX) { - terminatetetgen(this, 2); - } + if (ishulltet(spintet)) { + return 0; // It is a hull edge. } - // Mark this subface to avoid splitting in the future. - smarktest3(*splitfac); - return 0; - } -} -/////////////////////////////////////////////////////////////////////////////// -// // -// repairencfacs() Repair encroached subfaces. // -// // -/////////////////////////////////////////////////////////////////////////////// + // Get all tets at edge [a,b]. + abtets = new triface[n]; + spintet = searchtet; + for (i = 0; i < n; i++) { + abtets[i] = spintet; + fnextself(spintet); + } -void tetgenmesh::repairencfacs(int chkencflag) -{ - face *bface; - point encpt = NULL; - int qflag = 0; - REAL ccent[3]; - - // 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); - } - } + // 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; + } + + // 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]); + } + + // 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); } - bface->shver = -1; // Signal it as a deleted element. - badsubfacs->dealloc((void *) bface); // Remove this entry from list. - } - bface = (face *) badsubfacs->traverse(); + } // if (success) + + cavetetlist->restart(); + + if (!success) { + delete[] abtets; + return 0; } - } - if (badsubfacs->items > 0) { - if (steinerleft == 0) { - if (b->verbose) { - printf("The desired number of Steiner points is reached.\n"); - } + // 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 { - terminatetetgen(this, 2); - } - 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(); + // The Steiner point is too close to an existing vertex. Reject it. + pointdealloc(steinerpt); + return 0; } - badsubfacs->restart(); - } } /////////////////////////////////////////////////////////////////////////////// // // -// checktet4split() Check if the tet needs to be split. // +// removeslivers() Remove slivers by adding Steiner points. // // // /////////////////////////////////////////////////////////////////////////////// -int tetgenmesh::checktet4split(triface *chktet, int &qflag, REAL *ccent) -{ - 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->convex) { // -c - // Skip this tet if it lies in the exterior. - if (elemattribute(chktet->tet, numelemattrib - 1) == -1.0) { - return 0; - } - } - - qflag = 0; - - pd = (point) chktet->tet[7]; - if (pd == dummypoint) { - return 0; // Do not split a hull tet. - } - - 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]; - - // 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 (!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; - } - - // 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 (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; - } - } - - 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; - } - } - } - // 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; - } - } 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) - - // 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); - } - D = rd / smlen; - if (D > b->minratio) { - // A bad radius-edge ratio. - return 1; - } - } - - // 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); - } - 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])); - if (L[i] == 0) { - terminatetetgen(this, 2); - } - 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; - } - } +long tetgenmesh::removeslivers(int chkencflag) { + arraypool *flipqueue, *swapqueue; + badface *bface, *parybface; + triface slitet, *parytet; + point* ppt; + REAL cosdd[6], maxcosd; + long totalsptcount, sptcount; + int iter, i, j, k; + + // assert(unflipqueue->objects > 0l); + flipqueue = new arraypool(sizeof(badface), 10); + + // Swap the two flip queues. + swapqueue = flipqueue; + flipqueue = unflipqueue; + unflipqueue = swapqueue; + + totalsptcount = 0l; + iter = 0; + + while ((flipqueue->objects > 0l) && (steinerleft != 0)) { + + sptcount = 0l; + + 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 { + iter++; + if (iter == 2) { // if (iter >= b->optpasses) { + break; + } + } + + // Swap the two flip queues. + swapqueue = flipqueue; + flipqueue = unflipqueue; + unflipqueue = swapqueue; + } // while - return 0; + delete flipqueue; + + return totalsptcount; } /////////////////////////////////////////////////////////////////////////////// // // -// splittetrahedron() Split a tetrahedron. // +// optimizemesh() Optimize mesh for specified objective functions. // // // /////////////////////////////////////////////////////////////////////////////// -int tetgenmesh::splittetrahedron(triface* splittet, int qflag, REAL *ccent, - int chkencflag) -{ - triface searchtet; - face *paryseg; - point newpt, *ppt; - badface *bface; - insertvertexflags ivf; - int splitflag = 0; - int i; +void tetgenmesh::optimizemesh() { + 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) { + printf("Optimizing mesh...\n"); + } + optpasses = ((1 << b->optlevel) - 1); - REAL rv = 0.; // Insertion radius of 'newpt'. + 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); + } - makepoint(&newpt, FREEVOLVERTEX); - for (i = 0; i < 3; i++) newpt[i] = ccent[i]; + 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); - // Locate the new point. Starting from an interior point 'q' of the - // splittet. We perform a walk from q to the 'newpt', stop walking - // either we hit a subface or enter OUTSIDE. - searchtet = *splittet; - ivf.iloc = (int) OUTSIDE; - ivf.iloc = locate(newpt, &searchtet, 1); // 'chkencflag' = 1. + int attrnum = numelemattrib - 1; - if ((ivf.iloc == (int) OUTSIDE) || (ivf.iloc == (int) ENCSUBFACE)) { - // The circumcenter 'c' is not visible from 'q' (the interior of the tet). -// iffalse - if (b->verbose > 2) { - printf(" New point %d is blocked by a polygon.\n", pointmark(newpt)); + // Put all bad tetrahedra into array. + tetrahedrons->traversalinit(); + 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; + } + } + 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(); } -// \fi - pointdealloc(newpt); // Do not insert this vertex. - if (b->nobisect) return 0; // -Y option. - // There must be a polygon that blocks the visibility. - // Search a subpolygon that contains the proj(c). - face searchsh; - REAL prjpt[3]; - locateresult sloc = OUTSIDE; - tspivot(searchtet, searchsh); - ppt = (point *) &(searchsh.sh[3]); - projpt2face(ccent, ppt[0], ppt[1], ppt[2], prjpt); - // Locate proj(c) on polygon. - sloc = slocate(prjpt, &searchsh, 0, 0, 1); - if ((sloc == ONEDGE) || (sloc == ONFACE)) { - // Found a subface/edge containing proj(c). - // Check if 'c' encoraches upon this subface. - REAL fcent[3], r = 0; - ppt = (point *) &(searchsh.sh[3]); - if (checkfac4encroach(ppt[0], ppt[1], ppt[2], ccent, fcent, &r)) { - // Encroached. Split this subface. - splitflag = splitsubface(&searchsh, NULL, org(*splittet), qflag, - fcent, chkencflag | 2); - if (splitflag) { - // Some subfaces may need to be repaired. - repairencfacs(chkencflag | 2); - } - } - } - else if ((sloc == OUTSIDE) || (sloc == ENCSEGMENT)) { - // Hit a segment. We should split it. - // To be done... - // printf("hit segment, split it.\n"); // For debug only - } - if (splitflag) { - // Queue the tet if it is still alive. - if ((splittet->tet != NULL) && (splittet->tet[4] != NULL)) { - enqueuetetrahedron(splittet); - } - } - return splitflag; - } - - - - // 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 (useinsertradius) { - // Need to save insertion radius for this new point. - ivf.smlenflag = 1; // Return the shortest edge length after inserting - // the new vertex. [2016-09-19] - } - - - if (insertpoint(newpt, &searchtet, NULL, NULL, &ivf)) { - // Vertex is inserted. - st_volref_count++; - if (steinerleft > 0) steinerleft--; - if (useinsertradius) { - setpointinsradius(newpt, ivf.smlen); - setpoint2ppt(newpt, ivf.parentpt); + + 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; + badtetrahedrons = NULL; } - if (flipstack != NULL) { - flipconstraints fc; - fc.chkencflag = chkencflag; - fc.enqflag = 2; - lawsonflip3d(&fc); - unflipqueue->restart(); + + if (unflipqueue->objects > 0l) { + if (b->verbose > 1) { + printf(" %ld bad tets remained.\n", unflipqueue->objects); + } + unflipqueue->restart(); } - return 1; - } else { - // Point is not inserted. - pointdealloc(newpt); - // Check if there are encroached segments/subfaces. - if (ivf.iloc == (int) ENCSEGMENT) { - 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->verbose) { + if (totalremcount > 0l) { + printf(" Removed %ld edges.\n", totalremcount); } - } // if (!b->nobisect) - encseglist->restart(); - if (splitflag) { - // Some segments may need to be repaired. - if (badsubsegs->items > 0) { - repairencsegs(chkencflag | 3); - } - // Some subfaces may need to be repaired. - if (badsubfacs->items > 0) { - repairencfacs(chkencflag | 2); - } - } - } else if (ivf.iloc == (int) ENCSUBFACE) { - 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 (totalsmtcount > 0l) { + printf(" Smoothed %ld points.\n", totalsmtcount); } - } // if (!b->nobisect) - encshlist->restart(); - if (splitflag) { - // Some subfaces may need to be repaired. - if (badsubfacs->items > 0) { - repairencfacs(chkencflag | 2); + if (totalsptcount > 0l) { + printf(" Split %ld slivers.\n", totalsptcount); } - } - } else { - if (ivf.iloc == (int) NEARVERTEX) { - terminatetetgen(this, 2); - } - } - if (splitflag) { - // Queue the tet if it is still alive. - if ((splittet->tet != NULL) && (splittet->tet[4] != NULL)) { - enqueuetetrahedron(splittet); - } - } else { - //assert(0); // If no small angle, why can this happen? } - return splitflag; - } } +//// //// +//// //// +//// optimize_cxx ///////////////////////////////////////////////////////////// + +//// meshstat_cxx ///////////////////////////////////////////////////////////// +//// //// +//// //// + /////////////////////////////////////////////////////////////////////////////// // // -// repairbadtets() Repair bad quality tetrahedra. // +// printfcomma() Print a (large) number with the 'thousands separator'. // +// // +// The following code was simply copied from "stackoverflow". // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::repairbadtets(int chkencflag) -{ - triface *bface; - REAL ccent[3]; - int qflag = 0; - - - // 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(); +void tetgenmesh::printfcomma(unsigned long n) { + unsigned long n2 = 0; + int scale = 1; + while (n >= 1000) { + n2 = n2 + scale * (n % 1000); + n /= 1000; + scale *= 1000; } - } - - if (badtetrahedrons->items > 0) { - if (steinerleft == 0) { - if (b->verbose) { - printf("The desired number of Steiner points is reached.\n"); - } - } else { - terminatetetgen(this, 2); // 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(); + printf("%ld", n); + while (scale != 1) { + scale /= 1000; + n = n2 / scale; + n2 = n2 % scale; + printf(",%03ld", n); } - // Clear the pool. - badtetrahedrons->restart(); - } } /////////////////////////////////////////////////////////////////////////////// // // -// delaunayrefinement() Refine the mesh by Delaunay refinement. // +// 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. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::delaunayrefinement() -{ - triface checktet; - face checksh; - face checkseg; - long steinercount; - int chkencflag; - - long bak_segref_count, bak_facref_count, bak_volref_count; - long bak_flipcount = flip23count + flip32count + flip44count; - - if (!b->quiet) { - printf("Refining mesh...\n"); - } - - if (b->verbose) { - printf(" Min radius-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. - } - } +int tetgenmesh::checkmesh(int topoflag) { + triface tetloop, neightet, symtet; + point pa, pb, pc, pd; + REAL ori; + int horrors, i; - if (useinsertradius) { - if ((b->plc && b->nobisect) || b->refine) { // '-pY' or '-r' option. - makesegmentendpointsmap(); - makefacetverticesmap(); + if (!b->quiet) { + printf(" Checking consistency of mesh...\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"); + horrors = 0; + tetloop.ver = 0; + // Run through the list of tetrahedra, checking each one. + tetrahedrons->traversalinit(); + tetloop.tet = alltetrahedrontraverse(); + while (tetloop.tet != (tetrahedron*)NULL) { + // 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); + if (neightet.tet != NULL) { + // 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++; + } + } else { + printf(" !! !! Tet-face has no neighbor (%d, %d, %d) - %d:\n", pointmark(pa), + pointmark(pb), pointmark(pc), pointmark(pd)); + 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)); + } + } + // 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))); + } + } + tetloop.tet = alltetrahedrontraverse(); } - - chkencflag = 1; // Only check encroaching subsegments. - steinercount = points->items; - - // Initialize the pool of encroached subsegments. - badsubsegs = new memorypool(sizeof(face), b->shellfaceperblock, - sizeof(void *), 0); - - // Add all segments into the pool. - subsegs->traversalinit(); - checkseg.sh = shellfacetraverse(subsegs); - while (checkseg.sh != (shellface *) NULL) { - enqueuesubface(badsubsegs, &checkseg); - checkseg.sh = shellfacetraverse(subsegs); + 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"); } - // Split all encroached segments. - repairencsegs(chkencflag); - - if (b->verbose) { - printf(" Added %ld Steiner points.\n", points->items - steinercount); - } - - if (b->reflevel > 1) { // '-D2' option - if (b->verbose) { - printf(" Splitting encroached subfaces.\n"); - } - - 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"); - } + return horrors; +} - 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; +/////////////////////////////////////////////////////////////////////////////// +// // +// checkshells() Test the boundary mesh for topological consistency. // +// // +/////////////////////////////////////////////////////////////////////////////// - // The cosine value of the min dihedral angle (-qq) for tetrahedra. - cosmindihed = cos(b->mindihedral / 180.0 * PI); +int tetgenmesh::checkshells() { + triface neightet, symtet; + face shloop, spinsh, nextsh; + face checkseg; + point pa, pb; + int bakcount; + int horrors, i; - // 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(); + if (!b->quiet) { + printf(" Checking consistency of the mesh boundary...\n"); } + horrors = 0; - // Split all bad quality tetrahedra. - repairbadtets(chkencflag); + void** bakpathblock = subfaces->pathblock; + void* bakpathitem = subfaces->pathitem; + int bakpathitemsleft = subfaces->pathitemsleft; + int bakalignbytes = subfaces->alignbytes; - 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); + 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 (b->reflevel > 2) - if (b->verbose) { - if (flip23count + flip32count + flip44count > bak_flipcount) { - printf(" Performed %ld flips.\n", flip23count + flip32count + - flip44count - bak_flipcount); + if (horrors == 0) { + if (!b->quiet) { + printf(" Mesh boundaries connected correctly.\n"); + } + } else { + printf(" !! !! !! !! %d boundary connection viewed with horror.\n", horrors); } - } - - 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; - encseglist = NULL; - encshlist = NULL; - - if (!b->nobisect || checkconstraints) { - totalworkmemory += (badsubsegs->maxitems * badsubsegs->itembytes); - delete badsubsegs; - badsubsegs = NULL; - if (b->reflevel > 1) { - totalworkmemory += (badsubfacs->maxitems * badsubfacs->itembytes); - delete badsubfacs; - badsubfacs = NULL; - } - } - if (b->reflevel > 2) { - totalworkmemory += (badtetrahedrons->maxitems*badtetrahedrons->itembytes); - delete badtetrahedrons; - badtetrahedrons = NULL; - } -} -//// //// -//// //// -//// refine_cxx /////////////////////////////////////////////////////////////// + subfaces->pathblock = bakpathblock; + subfaces->pathitem = bakpathitem; + subfaces->pathitemsleft = bakpathitemsleft; + subfaces->alignbytes = bakalignbytes; -//// optimize_cxx ///////////////////////////////////////////////////////////// -//// //// -//// //// + return horrors; +} /////////////////////////////////////////////////////////////////////////////// // // -// lawsonflip3d() A three-dimensional Lawson's algorithm. // +// checksegments() Check the connections between tetrahedra and segments. // // // /////////////////////////////////////////////////////////////////////////////// -long tetgenmesh::lawsonflip3d(flipconstraints *fc) -{ - triface fliptets[5], neightet, hulltet; - face checksh, casingout; - badface *popface, *bface; - point pd, pe, *pts; - REAL sign, ori; - REAL vol, len3; - long flipcount, totalcount = 0l; - long sliver_peels = 0l; - int t1ver; - int i; - - - while (1) { +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->verbose > 2) { - printf(" Lawson flip %ld faces.\n", flippool->items); + if (!b->quiet) { + printf(" Checking tet->seg connections...\n"); } - flipcount = 0l; - while (flipstack != (badface *) NULL) { - // Pop a face from the stack. - popface = flipstack; - fliptets[0] = popface->tt; - flipstack = flipstack->nextitem; // The next top item in stack. - flippool->dealloc((void *) popface); + horrors = 0; + tetrahedrons->traversalinit(); + tetloop.tet = tetrahedrontraverse(); + 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 { + // 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); + } + // Check the seg->tet pointer. + sstpivot1(sseg, neightet); + if (neightet.tet == NULL) { + printf(" !! Wrong seg->tet connection (A NULL tet).\n"); + horrors++; + } else { + 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++; + } + } + } + } + } + // 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; + } + } + } + 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; + if (!b->quiet) { + printf(" Checking seg->tet connections...\n"); + } - unmarkface(fliptets[0]); + 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 (ishulltet(fliptets[0])) continue; + if (!b->quiet) { + printf(" Checking seg->seg connections...\n"); + } - 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); - } + 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 { + // 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) && (sdest(checkseg) != pa)) { + printf(" !! Wrong seg-seg connection at point %d.\n", + pointmark(pa)); + horrors++; + } + } + } } 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. + // 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 { + spivotself(checkseg); + checkseg.shver = 0; + if ((sorg(checkseg) != pa) && (sdest(checkseg) != pa)) { + printf(" !! Wrong seg-seg connection at point %d.\n", pointmark(pa)); + horrors++; + } } - } } - break; - } // if (neightet.tet == fliptets[0].tet) - } // if (!isshsubseg(checksh)) - senextself(checksh); - } // i - } // if (nonconvex) - continue; - } - - 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]); - - // Use the length of the edge [d,e] as a reference to determine - // a nearly degenerated new tet. - len3 = distance(pd, pe); - len3 = (len3 * len3 * len3); - int round_flag = 0; // [2017-10-20] - // 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) { - // Avoid creating a nearly degenerated new tet at boundary. - // Re-use fliptets[2], fliptets[3]; - esym(fliptets[0], fliptets[2]); - esym(fliptets[1], fliptets[3]); - if (issubface(fliptets[2]) || issubface(fliptets[3])) { - vol = orient3dfast(org(fliptets[0]), dest(fliptets[0]), pd, pe); - if ((fabs(vol) / len3) < b->epsilon) { - ori = 0.0; // Do rounding. - round_flag = 1; // [2017-10-20] - } - } - } // Rounding check - if (ori <= 0) break; - enextself(fliptets[0]); - eprevself(fliptets[1]); - } - - 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 (round_flag == 1) { - continue; // [2017-10-20] - } - 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. - } - /////// Debug - //if (checkmesh(0) > 0) { - // assert(0); - //} - continue; - } // if (ori == 0) } - } - } // if (ori <= 0) - - // This non-Delaunay face is unflippable. Save it. - unflipqueue->newindex((void **) &bface); - bface->tt = fliptets[0]; - bface->forg = org(fliptets[0]); - bface->fdest = dest(fliptets[0]); - bface->fapex = apex(fliptets[0]); - } // if (sign < 0) - } // while (flipstack) - - if (b->verbose > 2) { - if (flipcount > 0) { - printf(" Performed %ld flips.\n", flipcount); - } - } - // Accumulate the counter of flips. - totalcount += flipcount; - - // Return if no unflippable faces left. - if (unflipqueue->objects == 0l) break; - // Return if no flip has been performed. - if (flipcount == 0l) break; - - // Try to flip the unflippable faces. - for (i = 0; i < unflipqueue->objects; i++) { - bface = (badface *) fastlookup(unflipqueue, i); - if (!isdeadtet(bface->tt) && - (org(bface->tt) == bface->forg) && - (dest(bface->tt) == bface->fdest) && - (apex(bface->tt) == bface->fapex)) { - flippush(flipstack, &(bface->tt)); - } + } + pa = pointtraverse(); } - 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 (horrors == 0) { + printf(" Segments are connected properly.\n"); + } else { + printf(" !! !! !! !! Found %d missing connections.\n", horrors); } - if (unflipqueue->objects > 0l) { - printf(" %ld unflippable edges remained.\n", unflipqueue->objects); + if (miscount > 0) { + printf(" !! !! Found %d missing segments.\n", miscount); } - } - return totalcount + sliver_peels; + return horrors; } /////////////////////////////////////////////////////////////////////////////// // // -// recoverdelaunay() Recovery the locally Delaunay property. // +// checkdelaunay() Ensure that the mesh is (constrained) Delaunay. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::recoverdelaunay() -{ - arraypool *flipqueue, *nextflipqueue, *swapqueue; - triface tetloop, neightet, *parytet; - badface *bface, *parybface; - point *ppt; - flipconstraints fc; - int i, j; - - if (!b->quiet) { - printf("Recovering Delaunayness...\n"); - } - - tetprism_vol_sum = 0.0; // Initialize it. - - // 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); - } - } - ppt = (point *) &(tetloop.tet[4]); - tetprism_vol_sum += tetprismvol(ppt[0], ppt[1], ppt[2], ppt[3]); - tetloop.tet = tetrahedrontraverse(); - } - - // Calulate a relatively lower bound for small improvement. - // Used to avoid rounding error in volume calculation. - fc.bak_tetprism_vol = tetprism_vol_sum * b->epsilon * 1e-3; - - if (b->verbose) { - printf(" Initial obj = %.17g\n", tetprism_vol_sum); - } - - if (b->verbose > 1) { - printf(" Recover Delaunay [Lawson] : %ld\n", flippool->items); - } - - // 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 { - // Unable to remove this edge. Save it. - nextflipqueue->newindex((void **) &parybface); - *parybface = *bface; - // Normally, it should be zero. - //assert(fc.tetprism_vol_sum == 0.0); - // However, due to rounding errors, a tiny value may appear. - fc.tetprism_vol_sum = 0.0; - } - } - } // i +int tetgenmesh::checkdelaunay(int perturb) { + 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->verbose > 1) { - printf(" obj (after level %d) = %.17g.\n", autofliplinklevel, - tetprism_vol_sum); + if (!b->quiet) { + printf(" Checking Delaunay property of the mesh...\n"); } - 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; + 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); + if (perturb) { + sign = insphere_s(pa, pb, pc, pd, pe); + } else { + sign = insphere(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++; + } + } + } + } + tetloop.tet = tetrahedrontraverse(); } - } // while (flipqueue->objects > 0l) - if (flipqueue->objects > 0l) { - if (b->verbose > 1) { - printf(" %ld non-Delaunay edges remained.\n", flipqueue->objects); + 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); } - } - if (b->verbose) { - printf(" Final obj = %.17g\n", tetprism_vol_sum); - } - - b->flipstarsize = bakmaxflipstarsize; - delete flipqueue; - delete nextflipqueue; + return horrors; } /////////////////////////////////////////////////////////////////////////////// // // -// gettetrahedron() Get a tetrahedron which have the given vertices. // +// Check if the current tetrahedralization is (constrained) regular. // +// // +// 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. // // // /////////////////////////////////////////////////////////////////////////////// -int tetgenmesh::gettetrahedron(point pa, point pb, point pc, point pd, - triface *searchtet) -{ - triface spintet; - int t1ver; +int tetgenmesh::checkregular(int type) { + triface tetloop; + triface symtet; + face checksh; + point p[5]; + REAL sign; + int ndcount; // Count the non-locally Delaunay faces. + int horrors; - 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 (apex(*searchtet) == pc) { - if (oppo(*searchtet) == pd) { - return 1; - } else { - fsymself(*searchtet); - if (oppo(*searchtet) == pd) { - return 1; - } - } + if (!b->quiet) { + printf(" Checking %s %s property of the mesh...\n", + (type & 2) == 0 ? "Delaunay" : "regular", (type & 1) == 0 ? " " : "(s)"); } - } - return 0; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// improvequalitybyflips() Improve the mesh quality by flips. // -// // -/////////////////////////////////////////////////////////////////////////////// + // 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]. -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; + 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]); + } - 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) + if (sign > 0.0) { + ndcount++; + if (checksubfaceflag) { + tspivot(tetloop, checksh); } - } // j - } // if (fc.cosdihed_out < cosmaxdihed) - cavetetlist->restart(); - remcount++; - } - } - } // i - if (!remflag) { - // An unremoved bad tet. Queue it again. - unflipqueue->newindex((void **) &parybface); - *parybface = *bface; - } - } // 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); + 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++; + } + } + } + } + tetloop.tet = tetrahedrontraverse(); } - 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)); + 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"); } - // 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; + return horrors; } /////////////////////////////////////////////////////////////////////////////// // // -// 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. // -// // -// '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. // +// checkconforming() Ensure that the mesh is conforming Delaunay. // // // -// ASSUMPTION: This routine assumes that all link faces are true faces, i.e, // -// no face has 'dummypoint' as its vertex. // +// If 'flag' is 1, only check subsegments. If 'flag' is 2, check subfaces. // +// If 'flag' is 3, check both subsegments and subfaces. // // // /////////////////////////////////////////////////////////////////////////////// -int tetgenmesh::smoothpoint(point smtpt, arraypool *linkfacelist, int ccw, - optparameters *opm) -{ - 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; - - // Decide the number of moving directions. - numdirs = (int) linkfacelist->objects; - if (numdirs > opm->numofsearchdirs) { - numdirs = opm->numofsearchdirs; // Maximum search directions. - } - - // Set the initial value. - opm->imprval = opm->initval; - iter = 0; - - for (i = 0; i < 3; i++) { - bestpt[i] = startpt[i] = smtpt[i]; - } - - // Iterate until the obj function is not improved. - while (1) { - - // Find the best next location. - oldval = opm->imprval; - - 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 { - pb = org(*parytet); - pa = dest(*parytet); - } - 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->min_max_aspectratio) { - val = 1.0 / 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 +int tetgenmesh::checkconforming(int flag) { + 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; + + REAL A[4][4], rhs[4], D; + int indx[4]; + REAL elen[3]; + + encsubsegs = 0; + + if (flag & 1) { + if (!b->quiet) { + printf(" Checking conforming property of segments...\n"); + } + encsubsegs = 0; + + // 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); + } + + if (encsubsegs == 0) { + if (!b->quiet) { + printf(" The segments are conforming Delaunay.\n"); + } } else { - // Remember the smallest improved value. - if (j == 0) { - minval = val; - } else { - minval = (val < minval) ? val : minval; - } + printf(" !! !! %d subsegments are non-conforming.\n", encsubsegs); } - } // 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 + } // if (flag & 1) - 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->min_max_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. - terminatetetgen(this, 2); - } - } - - 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; - } + encsubfaces = 0; + + if (flag & 2) { + if (!b->quiet) { + printf(" Checking conforming property of subfaces...\n"); + } - } // while (1) + // 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); + } - 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]; - } + 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) - return iter; + return encsubsegs + encsubfaces; } - /////////////////////////////////////////////////////////////////////////////// // // -// improvequalitysmoothing() Improve mesh quality by smoothing. // +// qualitystatistics() Print statistics about the quality of the mesh. // // // /////////////////////////////////////////////////////////////////////////////// -long tetgenmesh::improvequalitybysmoothing(optparameters *opm) -{ - arraypool *flipqueue, *swapqueue; - triface *parytet; - badface *bface, *parybface; - point *ppt; - long totalsmtcount, smtcount; - int smtflag; - int iter, i, j, k; - - //assert(unflipqueue->objects > 0l); - flipqueue = new arraypool(sizeof(badface), 10); - - // Swap the two flip queues. - swapqueue = flipqueue; - flipqueue = unflipqueue; - unflipqueue = swapqueue; +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 smallestradiusratio, biggestradiusratio; // radius-edge ratio. + 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; + // Report the tet which has the biggest radius-edge ratio. + triface biggestradiusratiotet; + + printf("Mesh quality statistics:\n\n"); + + shortlen = longlen = 0.0; + smalldiangle = bigdiangle = 0.0; + total_tet_vol = 0.0; + total_tetprism_vol = 0.0; + + 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; + + 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; + + minaltitude = xmax - xmin + ymax - ymin + zmax - zmin; + minaltitude = minaltitude * minaltitude; + shortest = minaltitude; + longest = 0.0; + smallestvolume = minaltitude; + biggestvolume = 0.0; + smallestratio = smallestradiusratio = 1e+16; // minaltitude; + biggestratio = biggestradiusratio = 0.0; + smallestdiangle = smallestfaangle = 180.0; + biggestdiangle = biggestfaangle = 0.0; - totalsmtcount = 0l; - iter = 0; + int attrnum = numelemattrib - 1; - while (flipqueue->objects > 0l) { + // Loop all elements, calculate quality parameters for each element. + tetrahedrons->traversalinit(); + tetloop.tet = tetrahedrontraverse(); + while (tetloop.tet != (tetrahedron*)NULL) { - smtcount = 0l; + if (b->convex) { + // Skip tets in the exterior. + if (elemattribute(tetloop.tet, attrnum) == -1.0) { + tetloop.tet = tetrahedrontraverse(); + continue; + } + } - 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); - // 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 - - flipqueue->restart(); - - // Unmark the tets in unflipqueue. - for (i = 0; i < unflipqueue->objects; i++) { - bface = (badface *) fastlookup(unflipqueue, i); - unmarktest(bface->tt); - } + // Get four vertices: p0, p1, p2, p3. + for (i = 0; i < 4; i++) + p[i] = (point)tetloop.tet[4 + i]; - if (b->verbose > 1) { - printf(" Smooth %ld points.\n", smtcount); - } - totalsmtcount += smtcount; + // 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]); - if (smtcount == 0l) { - // No point has been smoothed. - break; - } else { - iter++; - if (iter == 2) { //if (iter >= b->optpasses) { - break; - } - } + // Calculate the largest and smallest volume. + if (tetvol < smallestvolume) { + smallestvolume = tetvol; + } + if (tetvol > biggestvolume) { + biggestvolume = tetvol; + } - // Swap the two flip queues. - swapqueue = flipqueue; - flipqueue = unflipqueue; - unflipqueue = swapqueue; - } // while + // 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. - delete flipqueue; + // Get the squares of the edge lengths. + for (i = 0; i < 6; i++) + edgelength[i] = dot(V[i], V[i]); - return totalsmtcount; -} + // 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]; + } + } -/////////////////////////////////////////////////////////////////////////////// -// // -// splitsliver() Split a sliver. // -// // -/////////////////////////////////////////////////////////////////////////////// + // 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]; + } -int tetgenmesh::splitsliver(triface *slitet, REAL cosd, int chkencflag) -{ - 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; - - // '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; - } - - // 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. - } - - // Get all tets at edge [a,b]. - abtets = new triface[n]; - spintet = searchtet; - for (i = 0; i < n; i++) { - abtets[i] = spintet; - fnextself(spintet); - } - - // 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; - } - - // 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]); - } - - // 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); - } - } // if (success) - - cavetetlist->restart(); - - if (!success) { - delete [] abtets; - return 0; - } + // 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 < 4; i++) { + if (H[i] > minheightinv) minheightinv = H[i]; + } + } else { + // A nearly degenerated tet. + if (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 < 4; 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); + } + // 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; - // Insert the Steiner point. - makepoint(&steinerpt, FREEVOLVERTEX); - for (i = 0; i < 3; i++) steinerpt[i] = smtpt[i]; + // 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]++; + } - // Insert the created Steiner point. - for (i = 0; i < n; i++) { - infect(abtets[i]); - caveoldtetlist->newindex((void **) &parytet); - *parytet = abtets[i]; - } + // 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]++; + } + } + } - searchtet = abtets[0]; // No need point location. - if (b->metric) { - locate(steinerpt, &searchtet); // For size interpolation. - } + // Calculate aspect ratio and radius-edge ratio for this element. + tetradius = cirradius / sqrt(shortlen); + if (tetradius < smallestradiusratio) { + smallestradiusratio = tetradius; + } + if (tetradius > biggestradiusratio) { + biggestradiusratio = tetradius; + biggestradiusratiotet.tet = tetloop.tet; + } + // 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]++; - delete [] abtets; + tetloop.tet = tetrahedrontraverse(); + } - ivf.iloc = (int) INSTAR; - ivf.chkencflag = chkencflag; - ivf.assignmeshsize = b->metric; + shortest = sqrt(shortest); + longest = sqrt(longest); + minaltitude = sqrt(minaltitude); + + 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); + + printf(" Aspect ratio histogram:\n"); + printf(" < %-6.6g : %8ld | %6.6g - %-6.6g : %8ld\n", aspectratiotable[0], + aspecttable[0], aspectratiotable[5], aspectratiotable[6], aspecttable[6]); + for (i = 1; i < 5; i++) { + printf(" %6.6g - %-6.6g : %8ld | %6.6g - %-6.6g : %8ld\n", + aspectratiotable[i - 1], aspectratiotable[i], aspecttable[i], + aspectratiotable[i + 5], aspectratiotable[i + 6], aspecttable[i + 6]); + } + printf(" %6.6g - %-6.6g : %8ld | %6.6g - : %8ld\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: %8ld | %3d - %3d degrees: %8ld\n", i * 10, + i * 10 + 10, faceangletable[i], i * 10 + 90, i * 10 + 100, faceangletable[i + 9]); + } + if (minfaceang != PI) { + printf(" Minimum input face angle is %g (degree).\n", minfaceang / PI * 180.0); + } + printf("\n"); + printf(" Dihedral angle histogram:\n"); + // Print the three two rows: + printf(" %3d - %2d degrees: %8ld | %3d - %3d degrees: %8ld\n", 0, 5, + dihedangletable[0], 80, 110, dihedangletable[9]); + printf(" %3d - %2d degrees: %8ld | %3d - %3d degrees: %8ld\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: %8ld | %3d - %3d degrees: %8ld\n", (i - 1) * 10, + (i - 1) * 10 + 10, dihedangletable[i], (i - 1) * 10 + 110, (i - 1) * 10 + 120, + dihedangletable[i + 9]); + } + // Print the last two rows. + printf(" %3d - %2d degrees: %8ld | %3d - %3d degrees: %8ld\n", 60, 70, + dihedangletable[7], 170, 175, dihedangletable[16]); + printf(" %3d - %2d degrees: %8ld | %3d - %3d degrees: %8ld\n", 70, 80, + dihedangletable[8], 175, 180, dihedangletable[17]); + if (minfacetdihed != PI) { + printf(" Minimum input dihedral angle is %g (degree).\n", minfacetdihed / PI * 180.0); + } + printf("\n"); - 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; - } + printf("\n"); } /////////////////////////////////////////////////////////////////////////////// // // -// removeslivers() Remove slivers by adding Steiner points. // +// memorystatistics() Report the memory usage. // // // /////////////////////////////////////////////////////////////////////////////// -long tetgenmesh::removeslivers(int chkencflag) -{ - arraypool *flipqueue, *swapqueue; - badface *bface, *parybface; - triface slitet, *parytet; - point *ppt; - REAL cosdd[6], maxcosd; - long totalsptcount, sptcount; - int iter, i, j, k; - - //assert(unflipqueue->objects > 0l); - flipqueue = new arraypool(sizeof(badface), 10); - - // Swap the two flip queues. - swapqueue = flipqueue; - flipqueue = unflipqueue; - unflipqueue = swapqueue; - - totalsptcount = 0l; - iter = 0; +void tetgenmesh::memorystatistics() { + printf("Memory usage statistics:\n\n"); - while ((flipqueue->objects > 0l) && (steinerleft != 0)) { + // Count the number of blocks of tetrahedra. + int tetblocks = 0; + tetrahedrons->pathblock = tetrahedrons->firstblock; + while (tetrahedrons->pathblock != NULL) { + tetblocks++; + tetrahedrons->pathblock = (void**)*(tetrahedrons->pathblock); + } - sptcount = 0l; + // 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); + } - 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 + 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); + } - flipqueue->restart(); + 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); - if (b->verbose > 1) { - printf(" Split %ld tets.\n", sptcount); - } - totalsptcount += sptcount; + printf(" Approximate memory for extra pointers (bytes): %ld\n", + totalt2shmemory); + } else { + printf(" Approximate memory for tetrahedralization (bytes): %ld\n", + totalmeshmemory); + } + printf(" Approximate memory for algorithms (bytes): %ld\n", + totalalgomemory); + printf(" Approximate memory for working arrays (bytes): %ld\n", + totalworkmemory); + printf(" Approximate total used memory (bytes): %ld\n", + totalmeshmemory + totalt2shmemory + totalalgomemory + + totalworkmemory); + */ + if (b->plc || b->refine) { + printf(" Approximate memory for tetrahedral mesh (bytes): "); + printfcomma(totalmeshmemory); + printf("\n"); - if (sptcount == 0l) { - // No point has been smoothed. - break; + printf(" Approximate memory for extra pointers (bytes): "); + printfcomma(totalt2shmemory); + printf("\n"); } else { - iter++; - if (iter == 2) { //if (iter >= b->optpasses) { - break; - } + 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"); - // Swap the two flip queues. - swapqueue = flipqueue; - flipqueue = unflipqueue; - unflipqueue = swapqueue; - } // while - - delete flipqueue; - - return totalsptcount; + printf("\n"); } /////////////////////////////////////////////////////////////////////////////// // // -// optimizemesh() Optimize mesh for specified objective functions. // +// statistics() Print all sorts of cool facts. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::optimizemesh() -{ - 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) { - printf("Optimizing mesh...\n"); - } - - optpasses = ((1 << b->optlevel) - 1); - - if (b->verbose) { - printf(" Optimization level = %d.\n", b->optlevel); - printf(" Optimization scheme = %d.\n", b->optscheme); - printf(" Number of iteration = %d.\n", optpasses); - printf(" Min_Max dihed angle = %g.\n", b->optmaxdihedral); - } - - totalsmtcount = totalsptcount = totalremcount = 0l; - - cosmaxdihed = cos(b->optmaxdihedral / 180.0 * PI); - cossmtdihed = cos(b->optminsmtdihed / 180.0 * PI); - cosslidihed = cos(b->optminslidihed / 180.0 * PI); - - int attrnum = numelemattrib - 1; - - // Put all bad tetrahedra into array. - tetrahedrons->traversalinit(); - 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; - } - } - 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; +void tetgenmesh::statistics() { + long tetnumber, facenumber; - 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; - } + printf("\nStatistics:\n\n"); + printf(" Input points: %d\n", in->numberofpoints); + if (b->refine) { + printf(" Input tetrahedra: %d\n", in->numberoftetrahedra); + if (in->numberoftrifaces > 0) { + printf(" Input triangles: %d\n", in->numberoftrifaces); } - } - if (unflipqueue->objects > 0l) { - if (remcount > 0l) { - iter++; - } else { - break; + if (in->numberofedges > 0) { + printf(" Input edges: %d\n", in->numberofedges); } - } else { - break; - } - } // while (iter) + } else if (b->plc) { + printf(" Input facets: %d\n", in->numberoffacets); + printf(" Input segments: %ld\n", insegments); + if (in->numberofedges > 0) { + printf(" Input edges: %d\n", in->numberofedges); + } + printf(" Input holes: %d\n", in->numberofholes); + printf(" Input regions: %d\n", in->numberofregions); + } - delete badtetrahedrons; - badtetrahedrons = NULL; - } + tetnumber = tetrahedrons->items - hullsize; + facenumber = (tetnumber * 4l + hullsize) / 2l; - if (unflipqueue->objects > 0l) { - if (b->verbose > 1) { - printf(" %ld bad tets remained.\n", unflipqueue->objects); + 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); + } } - unflipqueue->restart(); - } - if (b->verbose) { - if (totalremcount > 0l) { - printf(" Removed %ld edges.\n", totalremcount); + if (b->plc || b->refine) { + printf(" Mesh faces on exterior boundary: %ld\n", hullsize); + if (meshhulledges > 0l) { + printf(" Mesh edges on exterior boundary: %ld\n", meshhulledges); + } + printf(" Mesh faces on input facets: %ld\n", subfaces->items); + printf(" Mesh edges on input segments: %ld\n", subsegs->items); + if (st_facref_count > 0l) { + printf(" Steiner points on input facets: %ld\n", st_facref_count); + } + if (st_segref_count > 0l) { + printf(" Steiner points on input segments: %ld\n", st_segref_count); + } + if (st_volref_count > 0l) { + printf(" Steiner points inside domain: %ld\n", st_volref_count); + } + } else { + printf(" Convex hull faces: %ld\n", hullsize); + if (meshhulledges > 0l) { + printf(" Convex hull edges: %ld\n", meshhulledges); + } } - if (totalsmtcount > 0l) { - printf(" Smoothed %ld points.\n", totalsmtcount); + if (b->weighted) { // -w option + printf(" Skipped non-regular points: %ld\n", nonregularcount); } - if (totalsptcount > 0l) { - printf(" Split %ld slivers.\n", totalsptcount); + printf("\n"); + + if (b->verbose > 0) { + if (b->plc || b->refine) { // -p or -r + if (tetrahedrons->items > 0l) { + qualitystatistics(); + } + } + if (tetrahedrons->items > 0l) { + memorystatistics(); + } } - } } //// //// //// //// -//// optimize_cxx ///////////////////////////////////////////////////////////// - //// meshstat_cxx ///////////////////////////////////////////////////////////// + +//// output_cxx /////////////////////////////////////////////////////////////// //// //// //// //// /////////////////////////////////////////////////////////////////////////////// // // -// printfcomma() Print a (large) number with the 'thousands separator'. // +// jettisonnodes() Jettison unused or duplicated vertices. // // // -// The following code was simply copied from "stackoverflow". // +// 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. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::printfcomma(unsigned long n) -{ - unsigned long n2 = 0; - int scale = 1; - while (n >= 1000) { - n2 = n2 + scale * (n % 1000); - n /= 1000; - scale *= 1000; - } - printf ("%ld", n); - while (scale != 1) { - scale /= 1000; - n = n2 / scale; - n2 = n2 % scale; - printf (",%03ld", n); - } -} +void tetgenmesh::jettisonnodes() { + point pointloop; + bool jetflag; + int oldidx, newidx; + int remcount; -/////////////////////////////////////////////////////////////////////////////// -// // -// 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. // -// // -/////////////////////////////////////////////////////////////////////////////// + if (!b->quiet) { + printf("Jettisoning redundant points.\n"); + } -int tetgenmesh::checkmesh(int topoflag) -{ - triface tetloop, neightet, symtet; - point pa, pb, pc, pd; - REAL ori; - int horrors, i; - - if (!b->quiet) { - printf(" Checking consistency of mesh...\n"); - } - - horrors = 0; - tetloop.ver = 0; - // Run through the list of tetrahedra, checking each one. - tetrahedrons->traversalinit(); - tetloop.tet = alltetrahedrontraverse(); - while (tetloop.tet != (tetrahedron *) NULL) { - // 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++; + 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++; } - 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); - if (neightet.tet != NULL) { - // 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++; - } - } else { - printf(" !! !! Tet-face has no neighbor (%d, %d, %d) - %d:\n", - pointmark(pa), pointmark(pb), pointmark(pc), pointmark(pd)); - 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)); - } - } - // 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))); - } + oldidx++; + pointloop = pointtraverse(); } - tetloop.tet = alltetrahedrontraverse(); - } - if (horrors == 0) { - if (!b->quiet) { - printf(" In my studied opinion, the mesh appears to be consistent.\n"); + if (b->verbose) { + printf(" %ld duplicated vertices are removed.\n", dupverts); + printf(" %ld unused vertices are removed.\n", unuverts); } - } else { - printf(" !! !! !! !! %d %s witnessed.\n", horrors, - horrors > 1 ? "abnormity" : "abnormities"); - } + 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; } /////////////////////////////////////////////////////////////////////////////// // // -// checkshells() Test the boundary mesh for topological consistency. // +// 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. // // // /////////////////////////////////////////////////////////////////////////////// -int tetgenmesh::checkshells() -{ - triface neightet, symtet; - face shloop, spinsh, nextsh; - face checkseg; - point pa, pb; - int bakcount; - int horrors, i; - - if (!b->quiet) { - 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); - } +void tetgenmesh::highorder() { + triface tetloop, worktet, spintet; + point *extralist, *adjextralist; + point torg, tdest, newpoint; + int highorderindex; + int t1ver; + int i, j; - if (horrors == 0) { if (!b->quiet) { - printf(" Mesh boundaries connected correctly.\n"); + printf("Adding vertices for second-order tetrahedra.\n"); } - } else { - printf(" !! !! !! !! %d boundary connection viewed with horror.\n", - horrors); - } - subfaces->pathblock = bakpathblock; - subfaces->pathitem = bakpathitem; - subfaces->pathitemsleft = bakpathitemsleft; - subfaces->alignbytes = bakalignbytes; + // Initialize the 'highordertable'. + highordertable = new point[tetrahedrons->items * 6]; + if (highordertable == (point*)NULL) { + terminatetetgen(this, 1); + } - return horrors; -} + // This will overwrite the slot for element markers. + highorderindex = 11; -/////////////////////////////////////////////////////////////////////////////// -// // -// checksegments() Check the connections between tetrahedra and segments. // -// // -/////////////////////////////////////////////////////////////////////////////// + // 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; -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"); - } - - horrors = 0; - tetrahedrons->traversalinit(); - tetloop.tet = tetrahedrontraverse(); - 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 { - // 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); - } - // Check the seg->tet pointer. - sstpivot1(sseg, neightet); - if (neightet.tet == NULL) { - printf(" !! Wrong seg->tet connection (A NULL tet).\n"); - horrors++; - } else { - 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++; - } - } - } - } - } - // 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; + // Assign an entry for each tetrahedron to find its extra nodes. At the + // mean while, initialize all extra nodes be NULL. + i = 0; + tetrahedrons->traversalinit(); + tetloop.tet = tetrahedrontraverse(); + while (tetloop.tet != (tetrahedron*)NULL) { + tetloop.tet[highorderindex] = (tetrahedron)&highordertable[i]; + for (j = 0; j < 6; j++) { + highordertable[i + j] = (point)NULL; } - } + i += 6; + tetloop.tet = tetrahedrontraverse(); } + + // To create a unique node on each edge. Loop over all tetrahedra, and + // look at the six edges of each tetrahedron. If the extra node in + // the tetrahedron corresponding to this edge is NULL, create a node + // for this edge, at the same time, set the new node into the extra + // node lists of all other tetrahedra sharing this edge. + tetrahedrons->traversalinit(); tetloop.tet = tetrahedrontraverse(); - } - - if (!b->quiet) { - printf(" Checking seg->tet connections...\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 (!b->quiet) { - printf(" Checking seg->seg connections...\n"); - } - - 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 { - // 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) && (sdest(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 { - spivotself(checkseg); - checkseg.shver = 0; - if ((sorg(checkseg) != pa) && (sdest(checkseg) != pa)) { - printf(" !! Wrong seg-seg connection at point %d.\n", - pointmark(pa)); - horrors++; - } - } - } - } + 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(); } - pa = pointtraverse(); - } - - if (horrors == 0) { - printf(" Segments are connected properly.\n"); - } else { - printf(" !! !! !! !! Found %d missing connections.\n", horrors); - } - if (miscount > 0) { - printf(" !! !! Found %d missing segments.\n", miscount); - } - - return horrors; } /////////////////////////////////////////////////////////////////////////////// // // -// checkdelaunay() Ensure that the mesh is (constrained) Delaunay. // +// indexelements() Index all tetrahedra. // +// // +// Many output functions require that the tetrahedra are indexed. This // +// routine is called when -E option is used. // // // /////////////////////////////////////////////////////////////////////////////// -int tetgenmesh::checkdelaunay(int perturb) -{ - 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"); - } - - 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); - if (perturb) { - sign = insphere_s(pa, pb, pc, pd, pe); - } else { - sign = insphere(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++; - } +void tetgenmesh::indexelements() { + triface worktet; + int eindex = b->zeroindex ? 0 : in->firstnumber; // firstindex; + tetrahedrons->traversalinit(); + worktet.tet = tetrahedrontraverse(); + while (worktet.tet != NULL) { + setelemindex(worktet.tet, eindex); + eindex++; + if (b->metric) { // -m option + // Update the point-to-tet map, so that every point is pointing + // to a real tet, not a fictious one. Used by .p2t file. + tetrahedron tptr = encode(worktet); + for (int i = 0; i < 4; i++) { + setpoint2tet((point)(worktet.tet[4 + i]), tptr); + } } - } + worktet.tet = tetrahedrontraverse(); } - tetloop.tet = tetrahedrontraverse(); - } - - 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; } /////////////////////////////////////////////////////////////////////////////// // // -// Check if the current tetrahedralization is (constrained) regular. // +// numberedges() Count the number of edges, save in "meshedges". // // // -// 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. // +// 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. // // // /////////////////////////////////////////////////////////////////////////////// -int tetgenmesh::checkregular(int type) -{ - 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(" Checking %s %s property of the mesh...\n", - (type & 2) == 0 ? "Delaunay" : "regular", - (type & 1) == 0 ? " " : "(s)"); - } - - // 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]. - - 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]); - } - - 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++; - } +void tetgenmesh::numberedges() { + triface worktet, spintet; + int ishulledge; + int t1ver; + int i; + + meshedges = meshhulledges = 0l; + + tetrahedrons->traversalinit(); + worktet.tet = tetrahedrontraverse(); + while (worktet.tet != NULL) { + 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); + if (spintet.tet == worktet.tet) { + meshedges++; + if (ishulledge) meshhulledges++; + } } - } + infect(worktet); + worktet.tet = tetrahedrontraverse(); } - tetloop.tet = tetrahedrontraverse(); - } - - 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"); - } - - return horrors; } /////////////////////////////////////////////////////////////////////////////// // // -// checkconforming() Ensure that the mesh is conforming Delaunay. // +// outnodes() Output the points to a .node file or a tetgenio structure. // // // -// If 'flag' is 1, only check subsegments. If 'flag' is 2, check subfaces. // -// If 'flag' is 3, check both subsegments and subfaces. // +// Note: each point has already been numbered on input (the first index is // +// 'in->firstnumber'). // // // /////////////////////////////////////////////////////////////////////////////// -int tetgenmesh::checkconforming(int flag) -{ - 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; - - REAL A[4][4], rhs[4], D; - int indx[4]; - REAL elen[3]; - - encsubsegs = 0; - - if (flag & 1) { +void tetgenmesh::outnodes(tetgenio* out) { + 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) { - printf(" Checking conforming property of segments...\n"); + if (out == (tetgenio*)NULL) { + printf("Writing %s.\n", outnodefilename); + } else { + printf("Writing nodes.\n"); + } } - encsubsegs = 0; - // 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); - } - - if (encsubsegs == 0) { - if (!b->quiet) { - printf(" The segments are conforming Delaunay.\n"); - } - } else { - printf(" !! !! %d subsegments are non-conforming.\n", encsubsegs); + nextras = numpointattrib; + if (b->weighted) { // -w + if (b->weighted_param == 0) weightDT = 1; // Weighted DT. } - } // if (flag & 1) - encsubfaces = 0; + bmark = !b->nobound && in->pointmarkerlist; - if (flag & 2) { - if (!b->quiet) { - printf(" Checking conforming property of subfaces...\n"); + 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; } - // 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]); + // Determine the first index (0 or 1). + firstindex = b->zeroindex ? 0 : in->firstnumber; - // 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; + 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(...)) + } + } + 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. + } } - } - sesymself(shloop); } - } - shloop.sh = shellfacetraverse(subfaces); + pointloop = pointtraverse(); + pointnumber++; + index++; } - if (encsubfaces == 0) { - if (!b->quiet) { - printf(" The subfaces are conforming Delaunay.\n"); - } - } else { - printf(" !! !! %d subfaces are non-conforming.\n", encsubfaces); + if (out == (tetgenio*)NULL) { + fprintf(outfile, "# Generated by %s\n", b->commandline); + fclose(outfile); } - } // if (flag & 2) - - return encsubsegs + encsubfaces; } /////////////////////////////////////////////////////////////////////////////// // // -// qualitystatistics() Print statistics about the quality of the mesh. // +// outmetrics() Output the metric to a file (*.mtr) or a tetgenio obj. // // // /////////////////////////////////////////////////////////////////////////////// -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 smallestradiusratio, biggestradiusratio; // radius-edge ratio. - 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; - // Report the tet which has the biggest radius-edge ratio. - triface biggestradiusratiotet; - - printf("Mesh quality statistics:\n\n"); - - shortlen = longlen = 0.0; - smalldiangle = bigdiangle = 0.0; - total_tet_vol = 0.0; - total_tetprism_vol = 0.0; - - 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; - - 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; - - minaltitude = xmax - xmin + ymax - ymin + zmax - zmin; - minaltitude = minaltitude * minaltitude; - shortest = minaltitude; - longest = 0.0; - smallestvolume = minaltitude; - biggestvolume = 0.0; - smallestratio = smallestradiusratio = 1e+16; // minaltitude; - biggestratio = biggestradiusratio = 0.0; - smallestdiangle = smallestfaangle = 180.0; - biggestdiangle = biggestfaangle = 0.0; - - - int attrnum = numelemattrib - 1; - - // Loop all elements, calculate quality parameters for each element. - tetrahedrons->traversalinit(); - 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; - } +void tetgenmesh::outmetrics(tetgenio* out) { + FILE* outfile = NULL; + char outmtrfilename[FILENAMESIZE]; + point ptloop; + int mtrindex = 0; + int i; + int msize = (sizeoftensor - useinsertradius); + if (msize == 0) { + return; } - // Get four vertices: p0, p1, p2, p3. - for (i = 0; i < 4; i++) p[i] = (point) tetloop.tet[4 + i]; - - // Get the tet volume. - tetvol = orient3dfast(p[1], p[0], p[2], p[3]) / 6.0; - total_tet_vol += tetvol; - total_tetprism_vol += tetprismvol(p[0], p[1], p[2], p[3]); - - // Calculate the largest and smallest volume. - if (tetvol < smallestvolume) { - smallestvolume = tetvol; - } - if (tetvol > biggestvolume) { - biggestvolume = tetvol; + if (out == (tetgenio*)NULL) { + strcpy(outmtrfilename, b->outfilename); + strcat(outmtrfilename, ".mtr"); } - // 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. - - // Get the squares of the edge lengths. - for (i = 0; i < 6; i++) edgelength[i] = dot(V[i], V[i]); - - // 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]; - } - } - - // 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]; + if (!b->quiet) { + if (out == (tetgenio*)NULL) { + printf("Writing %s.\n", outmtrfilename); + } else { + printf("Writing metrics.\n"); + } } - // 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 < 4; i++) { - if (H[i] > minheightinv) minheightinv = H[i]; - } + 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, msize); } else { - // A nearly degenerated tet. - if (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 < 4; 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); - } - - // 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; - - // 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. + // Allocate space for 'pointmtrlist'. + out->numberofpointmtrs = msize; + out->pointmtrlist = new REAL[points->items * msize]; + if (out->pointmtrlist == (REAL*)NULL) { + terminatetetgen(this, 1); + } + } + + points->traversalinit(); + ptloop = pointtraverse(); + while (ptloop != (point)NULL) { + if (out == (tetgenio*)NULL) { + for (i = 0; i < msize; i++) { + fprintf(outfile, " %-16.8e", ptloop[pointmtrindex + i]); + } + fprintf(outfile, "\n"); } else { - tendegree--; // In the right column. + for (i = 0; i < msize; i++) { + out->pointmtrlist[mtrindex++] = ptloop[pointmtrindex + i]; + } } - } - dihedangletable[tendegree]++; + ptloop = pointtraverse(); } + // Output the point-to-tet map. + if (out == (tetgenio*)NULL) { + strcpy(outmtrfilename, b->outfilename); + strcat(outmtrfilename, ".p2t"); + } + if (!b->quiet) { + if (out == (tetgenio*)NULL) { + printf("Writing %s.\n", outmtrfilename); + } else { + printf("Writing point-to-tet map.\n"); + } + } - // 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; + 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); } - // 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]++; + // Number of points, + // fprintf(outfile, "%ld\n", points->items); + } else { + // Allocate space for 'point2tetlist'. + out->point2tetlist = new int[points->items]; + if (out->point2tetlist == (int*)NULL) { + terminatetetgen(this, 1); } - } } - // Calculate aspect ratio and radius-edge ratio for this element. - tetradius = cirradius / sqrt(shortlen); - if (tetradius < smallestradiusratio) { - smallestradiusratio = tetradius; - } - if (tetradius > biggestradiusratio) { - biggestradiusratio = tetradius; - biggestradiusratiotet.tet = tetloop.tet; - } - // 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++; + // The list of tetrahedra must be indexed. + if (bgm != NULL) { + bgm->indexelements(); } - aspecttable[aspectindex]++; - radiusindex = 0; - while ((tetradius > radiusratiotable[radiusindex]) && (radiusindex < 11)) { - radiusindex++; + // Determine the first index (0 or 1). + int firstindex = b->zeroindex ? 0 : in->firstnumber; + int pointindex = firstindex; + i = 0; + + triface parenttet; + points->traversalinit(); + ptloop = pointtraverse(); + while (ptloop != (point)NULL) { + if (bgm != NULL) { + bgm->decode(point2bgmtet(ptloop), parenttet); + } else { + decode(point2tet(ptloop), parenttet); + } + if (out == (tetgenio*)NULL) { + fprintf(outfile, "%d %d\n", pointindex, elemindex(parenttet.tet)); + } else { + out->point2tetlist[i] = elemindex(parenttet.tet); + } + pointindex++; + i++; + ptloop = pointtraverse(); } - radiustable[radiusindex]++; - tetloop.tet = tetrahedrontraverse(); - } - - shortest = sqrt(shortest); - longest = sqrt(longest); - minaltitude = sqrt(minaltitude); - - 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); - - printf(" Aspect ratio histogram:\n"); - printf(" < %-6.6g : %8ld | %6.6g - %-6.6g : %8ld\n", - aspectratiotable[0], aspecttable[0], aspectratiotable[5], - aspectratiotable[6], aspecttable[6]); - for (i = 1; i < 5; i++) { - printf(" %6.6g - %-6.6g : %8ld | %6.6g - %-6.6g : %8ld\n", - aspectratiotable[i - 1], aspectratiotable[i], aspecttable[i], - aspectratiotable[i + 5], aspectratiotable[i + 6], - aspecttable[i + 6]); - } - printf(" %6.6g - %-6.6g : %8ld | %6.6g - : %8ld\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: %8ld | %3d - %3d degrees: %8ld\n", - i * 10, i * 10 + 10, faceangletable[i], - i * 10 + 90, i * 10 + 100, faceangletable[i + 9]); - } - if (minfaceang != PI) { - printf(" Minimum input face angle is %g (degree).\n", - minfaceang / PI * 180.0); - } - printf("\n"); - - printf(" Dihedral angle histogram:\n"); - // Print the three two rows: - printf(" %3d - %2d degrees: %8ld | %3d - %3d degrees: %8ld\n", - 0, 5, dihedangletable[0], 80, 110, dihedangletable[9]); - printf(" %3d - %2d degrees: %8ld | %3d - %3d degrees: %8ld\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: %8ld | %3d - %3d degrees: %8ld\n", - (i - 1) * 10, (i - 1) * 10 + 10, dihedangletable[i], - (i - 1) * 10 + 110, (i - 1) * 10 + 120, dihedangletable[i + 9]); - } - // Print the last two rows. - printf(" %3d - %2d degrees: %8ld | %3d - %3d degrees: %8ld\n", - 60, 70, dihedangletable[7], 170, 175, dihedangletable[16]); - printf(" %3d - %2d degrees: %8ld | %3d - %3d degrees: %8ld\n", - 70, 80, dihedangletable[8], 175, 180, dihedangletable[17]); - if (minfacetdihed != PI) { - printf(" Minimum input dihedral angle is %g (degree).\n", - minfacetdihed / PI * 180.0); - } - printf("\n"); - - printf("\n"); + if (out == (tetgenio*)NULL) { + fprintf(outfile, "# Generated by %s\n", b->commandline); + fclose(outfile); + } } - /////////////////////////////////////////////////////////////////////////////// // // -// memorystatistics() Report the memory usage. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::memorystatistics() -{ - printf("Memory usage statistics:\n\n"); - - // Count the number of blocks of tetrahedra. - int tetblocks = 0; - tetrahedrons->pathblock = tetrahedrons->firstblock; - while (tetrahedrons->pathblock != NULL) { - tetblocks++; - tetrahedrons->pathblock = (void **) *(tetrahedrons->pathblock); - } - - // Calculate the total memory (in bytes) used by storing meshes. - unsigned long totalmeshmemory = 0l, totalt2shmemory = 0l; - totalmeshmemory = points->maxitems * points->itembytes + - tetrahedrons->maxitems * tetrahedrons->itembytes; - if (b->plc || b->refine) { - totalmeshmemory += (subfaces->maxitems * subfaces->itembytes + - subsegs->maxitems * subsegs->itembytes); - totalt2shmemory = (tet2subpool->maxitems * tet2subpool->itembytes + - tet2segpool->maxitems * tet2segpool->itembytes); - } - - unsigned long totalalgomemory = 0l; - totalalgomemory = cavetetlist->totalmemory + cavebdrylist->totalmemory + - caveoldtetlist->totalmemory + - flippool->maxitems * flippool->itembytes; - if (b->plc || b->refine) { - totalalgomemory += (subsegstack->totalmemory + subfacstack->totalmemory + - subvertstack->totalmemory + - caveshlist->totalmemory + caveshbdlist->totalmemory + - cavesegshlist->totalmemory + - cavetetshlist->totalmemory + - cavetetseglist->totalmemory + - caveencshlist->totalmemory + - caveencseglist->totalmemory + - cavetetvertlist->totalmemory + - unflipqueue->totalmemory); - } - - printf(" Maximum number of tetrahedra: %ld\n", tetrahedrons->maxitems); - printf(" Maximum number of tet blocks (blocksize = %d): %d\n", - b->tetrahedraperblock, tetblocks); - /* - if (b->plc || b->refine) { - printf(" Approximate memory for tetrahedral mesh (bytes): %ld\n", - totalmeshmemory); - - printf(" Approximate memory for extra pointers (bytes): %ld\n", - totalt2shmemory); - } else { - printf(" Approximate memory for tetrahedralization (bytes): %ld\n", - totalmeshmemory); - } - printf(" Approximate memory for algorithms (bytes): %ld\n", - totalalgomemory); - printf(" Approximate memory for working arrays (bytes): %ld\n", - totalworkmemory); - printf(" Approximate total used memory (bytes): %ld\n", - totalmeshmemory + totalt2shmemory + totalalgomemory + - totalworkmemory); - */ - if (b->plc || b->refine) { - printf(" Approximate memory for tetrahedral mesh (bytes): "); - printfcomma(totalmeshmemory); printf("\n"); - - printf(" Approximate memory for extra pointers (bytes): "); - printfcomma(totalt2shmemory); printf("\n"); - } else { - printf(" Approximate memory for tetrahedralization (bytes): "); - printfcomma(totalmeshmemory); printf("\n"); - } - printf(" Approximate memory for algorithms (bytes): "); - printfcomma(totalalgomemory); printf("\n"); - printf(" Approximate memory for working arrays (bytes): "); - printfcomma(totalworkmemory); printf("\n"); - printf(" Approximate total used memory (bytes): "); - printfcomma(totalmeshmemory + totalt2shmemory + totalalgomemory + - totalworkmemory); - printf("\n"); - - printf("\n"); -} - -/////////////////////////////////////////////////////////////////////////////// +// outelements() Output the tetrahedra to an .ele file or a tetgenio // +// structure. // // // -// statistics() Print all sorts of cool facts. // +// This routine also indexes all tetrahedra (exclusing hull tets) (from in-> // +// firstnumber). The total number of mesh edges is counted in 'meshedges'. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::statistics() -{ - long tetnumber, facenumber; - - printf("\nStatistics:\n\n"); - printf(" Input points: %d\n", in->numberofpoints); - if (b->refine) { - printf(" Input tetrahedra: %d\n", in->numberoftetrahedra); - if (in->numberoftrifaces > 0) { - printf(" Input triangles: %d\n", in->numberoftrifaces); - } - if (in->numberofedges > 0) { - printf(" Input edges: %d\n", in->numberofedges); - } - } else if (b->plc) { - printf(" Input facets: %d\n", in->numberoffacets); - printf(" Input segments: %ld\n", insegments); - if (in->numberofedges > 0) { - printf(" Input edges: %d\n", in->numberofedges); - } - printf(" Input holes: %d\n", in->numberofholes); - printf(" Input regions: %d\n", in->numberofregions); - } - - 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); - } - } +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 (b->plc || b->refine) { - printf(" Mesh faces on exterior boundary: %ld\n", hullsize); - if (meshhulledges > 0l) { - printf(" Mesh edges on exterior boundary: %ld\n", meshhulledges); + if (out == (tetgenio*)NULL) { + strcpy(outelefilename, b->outfilename); + strcat(outelefilename, ".ele"); } - printf(" Mesh faces on input facets: %ld\n", subfaces->items); - printf(" Mesh edges on input segments: %ld\n", subsegs->items); - if (st_facref_count > 0l) { - printf(" Steiner points on input facets: %ld\n", st_facref_count); - } - if (st_segref_count > 0l) { - printf(" Steiner points on input segments: %ld\n", st_segref_count); - } - if (st_volref_count > 0l) { - printf(" Steiner points inside domain: %ld\n", st_volref_count); - } - } else { - printf(" Convex hull faces: %ld\n", hullsize); - if (meshhulledges > 0l) { - printf(" Convex hull edges: %ld\n", meshhulledges); + + if (!b->quiet) { + if (out == (tetgenio*)NULL) { + printf("Writing %s.\n", outelefilename); + } else { + printf("Writing elements.\n"); + } } - } - if (b->weighted) { // -w option - printf(" Skipped non-regular points: %ld\n", nonregularcount); - } - printf("\n"); + // The number of tets excluding hull tets. + ntets = tetrahedrons->items - hullsize; - if (b->verbose > 0) { - if (b->plc || b->refine) { // -p or -r - if (tetrahedrons->items > 0l) { - qualitystatistics(); - } + 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; } - if (tetrahedrons->items > 0l) { - memorystatistics(); + + // 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. } - } -} -//// //// -//// //// -//// meshstat_cxx ///////////////////////////////////////////////////////////// + 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); + if (b->metric) { // -m option + // Update the point-to-tet map, so that every point is pointing + // to a real tet, not a fictious one. Used by .p2t file. + for (int i = 0; i < 4; i++) { + setpoint2tet((point)(tptr[4 + i]), (tetrahedron)tptr); + } + } + tptr = tetrahedrontraverse(); + elementnumber++; + } -//// output_cxx /////////////////////////////////////////////////////////////// -//// //// -//// //// + if (out == (tetgenio*)NULL) { + fprintf(outfile, "# Generated by %s\n", b->commandline); + fclose(outfile); + } +} /////////////////////////////////////////////////////////////////////////////// // // -// 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. // +// outfaces() Output all faces to a .face file or a tetgenio object. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::jettisonnodes() -{ - point pointloop; - bool jetflag; - int oldidx, newidx; - int remcount; - - if (!b->quiet) { - printf("Jettisoning redundant points.\n"); - } - - 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++; +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 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; + + // For -nn option. + int* tet2facelist = NULL; + int tidx; + + 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 { - // 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]; + // 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->face2tetlist = new int[faces * 2]; + if (out->face2tetlist == (int*)NULL) { + printf("Error: Out of memory.\n"); + terminatetetgen(this, 1); + } } - } - newidx++; + out->numberoftrifaces = faces; + elist = out->trifacelist; + emlist = out->trifacemarkerlist; } - oldidx++; - pointloop = pointtraverse(); - } - if (b->verbose) { - printf(" %ld duplicated vertices are removed.\n", dupverts); - printf(" %ld unused vertices are removed.\n", unuverts); - } - dupverts = 0l; - unuverts = 0l; - - // The following line ensures that dead items in the pool of nodes cannot - // be allocated for the new created nodes. This ensures that the input - // nodes will occur earlier in the output files, and have lower indices. - points->deaditemstack = (void *) NULL; -} -/////////////////////////////////////////////////////////////////////////////// -// // -// highorder() Create extra nodes for quadratic subparametric elements. // -// // -// 'highordertable' is an array (size = numberoftetrahedra * 6) for storing // -// high-order nodes of each tetrahedron. This routine is used only when -o2 // -// switch is used. // -// // -/////////////////////////////////////////////////////////////////////////////// + if (b->neighout > 1) { // -nn option + // Output the tetrahedron-to-face map. + tet2facelist = new int[ntets * 4]; + } -void tetgenmesh::highorder() -{ - triface tetloop, worktet, spintet; - point *extralist, *adjextralist; - point torg, tdest, newpoint; - int highorderindex; - int t1ver; - int i, j; - - if (!b->quiet) { - printf("Adding vertices for second-order tetrahedra.\n"); - } - - // Initialize the 'highordertable'. - highordertable = new point[tetrahedrons->items * 6]; - if (highordertable == (point *) NULL) { - terminatetetgen(this, 1); - } - - // 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(); - } - - // 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]); + // 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 { + marker = shellmark(checkmark); + } + } 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. + if (!ishulltet(tface)) { + neigh1 = elemindex(tface.tet); + } else { + neigh1 = -1; + } + if (!ishulltet(tsymface)) { + neigh2 = elemindex(tsymface.tet); + } else { + neigh2 = -1; + } + // Fill the tetrahedron-to-face map. + tidx = elemindex(tface.tet) - firstindex; + tet2facelist[tidx * 4 + tface.ver] = facenumber; + if (!ishulltet(tsymface)) { + tidx = elemindex(tsymface.tet) - firstindex; + tet2facelist[tidx * 4 + (tsymface.ver & 3)] = facenumber; + } + } + 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->face2tetlist[(facenumber - in->firstnumber) * 2] = neigh1; + out->face2tetlist[(facenumber - in->firstnumber) * 2 + 1] = neigh2; + } + } + facenumber++; + } + } + tface.tet = tetrahedrontraverse(); + } + + if (out == (tetgenio*)NULL) { + fprintf(outfile, "# Generated by %s\n", b->commandline); + fclose(outfile); + } + + if (b->neighout > 1) { // -nn option + // Output the tetrahedron-to-face map. + if (out == (tetgenio*)NULL) { + strcpy(facefilename, b->outfilename); + strcat(facefilename, ".t2f"); } - // 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 (!b->quiet) { + if (out == (tetgenio*)NULL) { + printf("Writing %s.\n", facefilename); + } else { + printf("Writing tetrahedron-to-face map.\n"); + } } - } // if (!extralist[i]) - } // i - tetloop.tet = tetrahedrontraverse(); - } + if (out == (tetgenio*)NULL) { + outfile = fopen(facefilename, "w"); + for (tidx = 0; tidx < ntets; tidx++) { + index = tidx * 4; + fprintf(outfile, "%4d %d %d %d %d\n", tidx + in->firstnumber, tet2facelist[index], + tet2facelist[index + 1], tet2facelist[index + 2], tet2facelist[index + 3]); + } + fclose(outfile); + delete[] tet2facelist; + } else { + // Simply copy the address of the list to the output. + out->tet2facelist = tet2facelist; + } + } } /////////////////////////////////////////////////////////////////////////////// // // -// indexelements() Index all tetrahedra. // +// outhullfaces() Output hull faces to a .face file or a tetgenio object. // // // -// Many output functions require that the tetrahedra are indexed. This // -// routine is called when -E option is used. // +// The normal of each face is pointing to the outside of the domain. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::indexelements() -{ - triface worktet; - int eindex = b->zeroindex ? 0 : in->firstnumber; // firstindex; - tetrahedrons->traversalinit(); - worktet.tet = tetrahedrontraverse(); - while (worktet.tet != NULL) { - setelemindex(worktet.tet, eindex); - eindex++; - if (b->metric) { // -m option - // Update the point-to-tet map, so that every point is pointing - // to a real tet, not a fictious one. Used by .p2t file. - tetrahedron tptr = encode(worktet); - for (int i = 0; i < 4; i++) { - setpoint2tet((point) (worktet.tet[4 + i]), tptr); - } - } - worktet.tet = tetrahedrontraverse(); - } -} +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; -/////////////////////////////////////////////////////////////////////////////// -// // -// 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. // -// // -/////////////////////////////////////////////////////////////////////////////// + if (out == (tetgenio*)NULL) { + strcpy(facefilename, b->outfilename); + strcat(facefilename, ".face"); + } -void tetgenmesh::numberedges() -{ - triface worktet, spintet; - int ishulledge; - int t1ver; - int i; + 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; + } - meshedges = meshhulledges = 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. + } - tetrahedrons->traversalinit(); - worktet.tet = tetrahedrontraverse(); - while (worktet.tet != NULL) { - 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; + 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++; } - fnextself(spintet); - } while (spintet.tet != worktet.tet); - if (spintet.tet == worktet.tet) { - meshedges++; - if (ishulledge) meshhulledges++; - } + hulltet.tet = alltetrahedrontraverse(); + } + + if (out == (tetgenio*)NULL) { + fprintf(outfile, "# Generated by %s\n", b->commandline); + fclose(outfile); } - infect(worktet); - worktet.tet = tetrahedrontraverse(); - } } /////////////////////////////////////////////////////////////////////////////// // // -// outnodes() Output the points to a .node file or a tetgenio structure. // +// outsubfaces() Output subfaces (i.e. boundary faces) to a .face file or // +// a tetgenio structure. // // // -// Note: each point has already been numbered on input (the first index is // -// 'in->firstnumber'). // +// 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::outnodes(tetgenio* out) -{ - 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("Writing nodes.\n"); - } - } +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 marker = 0; + int firstindex, shift; + int neigh1 = 0, neigh2 = 0; + int facenumber; - nextras = numpointattrib; - if (b->weighted) { // -w - if (b->weighted_param == 0) weightDT = 1; // Weighted DT. - } + // For -o2 option. + triface workface; + point *extralist, pp[3] = {0, 0, 0}; + int highorderindex = 11; + int o2index = 0, i; - bmark = !b->nobound && in->pointmarkerlist; + int t1ver; // used by fsymself() - 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); + if (out == (tetgenio*)NULL) { + strcpy(facefilename, b->outfilename); + strcat(facefilename, ".face"); } - // 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); - } + + if (!b->quiet) { + if (out == (tetgenio*)NULL) { + printf("Writing %s.\n", facefilename); + } else { + printf("Writing faces.\n"); + } } - // 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 (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->face2tetlist = new int[subfaces->items * 2]; + if (out->face2tetlist == (int*)NULL) { + terminatetetgen(this, 1); + } + } + out->numberoftrifaces = subfaces->items; + elist = out->trifacelist; + emlist = out->trifacemarkerlist; } - if (b->psc) { - out->pointparamlist = new tetgenio::pointparam[points->items]; - if (out->pointparamlist == NULL) { - printf("Error: Out of memory.\n"); - terminatetetgen(this, 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. } - out->numberofpoints = points->items; - out->numberofpointattributes = nextras; - coordindex = 0; - attribindex = 0; - } - - // Determine the first index (0 or 1). - firstindex = b->zeroindex ? 0 : in->firstnumber; - - 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(...)) - } - } - 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. + + 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); + } } - } - 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]; + 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 { - 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; + // 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) { + marker = shellmark(faceloop); + } + if (b->neighout > 1) { + // '-nn' switch. Output adjacent tets indices. + neigh1 = -1; + neigh2 = -1; + stpivot(faceloop, abuttingtet); + if (abuttingtet.tet != NULL) { + if (!ishulltet(abuttingtet)) { + 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 { - out->pointparamlist[index].type = -1; // Unknown type. + // 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->face2tetlist[index2++] = neigh1; + out->face2tetlist[index2++] = neigh2; + } } - } + facenumber++; + faceloop.sh = shellfacetraverse(subfaces); } - pointloop = pointtraverse(); - pointnumber++; - index++; - } - if (out == (tetgenio *) NULL) { - fprintf(outfile, "# Generated by %s\n", b->commandline); - fclose(outfile); - } + 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. // +// 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::outmetrics(tetgenio* out) -{ - FILE *outfile = NULL; - char outmtrfilename[FILENAMESIZE]; - point ptloop; - int mtrindex = 0; - int i; - int msize = (sizeoftensor - useinsertradius); - if (msize == 0) { - return; - } - - 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, msize); - } else { - // Allocate space for 'pointmtrlist'. - out->numberofpointmtrs = msize; - out->pointmtrlist = new REAL[points->items * msize]; - if (out->pointmtrlist == (REAL *) NULL) { - terminatetetgen(this, 1); - } - } - - points->traversalinit(); - ptloop = pointtraverse(); - while (ptloop != (point) NULL) { - if (out == (tetgenio *) NULL) { - for (i = 0; i < msize; i++) { - fprintf(outfile, " %-16.8e", ptloop[pointmtrindex + i]); - } - fprintf(outfile, "\n"); - } else { - for (i = 0; i < msize; i++) { - out->pointmtrlist[mtrindex++] = ptloop[pointmtrindex + i]; - } - } - ptloop = pointtraverse(); - } +void tetgenmesh::outedges(tetgenio* out) { + FILE* outfile = NULL; + char edgefilename[FILENAMESIZE]; + triface tetloop, worktet, spintet; + face checkseg; + point torg, tdest; + int ishulledge; + int firstindex, shift; + int edgenumber, marker; + int index = 0, index1 = 0, index2 = 0; + int t1ver; + int i; - // Output the point-to-tet map. - if (out == (tetgenio *) NULL) { - strcpy(outmtrfilename, b->outfilename); - strcat(outmtrfilename, ".p2t"); - } + // For -o2 option. + point *extralist, pp = NULL; + int highorderindex = 11; + int o2index = 0; - if (!b->quiet) { - if (out == (tetgenio *) NULL) { - printf("Writing %s.\n", outmtrfilename); - } else { - printf("Writing point-to-tet map.\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, - //fprintf(outfile, "%ld\n", points->items); - } else { - // Allocate space for 'point2tetlist'. - out->point2tetlist = new int[points->items]; - if (out->point2tetlist == (int *) NULL) { - terminatetetgen(this, 1); - } - } - - // The list of tetrahedra must be indexed. - if (bgm != NULL) { - bgm->indexelements(); - } - // Determine the first index (0 or 1). - int firstindex = b->zeroindex ? 0 : in->firstnumber; - int pointindex = firstindex; - i = 0; - - triface parenttet; - points->traversalinit(); - ptloop = pointtraverse(); - while (ptloop != (point) NULL) { - if (bgm != NULL) { - bgm->decode(point2bgmtet(ptloop), parenttet); - } else { - decode(point2tet(ptloop), parenttet); - } - if (out == (tetgenio *) NULL) { - fprintf(outfile, "%d %d\n", pointindex, elemindex(parenttet.tet)); - } else { - out->point2tetlist[i] = elemindex(parenttet.tet); + // For -nn option. + int* tet2edgelist = NULL; + int tidx; + + if (out == (tetgenio*)NULL) { + strcpy(edgefilename, b->outfilename); + strcat(edgefilename, ".edge"); } - pointindex++; - i++; - ptloop = pointtraverse(); - } - if (out == (tetgenio *) NULL) { - fprintf(outfile, "# Generated by %s\n", b->commandline); - fclose(outfile); - } -} + if (!b->quiet) { + if (out == (tetgenio*)NULL) { + printf("Writing %s.\n", edgefilename); + } else { + printf("Writing edges.\n"); + } + } -/////////////////////////////////////////////////////////////////////////////// -// // -// 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'. // -// // -/////////////////////////////////////////////////////////////////////////////// + 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; + } + } + meshhulledges = 0l; // It will be counted. -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); + 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 { - printf("Writing elements.\n"); + // Allocate memory for 'edgelist'. + out->numberofedges = meshedges; + 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->edge2tetlist = new int[meshedges]; + } } - } - - // 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); + if (b->neighout > 1) { // -nn option + // Output the tetrahedron-to-edge map. + long tsize = tetrahedrons->items - hullsize; + tet2edgelist = new int[tsize * 6]; } - // 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); - if (b->metric) { // -m option - // Update the point-to-tet map, so that every point is pointing - // to a real tet, not a fictious one. Used by .p2t file. - for (int i = 0; i < 4; i++) { - setpoint2tet((point) (tptr[4 + i]), (tetrahedron) tptr); - } - } - 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. // -// // -/////////////////////////////////////////////////////////////////////////////// + // 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. + } -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 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; - - // For -nn option. - int *tet2facelist = NULL; - int tidx; - - 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"); + 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); + if (spintet.tet == worktet.tet) { + // Found a new edge. + if (ishulledge) meshhulledges++; + 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; + out->edgelist[index++] = pointmark(torg) - shift; + out->edgelist[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); + } 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 { + out->edgemarkerlist[index1++] = marker; + } + } + if (b->neighout > 1) { // '-nn' switch. + if (out == (tetgenio*)NULL) { + fprintf(outfile, " %d", elemindex(tetloop.tet)); + } else { + out->edge2tetlist[index2++] = elemindex(tetloop.tet); + } + // Fill the tetrahedron-to-edge map. + spintet = worktet; + while (1) { + if (!ishulltet(spintet)) { + tidx = elemindex(spintet.tet) - firstindex; + tet2edgelist[tidx * 6 + ver2edge[spintet.ver]] = edgenumber; + } + fnextself(spintet); + if (spintet.tet == worktet.tet) break; + } + } + if (out == (tetgenio*)NULL) { + fprintf(outfile, "\n"); + } + edgenumber++; + } + } + tetloop.tet = tetrahedrontraverse(); } - } - ntets = tetrahedrons->items - hullsize; - faces = (ntets * 4l + hullsize) / 2l; + if (out == (tetgenio*)NULL) { + fprintf(outfile, "# Generated by %s\n", b->commandline); + fclose(outfile); + } + + if (b->neighout > 1) { // -nn option + long tsize = tetrahedrons->items - hullsize; + + if (b->facesout) { // -f option + // Build the face-to-edge map (use the tet-to-edge map). + long fsize = (tsize * 4l + hullsize) / 2l; + int* face2edgelist = new int[fsize * 3]; + + tetrahedrons->traversalinit(); + tetloop.tet = tetrahedrontraverse(); + int facenumber = 0; // firstindex; // in->firstnumber; + while (tetloop.tet != (tetrahedron*)NULL) { + for (tetloop.ver = 0; tetloop.ver < 4; tetloop.ver++) { + fsym(tetloop, spintet); + if (ishulltet(spintet) || (elemindex(tetloop.tet) < elemindex(spintet.tet))) { + // The three edges of this face are ordered such that the + // first edge is opposite to the first vertex of this face + // that appears in the .face file, and so on. + tidx = elemindex(tetloop.tet) - firstindex; + worktet = tetloop; + for (i = 0; i < 3; i++) { + enextself(worktet); // The edge opposite to vertex i. + int eidx = tet2edgelist[tidx * 6 + ver2edge[worktet.ver]]; + face2edgelist[facenumber * 3 + i] = eidx; + } + facenumber++; + } + } + tetloop.tet = tetrahedrontraverse(); + } - 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->face2tetlist = new int[faces * 2]; - if (out->face2tetlist == (int *) NULL) { - printf("Error: Out of memory.\n"); - terminatetetgen(this, 1); - } - } - out->numberoftrifaces = faces; - elist = out->trifacelist; - emlist = out->trifacemarkerlist; - } - - if (b->neighout > 1) { // -nn option - // Output the tetrahedron-to-face map. - tet2facelist = new int[ntets * 4]; - } - - // 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. + // Output the face-to-edge map. + if (out == (tetgenio*)NULL) { + strcpy(edgefilename, b->outfilename); + strcat(edgefilename, ".f2e"); + } + if (!b->quiet) { + if (out == (tetgenio*)NULL) { + printf("Writing %s.\n", edgefilename); + } else { + printf("Writing face-to-edge map.\n"); + } + } + if (out == (tetgenio*)NULL) { + outfile = fopen(edgefilename, "w"); + for (tidx = 0; tidx < fsize; tidx++) { // Re-use `tidx' + i = tidx * 3; + fprintf(outfile, "%4d %d %d %d\n", tidx + in->firstnumber, face2edgelist[i], + face2edgelist[i + 1], face2edgelist[i + 2]); + } + fclose(outfile); + delete[] face2edgelist; } else { - marker = shellmark(checkmark); + // Simply copy the address of the list to the output. + out->face2edgelist = face2edgelist; } - } 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. - if (!ishulltet(tface)) { - neigh1 = elemindex(tface.tet); - } else { - neigh1 = -1; - } - if (!ishulltet(tsymface)) { - neigh2 = elemindex(tsymface.tet); - } else { - neigh2 = -1; - } - // Fill the tetrahedron-to-face map. - tidx = elemindex(tface.tet) - firstindex; - tet2facelist[tidx * 4 + tface.ver] = facenumber; - if (!ishulltet(tsymface)) { - tidx = elemindex(tsymface.tet) - firstindex; - tet2facelist[tidx * 4 + (tsymface.ver & 3)] = facenumber; - } - } - 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->face2tetlist[(facenumber - in->firstnumber) * 2] = neigh1; - out->face2tetlist[(facenumber - in->firstnumber) * 2 + 1] = neigh2; - } - } - facenumber++; - } - } - tface.tet = tetrahedrontraverse(); - } + } // if (b->facesout) - if (out == (tetgenio *) NULL) { - fprintf(outfile, "# Generated by %s\n", b->commandline); - fclose(outfile); - } - - if (b->neighout > 1) { // -nn option - // Output the tetrahedron-to-face map. - if (out == (tetgenio *) NULL) { - strcpy(facefilename, b->outfilename); - strcat(facefilename, ".t2f"); - } - if (!b->quiet) { - if (out == (tetgenio *) NULL) { - printf("Writing %s.\n", facefilename); - } else { - printf("Writing tetrahedron-to-face map.\n"); - } - } - if (out == (tetgenio *) NULL) { - outfile = fopen(facefilename, "w"); - for (tidx = 0; tidx < ntets; tidx++) { - index = tidx * 4; - fprintf(outfile, "%4d %d %d %d %d\n", tidx + in->firstnumber, - tet2facelist[index], tet2facelist[index+1], - tet2facelist[index+2], tet2facelist[index+3]); - } - fclose(outfile); - delete [] tet2facelist; - } else { - // Simply copy the address of the list to the output. - out->tet2facelist = tet2facelist; + // Output the tetrahedron-to-edge map. + if (out == (tetgenio*)NULL) { + strcpy(edgefilename, b->outfilename); + strcat(edgefilename, ".t2e"); + } + if (!b->quiet) { + if (out == (tetgenio*)NULL) { + printf("Writing %s.\n", edgefilename); + } else { + printf("Writing tetrahedron-to-edge map.\n"); + } + } + if (out == (tetgenio*)NULL) { + outfile = fopen(edgefilename, "w"); + for (tidx = 0; tidx < tsize; tidx++) { + i = tidx * 6; + fprintf(outfile, "%4d %d %d %d %d %d %d\n", tidx + in->firstnumber, + tet2edgelist[i], tet2edgelist[i + 1], tet2edgelist[i + 2], + tet2edgelist[i + 3], tet2edgelist[i + 4], tet2edgelist[i + 5]); + } + fclose(outfile); + delete[] tet2edgelist; + } else { + // Simply copy the address of the list to the output. + out->tet2edgelist = tet2edgelist; + } } - } } /////////////////////////////////////////////////////////////////////////////// // // -// 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. // +// outsubsegments() Output segments to a .edge file or a structure. // // // /////////////////////////////////////////////////////////////////////////////// -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(); - } +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; - if (out == (tetgenio *) NULL) { - fprintf(outfile, "# Generated by %s\n", b->commandline); - fclose(outfile); - } -} + // For -o2 option. + triface workface, spintet; + point *extralist, pp = NULL; + int highorderindex = 11; + int o2index = 0; -/////////////////////////////////////////////////////////////////////////////// -// // -// 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. // -// // -/////////////////////////////////////////////////////////////////////////////// + // For -nn option. + int neigh = -1; + int index2 = 0; -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 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"); - } - } + int t1ver; // used by fsymself() - 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); + if (out == (tetgenio*)NULL) { + strcpy(edgefilename, b->outfilename); + strcat(edgefilename, ".edge"); } - // 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->quiet) { + if (out == (tetgenio*)NULL) { + printf("Writing %s.\n", edgefilename); + } else { + printf("Writing edges.\n"); + } } - if (b->neighout > 1) { - // '-nn' switch. - out->face2tetlist = new int[subfaces->items * 2]; - if (out->face2tetlist == (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); - } - } - 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); + + 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 { - // 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) { - marker = shellmark(faceloop); - } - if (b->neighout > 1) { - // '-nn' switch. Output adjacent tets indices. - neigh1 = -1; - neigh2 = -1; - stpivot(faceloop, abuttingtet); - if (abuttingtet.tet != NULL) { - if (!ishulltet(abuttingtet)) { - 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->face2tetlist[index2++] = neigh1; - out->face2tetlist[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. // -// // -/////////////////////////////////////////////////////////////////////////////// + // 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->edge2tetlist = new int[subsegs->items]; + } + out->numberofedges = subsegs->items; + elist = out->edgelist; + } -void tetgenmesh::outedges(tetgenio* out) -{ - FILE *outfile = NULL; - char edgefilename[FILENAMESIZE]; - triface tetloop, worktet, spintet; - face checkseg; - point torg, tdest; - 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; - - // For -nn option. - int *tet2edgelist = NULL; - int tidx; - - 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"); + // 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; - 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; - } - } - meshhulledges = 0l; // It will be counted. - - 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->numberofedges = meshedges; - 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->edge2tetlist = new int[meshedges]; - } - } - - if (b->neighout > 1) { // -nn option - // Output the tetrahedron-to-edge map. - long tsize = tetrahedrons->items - hullsize; - tet2edgelist = new int[tsize * 6]; - } - - // 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; + 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; + } + workface = spintet; + } + } } - fnextself(spintet); - } while (spintet.tet != worktet.tet); - if (spintet.tet == worktet.tet) { - // Found a new edge. - if (ishulledge) meshhulledges++; - 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; - out->edgelist[index++] = pointmark(torg) - shift; - out->edgelist[index++] = pointmark(tdest) - shift; - if (b->order == 2) { // -o2 - out->o2edgelist[o2index++] = pointmark(pp) - shift; - } + // 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->nobound) { - if (b->plc || b->refine) { - // Check if the edge is a segment. - tsspivot1(worktet, checkseg); - if (checkseg.sh != NULL) { - marker = shellmark(checkseg); + if (b->neighout > 1) { // -nn + if (workface.tet != NULL) { + neigh = elemindex(workface.tet); } else { - marker = 0; // It's not a segment. + neigh = -1; } - } else { - // Mark it if it is a hull edge. - marker = ishulledge ? 1 : 0; - } - if (out == (tetgenio *) NULL) { - fprintf(outfile, " %d", marker); - } else { - out->edgemarkerlist[index1++] = marker; - } } - if (b->neighout > 1) { // '-nn' switch. - if (out == (tetgenio *) NULL) { - fprintf(outfile, " %d", elemindex(tetloop.tet)); - } else { - out->edge2tetlist[index2++] = elemindex(tetloop.tet); - } - // Fill the tetrahedron-to-edge map. - spintet = worktet; - while (1) { - if (!ishulltet(spintet)) { - tidx = elemindex(spintet.tet) - firstindex; - tet2edgelist[tidx * 6 + ver2edge[spintet.ver]] = edgenumber; - } - fnextself(spintet); - if (spintet.tet == worktet.tet) break; - } - } - if (out == (tetgenio *) NULL) { - fprintf(outfile, "\n"); + 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->edge2tetlist[index2++] = neigh; + } } edgenumber++; - } + edgeloop.sh = shellfacetraverse(subsegs); } - tetloop.tet = tetrahedrontraverse(); - } - if (out == (tetgenio *) NULL) { - fprintf(outfile, "# Generated by %s\n", b->commandline); - fclose(outfile); - } - - if (b->neighout > 1) { // -nn option - long tsize = tetrahedrons->items - hullsize; - - if (b->facesout) { // -f option - // Build the face-to-edge map (use the tet-to-edge map). - long fsize = (tsize * 4l + hullsize) / 2l; - int *face2edgelist = new int[fsize * 3]; - - tetrahedrons->traversalinit(); - tetloop.tet = tetrahedrontraverse(); - int facenumber = 0; // firstindex; // in->firstnumber; - while (tetloop.tet != (tetrahedron *) NULL) { - for (tetloop.ver = 0; tetloop.ver < 4; tetloop.ver++) { - fsym(tetloop, spintet); - if (ishulltet(spintet) || - (elemindex(tetloop.tet) < elemindex(spintet.tet))) { - // The three edges of this face are ordered such that the - // first edge is opposite to the first vertex of this face - // that appears in the .face file, and so on. - tidx = elemindex(tetloop.tet) - firstindex; - worktet = tetloop; - for (i = 0; i < 3; i++) { - enextself(worktet); // The edge opposite to vertex i. - int eidx = tet2edgelist[tidx * 6 + ver2edge[worktet.ver]]; - face2edgelist[facenumber * 3 + i] = eidx; - } - facenumber++; - } - } - tetloop.tet = tetrahedrontraverse(); - } - - // Output the face-to-edge map. - if (out == (tetgenio *) NULL) { - strcpy(edgefilename, b->outfilename); - strcat(edgefilename, ".f2e"); - } - if (!b->quiet) { - if (out == (tetgenio *) NULL) { - printf("Writing %s.\n", edgefilename); - } else { - printf("Writing face-to-edge map.\n"); - } - } - if (out == (tetgenio *) NULL) { - outfile = fopen(edgefilename, "w"); - for (tidx = 0; tidx < fsize; tidx++) { // Re-use `tidx' - i = tidx * 3; - fprintf(outfile, "%4d %d %d %d\n", tidx + in->firstnumber, - face2edgelist[i], face2edgelist[i+1], face2edgelist[i+2]); - } - fclose(outfile); - delete [] face2edgelist; - } else { - // Simply copy the address of the list to the output. - out->face2edgelist = face2edgelist; - } - } // if (b->facesout) - - // Output the tetrahedron-to-edge map. - if (out == (tetgenio *) NULL) { - strcpy(edgefilename, b->outfilename); - strcat(edgefilename, ".t2e"); - } - if (!b->quiet) { - if (out == (tetgenio *) NULL) { - printf("Writing %s.\n", edgefilename); - } else { - printf("Writing tetrahedron-to-edge map.\n"); - } - } - if (out == (tetgenio *) NULL) { - outfile = fopen(edgefilename, "w"); - for (tidx = 0; tidx < tsize; tidx++) { - i = tidx * 6; - fprintf(outfile, "%4d %d %d %d %d %d %d\n", tidx + in->firstnumber, - tet2edgelist[i], tet2edgelist[i+1], tet2edgelist[i+2], - tet2edgelist[i+3], tet2edgelist[i+4], tet2edgelist[i+5]); - } - fclose(outfile); - delete [] tet2edgelist; - } else { - // Simply copy the address of the list to the output. - out->tet2edgelist = tet2edgelist; - } - } + if (out == (tetgenio*)NULL) { + fprintf(outfile, "# Generated by %s\n", b->commandline); + fclose(outfile); + } } /////////////////////////////////////////////////////////////////////////////// // // -// outsubsegments() Output segments to a .edge file or a structure. // +// outneighbors() Output tet neighbors to a .neigh 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"); - } - } +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) { - outfile = fopen(edgefilename, "w"); - if (outfile == (FILE *) NULL) { - printf("File I/O Error: Cannot create file %s.\n", edgefilename); - terminatetetgen(this, 3); + if (out == (tetgenio*)NULL) { + strcpy(neighborfilename, b->outfilename); + strcat(neighborfilename, ".neigh"); } - // 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->edge2tetlist = 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; - } - 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->edge2tetlist[index2++] = neigh; - } - } - edgenumber++; - edgeloop.sh = shellfacetraverse(subsegs); - } - if (out == (tetgenio *) NULL) { - fprintf(outfile, "# Generated by %s\n", b->commandline); - fclose(outfile); - } -} + if (!b->quiet) { + if (out == (tetgenio*)NULL) { + printf("Writing %s.\n", neighborfilename); + } else { + printf("Writing neighbors.\n"); + } + } -/////////////////////////////////////////////////////////////////////////////// -// // -// outneighbors() Output tet neighbors to a .neigh file or a structure. // -// // -/////////////////////////////////////////////////////////////////////////////// + ntets = tetrahedrons->items - hullsize; -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]); + 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 { - nlist[index++] = neighbori[0]; - nlist[index++] = neighbori[1]; - nlist[index++] = neighbori[2]; - nlist[index++] = neighbori[3]; + // 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++; - } + 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); - } + if (out == (tetgenio*)NULL) { + fprintf(outfile, "# Generated by %s\n", b->commandline); + fclose(outfile); + } } /////////////////////////////////////////////////////////////////////////////// @@ -31247,457 +30878,457 @@ void tetgenmesh::outneighbors(tetgenio* out) // // /////////////////////////////////////////////////////////////////////////////// -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; - - int t1ver; // used by fsymself() - - // Output Voronoi vertices to .v.node file. - if (out == (tetgenio *) NULL) { - strcpy(outfilename, b->outfilename); - strcat(outfilename, ".v.node"); - } - - if (!b->quiet) { - if (out == (tetgenio *) NULL) { - printf("Writing %s.\n", outfilename); - } else { - printf("Writing Voronoi vertices.\n"); +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; + + int t1ver; // used by fsymself() + + // Output Voronoi vertices to .v.node file. + if (out == (tetgenio*)NULL) { + strcpy(outfilename, b->outfilename); + strcat(outfilename, ".v.node"); + } + + 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); + // 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). + // 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 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++; + // Allocate space (10 integers) into each tetrahedron. It re-uses the slot + // for element markers, flags. + i = 0; + tetrahedrons->traversalinit(); 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; - if (!nonconvex) { - edges = vsize + faces - ntets - 1; - } else { - if (meshedges == 0l) { - numberedges(); // Count edges. - } - edges = meshedges; - } - - 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)); + while (tetloop.tet != NULL) { + tetloop.tet[11] = (tetrahedron) & (indexarray[i * 10]); + i++; + tetloop.tet = alltetrahedrontraverse(); } - 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); + + // 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; + if (!nonconvex) { + edges = vsize + faces - ntets - 1; } else { - circumsphere(pt[0], pt[1], pt[2], pt[3], ccent, NULL); + if (meshedges == 0l) { + numberedges(); // Count edges. + } + edges = meshedges; } - if (out == (tetgenio *) NULL) { - fprintf(outfile, "%4d %16.8e %16.8e %16.8e\n", vpointcount + shift, - ccent[0], ccent[1], ccent[2]); + + 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 { - out->vpointlist[index++] = ccent[0]; - out->vpointlist[index++] = ccent[1]; - out->vpointlist[index++] = ccent[2]; + // Allocate space for 'vpointlist'. + out->numberofvpoints = (int)ntets; + out->vpointlist = new REAL[out->numberofvpoints * 3]; + if (out->vpointlist == (REAL*)NULL) { + terminatetetgen(this, 1); + } } - 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); + // 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 { - vedge = &(out->vedgelist[index++]); - vedge->v1 = end1 + shift; + circumsphere(pt[0], pt[1], pt[2], pt[3], ccent, NULL); } - if (!ishulltet(worktet)) { - end2 = elemindex(worktet.tet); + if (out == (tetgenio*)NULL) { + fprintf(outfile, "%4d %16.8e %16.8e %16.8e\n", vpointcount + shift, ccent[0], ccent[1], + ccent[2]); } 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]; - } + 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 { - 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; - } + printf("Writing Voronoi edges.\n"); } - // 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); - } - - // 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); + 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 { - printf("Writing Voronoi faces.\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 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); - } - } - - // 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; + // 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); + } + + // 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 { - //ishulledge = 1; - if (apex(spintet) == dummypoint) { - // We make this V-edge appear in the end of the edge list. - fnext(spintet, firsttet); - } + printf("Writing Voronoi faces.\n"); } - } // 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"); + } + + 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); } - vfacecount++; - } // if (spintet.tet == worktet.tet) - } // if (i = 0; i < 6; i++) + } + + // 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(); + } - 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"); - } - } - - 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 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); - } - } - - // 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; - } - } - tcount = (int) ptlist->objects; - 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] = tcount; - index = 1; - } - // 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"); + 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 { - vertarray[index++] = -1; + printf("Writing Voronoi cells.\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 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); } - } - if (out == (tetgenio *) NULL) { - fprintf(outfile, "\n"); - } - tetlist->restart(); - ptlist->restart(); - vpointcount++; } + + // 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; + } + } + tcount = (int)ptlist->objects; + 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] = tcount; + index = 1; + } + // 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; + } + } + if (out == (tetgenio*)NULL) { + fprintf(outfile, "\n"); + } + tetlist->restart(); + ptlist->restart(); + vpointcount++; + } + ploop = pointtraverse(); + } - // Delete the space for face/edge indices. - delete [] indexarray; + // Delete the space for face/edge indices. + delete[] indexarray; - if (out == (tetgenio *) NULL) { - fprintf(outfile, "# Generated by %s\n", b->commandline); - fclose(outfile); - } + if (out == (tetgenio*)NULL) { + fprintf(outfile, "# Generated by %s\n", b->commandline); + fclose(outfile); + } } /////////////////////////////////////////////////////////////////////////////// @@ -31711,95 +31342,92 @@ void tetgenmesh::outvoronoi(tetgenio* out) // // /////////////////////////////////////////////////////////////////////////////// -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 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 || in->trifacemarkerlist); - - 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) { - marker = shellmark(faceloop); - } - fprintf(outfile, "3 %4d %4d %4d", pointmark(p1) - shift, - pointmark(p2) - shift, pointmark(p3) - shift); - if (bmark) { - fprintf(outfile, " %d", marker); +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 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"); } - fprintf(outfile, "\n"); + 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 || in->trifacemarkerlist); + + 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); - } - - // 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]); - } - - fprintf(outfile, "# Generated by %s\n", b->commandline); - fclose(outfile); + while (faceloop.sh != (shellface*)NULL) { + p1 = sorg(faceloop); + p2 = sdest(faceloop); + p3 = sapex(faceloop); + if (bmark) { + marker = shellmark(faceloop); + } + 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); + } + + // 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]); + } + + fprintf(outfile, "# Generated by %s\n", b->commandline); + fclose(outfile); } /////////////////////////////////////////////////////////////////////////////// @@ -31813,154 +31441,147 @@ void tetgenmesh::outsmesh(char* smfilename) // // /////////////////////////////////////////////////////////////////////////////// -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 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"); - } - 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"); - - 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]); +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 marker; + int i; + + if (mfilename != (char*)NULL && mfilename[0] != '\0') { + strcpy(mefilename, mfilename); + } else if (b->outfilename[0] != '\0') { + strcpy(mefilename, b->outfilename); } else { - fprintf(outfile, " 0\n"); + strcpy(mefilename, "unnamed"); + } + strcat(mefilename, ".mesh"); + + if (!b->quiet) { + printf("Writing %s.\n", mefilename); } - setpointmark(ptloop, pointnumber); + 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"); + + fprintf(outfile, "\n# Set of mesh vertices\n"); + fprintf(outfile, "Vertices\n"); + fprintf(outfile, "%ld\n", points->items); + + points->traversalinit(); ptloop = pointtraverse(); - pointnumber++; - } - - // 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. + 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 { - marker = shellmark(checkmark); + fprintf(outfile, " 0\n"); } - fprintf(outfile, " %d\n", marker); - } + setpointmark(ptloop, pointnumber); + ptloop = pointtraverse(); + pointnumber++; } + + // 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(); - } - - 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"); + 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 { + marker = shellmark(checkmark); + } + fprintf(outfile, " %d\n", marker); + } + } + tface.tet = tetrahedrontraverse(); } - fprintf(outfile, "\n"); - tetptr = tetrahedrontraverse(); - } - fprintf(outfile, "\nCorners\n"); - fprintf(outfile, "%d\n", in->numberofpoints); + fprintf(outfile, "\n# Set of Tetrahedra\n"); + fprintf(outfile, "Tetrahedra\n"); + fprintf(outfile, "%ld\n", ntets); - for (i = 0; i < in->numberofpoints; i++) { - fprintf(outfile, "%4d\n", i + 1); - } - - if (b->plc || b->refine) { - fprintf(outfile, "\nEdges\n"); - fprintf(outfile, "%ld\n", subsegs->items); + 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(); + } - 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); - } - } - - fprintf(outfile, "\nEnd\n"); - fclose(outfile); -} + fprintf(outfile, "\nCorners\n"); + fprintf(outfile, "%d\n", in->numberofpoints); + for (i = 0; i < in->numberofpoints; i++) { + fprintf(outfile, "%4d\n", i + 1); + } + 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); + } + } + fprintf(outfile, "\nEnd\n"); + fclose(outfile); +} /////////////////////////////////////////////////////////////////////////////// // // @@ -31970,109 +31591,108 @@ void tetgenmesh::outmesh2medit(char* mfilename) // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::outmesh2vtk(char* ofilename) -{ - 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; - } - - int NEL = tetrahedrons->items - hullsize; - int NN = points->items; - - if (ofilename != (char *) NULL && ofilename[0] != '\0') { - strcpy(vtkfilename, ofilename); - } else if (b->outfilename[0] != '\0') { - strcpy(vtkfilename, b->outfilename); - } else { - strcpy(vtkfilename, "unnamed"); - } - strcat(vtkfilename, ".vtk"); - - if (!b->quiet) { - printf("Writing %s.\n", vtkfilename); - } - outfile = fopen(vtkfilename, "w"); - if (outfile == (FILE *) NULL) { - printf("File I/O Error: Cannot create file %s.\n", vtkfilename); - return; - } - - //always write big endian - //bool ImALittleEndian = !testIsBigEndian(); - - fprintf(outfile, "# vtk DataFile Version 2.0\n"); - fprintf(outfile, "Unstructured Grid\n"); - fprintf(outfile, "ASCII\n"); // BINARY - fprintf(outfile, "DATASET UNSTRUCTURED_GRID\n"); - fprintf(outfile, "POINTS %d double\n", NN); - - points->traversalinit(); - pointloop = pointtraverse(); - for(int id=0; idtraversalinit(); - tptr = tetrahedrontraverse(); - //elementnumber = firstindex; // in->firstnumber; - while (tptr != (tetrahedron *) NULL) { - if (!b->reversetetori) { - p1 = (point) tptr[4]; - p2 = (point) tptr[5]; +void tetgenmesh::outmesh2vtk(char* ofilename) { + 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; + } + + int NEL = tetrahedrons->items - hullsize; + int NN = points->items; + + if (ofilename != (char*)NULL && ofilename[0] != '\0') { + strcpy(vtkfilename, ofilename); + } else if (b->outfilename[0] != '\0') { + strcpy(vtkfilename, b->outfilename); } else { - 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"); + strcpy(vtkfilename, "unnamed"); + } + strcat(vtkfilename, ".vtk"); + + if (!b->quiet) { + printf("Writing %s.\n", vtkfilename); + } + outfile = fopen(vtkfilename, "w"); + if (outfile == (FILE*)NULL) { + printf("File I/O Error: Cannot create file %s.\n", vtkfilename); + return; + } + + // always write big endian + // bool ImALittleEndian = !testIsBigEndian(); + + fprintf(outfile, "# vtk DataFile Version 2.0\n"); + fprintf(outfile, "Unstructured Grid\n"); + fprintf(outfile, "ASCII\n"); // BINARY + fprintf(outfile, "DATASET UNSTRUCTURED_GRID\n"); + fprintf(outfile, "POINTS %d double\n", NN); + + points->traversalinit(); + pointloop = pointtraverse(); + for (int id = 0; id < NN && pointloop != (point)NULL; id++) { + x = pointloop[0]; + y = pointloop[1]; + z = pointloop[2]; + fprintf(outfile, "%.17g %.17g %.17g\n", x, y, z); + pointloop = pointtraverse(); + } + fprintf(outfile, "\n"); + + fprintf(outfile, "CELLS %d %d\n", NEL, NEL * (4 + 1)); + // NEL rows, each has 1 type id + 4 node id's + tetrahedrons->traversalinit(); tptr = tetrahedrontraverse(); - while (tptr != (tetrahedron *) NULL) { - fprintf(outfile, "%d\n", (int) elemattribute(tptr, numelemattrib - 1)); - 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"); - } - fclose(outfile); + fprintf(outfile, "CELL_TYPES %d\n", NEL); + for (int tid = 0; tid < NEL; tid++) { + fprintf(outfile, "%d\n", celltype); + } + fprintf(outfile, "\n"); + + if (numelemattrib > 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(); + } + fprintf(outfile, "\n"); + } + + fclose(outfile); } //// //// @@ -32107,344 +31727,334 @@ void tetgenmesh::outmesh2vtk(char* ofilename) // // /////////////////////////////////////////////////////////////////////////////// -void tetrahedralize(tetgenbehavior *b, tetgenio *in, tetgenio *out, - tetgenio *addin, tetgenio *bgmin) -{ - tetgenmesh m; - 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.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; - } +void tetrahedralize(tetgenbehavior* b, tetgenio* in, tetgenio* out, tetgenio* addin, + tetgenio* bgmin) { + tetgenmesh m; + clock_t tv[12], ts[5]; // Timing informations (defined in time.h) + REAL cps = (REAL)CLOCKS_PER_SEC; - m.initializepools(); - m.transfernodes(); + tv[0] = clock(); - exactinit(b->verbose, b->noexact, b->nostaticfilter, - m.xmax - m.xmin, m.ymax - m.ymin, m.zmax - m.zmin); + m.b = b; + m.in = in; + m.addin = addin; - tv[1] = clock(); + if (b->metric && bgmin && (bgmin->numberofpoints > 0)) { + m.bgm = new tetgenmesh(); // Create an empty background mesh. + m.bgm->b = b; + m.bgm->in = bgmin; + } - if (b->refine) { // -r - m.reconstructmesh(); - } else { // -p - m.incrementaldelaunay(ts[0]); - } + m.initializepools(); + m.transfernodes(); - tv[2] = clock(); + exactinit(b->verbose, b->noexact, b->nostaticfilter, m.xmax - m.xmin, m.ymax - m.ymin, + m.zmax - m.zmin); - if (!b->quiet) { - if (b->refine) { - printf("Mesh reconstruction seconds: %g\n", ((REAL)(tv[2]-tv[1])) / cps); - } else { - printf("Delaunay seconds: %g\n", ((REAL)(tv[2]-tv[1])) / cps); - if (b->verbose) { - printf(" Point sorting seconds: %g\n", ((REAL)(ts[0]-tv[1])) / cps); + tv[1] = clock(); - } + if (b->refine) { // -r + m.reconstructmesh(); + } else { // -p + m.incrementaldelaunay(ts[0]); } - } - - if (b->plc && !b->refine) { // -p - m.meshsurface(); - ts[0] = clock(); + tv[2] = clock(); if (!b->quiet) { - printf("Surface mesh seconds: %g\n", ((REAL)(ts[0]-tv[2])) / cps); + if (b->refine) { + printf("Mesh reconstruction seconds: %g\n", ((REAL)(tv[2] - tv[1])) / cps); + } else { + printf("Delaunay seconds: %g\n", ((REAL)(tv[2] - tv[1])) / cps); + if (b->verbose) { + printf(" Point sorting seconds: %g\n", ((REAL)(ts[0] - tv[1])) / cps); + } + } } - if (b->diagnose) { // -d - m.detectinterfaces(); + if (b->plc && !b->refine) { // -p + m.meshsurface(); - ts[1] = clock(); + ts[0] = clock(); - if (!b->quiet) { - printf("Self-intersection seconds: %g\n", ((REAL)(ts[1]-ts[0])) / cps); - } - - // Only output when self-intersecting faces exist. - if (m.subfaces->items > 0l) { - m.outnodes(out); - m.outsubfaces(out); - } - - return; - } - } + if (!b->quiet) { + printf("Surface mesh seconds: %g\n", ((REAL)(ts[0] - tv[2])) / cps); + } + if (b->diagnose) { // -d + m.detectinterfaces(); - tv[3] = clock(); + ts[1] = clock(); - if ((b->metric) && (m.bgm != NULL)) { // -m - m.bgm->initializepools(); - m.bgm->transfernodes(); - m.bgm->reconstructmesh(); + if (!b->quiet) { + printf("Self-intersection seconds: %g\n", ((REAL)(ts[1] - ts[0])) / cps); + } - ts[0] = clock(); + // Only output when self-intersecting faces exist. + if (m.subfaces->items > 0l) { + m.outnodes(out); + m.outsubfaces(out); + } - if (!b->quiet) { - printf("Background mesh reconstruct seconds: %g\n", - ((REAL)(ts[0] - tv[3])) / cps); + return; + } } - if (b->metric) { // -m - m.interpolatemeshsize(); + tv[3] = clock(); - ts[1] = clock(); + if ((b->metric) && (m.bgm != NULL)) { // -m + m.bgm->initializepools(); + m.bgm->transfernodes(); + m.bgm->reconstructmesh(); - if (!b->quiet) { - printf("Size interpolating seconds: %g\n",((REAL)(ts[1]-ts[0])) / cps); - } - } - } + ts[0] = clock(); - tv[4] = clock(); + if (!b->quiet) { + printf("Background mesh reconstruct seconds: %g\n", ((REAL)(ts[0] - tv[3])) / cps); + } - if (b->plc && !b->refine) { // -p - if (b->nobisect) { // -Y - m.recoverboundary(ts[0]); - } else { - m.constraineddelaunay(ts[0]); - } + if (b->metric) { // -m + m.interpolatemeshsize(); - ts[1] = clock(); + ts[1] = clock(); - if (!b->quiet) { - if (b->nobisect) { - printf("Boundary recovery "); - } else { - 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); - } + if (!b->quiet) { + printf("Size interpolating seconds: %g\n", ((REAL)(ts[1] - ts[0])) / cps); + } + } } - m.carveholes(); + tv[4] = clock(); - ts[2] = clock(); + if (b->plc && !b->refine) { // -p + if (b->nobisect) { // -Y + m.recoverboundary(ts[0]); + } else { + m.constraineddelaunay(ts[0]); + } - if (!b->quiet) { - printf("Exterior tets removal seconds: %g\n",((REAL)(ts[2]-ts[1]))/cps); - } + ts[1] = clock(); - if (b->nobisect) { // -Y - if (m.subvertstack->objects > 0l) { - m.suppresssteinerpoints(); + if (!b->quiet) { + if (b->nobisect) { + printf("Boundary recovery "); + } else { + 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); + } + } - ts[3] = clock(); + m.carveholes(); + + ts[2] = clock(); if (!b->quiet) { - printf("Steiner suppression seconds: %g\n", - ((REAL)(ts[3]-ts[2]))/cps); + printf("Exterior tets removal seconds: %g\n", ((REAL)(ts[2] - ts[1])) / cps); } - } - } - } - tv[5] = clock(); + if (b->nobisect) { // -Y + if (m.subvertstack->objects > 0l) { + m.suppresssteinerpoints(); - if (b->coarsen) { // -R - m.meshcoarsening(); - } + ts[3] = clock(); - tv[6] = clock(); + if (!b->quiet) { + printf("Steiner suppression seconds: %g\n", ((REAL)(ts[3] - ts[2])) / cps); + } + } + } + } - if (!b->quiet) { - if (b->coarsen) { - printf("Mesh coarsening seconds: %g\n", ((REAL)(tv[6] - tv[5])) / cps); + tv[5] = clock(); + + if (b->coarsen) { // -R + m.meshcoarsening(); } - } - if ((b->plc && b->nobisect) || b->coarsen) { - m.recoverdelaunay(); - } + tv[6] = clock(); - tv[7] = clock(); + if (!b->quiet) { + if (b->coarsen) { + printf("Mesh coarsening seconds: %g\n", ((REAL)(tv[6] - tv[5])) / cps); + } + } - if (!b->quiet) { if ((b->plc && b->nobisect) || b->coarsen) { - printf("Delaunay recovery seconds: %g\n", ((REAL)(tv[7] - tv[6]))/cps); + m.recoverdelaunay(); } - } - if ((b->plc || b->refine) && b->insertaddpoints) { // -i - if ((addin != NULL) && (addin->numberofpoints > 0)) { - m.insertconstrainedpoints(addin); - } - } + tv[7] = clock(); - tv[8] = clock(); + if (!b->quiet) { + if ((b->plc && b->nobisect) || b->coarsen) { + printf("Delaunay recovery seconds: %g\n", ((REAL)(tv[7] - tv[6])) / cps); + } + } - if (!b->quiet) { 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 ((addin != NULL) && (addin->numberofpoints > 0)) { + m.insertconstrainedpoints(addin); + } } - } - if (b->quality) { - m.delaunayrefinement(); - } + tv[8] = clock(); - tv[9] = clock(); + if (!b->quiet) { + 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->quiet) { if (b->quality) { - printf("Refinement seconds: %g\n", ((REAL)(tv[9] - tv[8])) / cps); + m.delaunayrefinement(); } - } - if ((b->plc || b->refine) && (b->optlevel > 0)) { - m.optimizemesh(); - } + tv[9] = clock(); - tv[10] = clock(); + if (!b->quiet) { + if (b->quality) { + printf("Refinement seconds: %g\n", ((REAL)(tv[9] - tv[8])) / cps); + } + } - if (!b->quiet) { if ((b->plc || b->refine) && (b->optlevel > 0)) { - printf("Optimization seconds: %g\n", ((REAL)(tv[10] - tv[9])) / cps); + m.optimizemesh(); } - } - - - if (!b->nojettison && ((m.dupverts > 0) || (m.unuverts > 0) - || (b->refine && (in->numberofcorners == 10)))) { - m.jettisonnodes(); - } - - if ((b->order == 2) && !b->convex) { - m.highorder(); - } - - if (!b->quiet) { - printf("\n"); - } - if (out != (tetgenio *) NULL) { - out->firstnumber = in->firstnumber; - out->mesh_dim = in->mesh_dim; - } + tv[10] = clock(); - if (b->nonodewritten || b->noiterationnum) { if (!b->quiet) { - printf("NOT writing a .node file.\n"); + if ((b->plc || b->refine) && (b->optlevel > 0)) { + printf("Optimization seconds: %g\n", ((REAL)(tv[10] - tv[9])) / cps); + } } - } else { - m.outnodes(out); - } - if (b->noelewritten) { - if (!b->quiet) { - printf("NOT writing an .ele file.\n"); + if (!b->nojettison && + ((m.dupverts > 0) || (m.unuverts > 0) || (b->refine && (in->numberofcorners == 10)))) { + m.jettisonnodes(); } - m.indexelements(); - } else { - if (m.tetrahedrons->items > 0l) { - m.outelements(out); + + if ((b->order == 2) && !b->convex) { + m.highorder(); } - } - if (b->nofacewritten) { if (!b->quiet) { - printf("NOT writing an .face file.\n"); + printf("\n"); } - } else { - if (b->facesout) { - if (m.tetrahedrons->items > 0l) { - m.outfaces(out); // Output all faces. - } + + if (out != (tetgenio*)NULL) { + out->firstnumber = in->firstnumber; + out->mesh_dim = in->mesh_dim; + } + + if (b->nonodewritten || b->noiterationnum) { + if (!b->quiet) { + printf("NOT writing a .node file.\n"); + } } else { - if (b->plc || b->refine) { - if (m.subfaces->items > 0l) { - m.outsubfaces(out); // Output boundary faces. + m.outnodes(out); + } + + if (b->noelewritten) { + if (!b->quiet) { + printf("NOT writing an .ele file.\n"); } - } else { + m.indexelements(); + } else { if (m.tetrahedrons->items > 0l) { - m.outhullfaces(out); // Output convex hull faces. + m.outelements(out); } - } } - } - - if (b->nofacewritten) { - if (!b->quiet) { - printf("NOT writing an .edge file.\n"); - } - } else { - if (b->edgesout) { // -e - m.outedges(out); // output all mesh edges. + if (b->nofacewritten) { + if (!b->quiet) { + printf("NOT writing an .face file.\n"); + } } else { - if (b->plc || b->refine) { - m.outsubsegments(out); // output subsegments. - } + if (b->facesout) { + if (m.tetrahedrons->items > 0l) { + m.outfaces(out); // Output all faces. + } + } else { + if (b->plc || b->refine) { + if (m.subfaces->items > 0l) { + m.outsubfaces(out); // Output boundary faces. + } + } else { + if (m.tetrahedrons->items > 0l) { + m.outhullfaces(out); // Output convex hull faces. + } + } + } } - } - - if ((b->plc || b->refine) && b->metric) { // -m - m.outmetrics(out); - } - - if (!out && b->plc && - ((b->object == tetgenbehavior::OFF) || - (b->object == tetgenbehavior::PLY) || - (b->object == tetgenbehavior::STL))) { - m.outsmesh(b->outfilename); - } - if (!out && b->meditview) { - m.outmesh2medit(b->outfilename); - } + 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 { + if (b->plc || b->refine) { + m.outsubsegments(out); // output subsegments. + } + } + } + if ((b->plc || b->refine) && b->metric) { // -m + m.outmetrics(out); + } - if (!out && b->vtkview) { - m.outmesh2vtk(b->outfilename); - } + if (!out && b->plc && + ((b->object == tetgenbehavior::OFF) || (b->object == tetgenbehavior::PLY) || + (b->object == tetgenbehavior::STL))) { + m.outsmesh(b->outfilename); + } - if (b->neighout) { - m.outneighbors(out); - } + if (!out && b->meditview) { + m.outmesh2medit(b->outfilename); + } - if (b->voroout) { - m.outvoronoi(out); - } + if (!out && b->vtkview) { + m.outmesh2vtk(b->outfilename); + } + if (b->neighout) { + m.outneighbors(out); + } - tv[11] = clock(); + if (b->voroout) { + m.outvoronoi(out); + } - if (!b->quiet) { - printf("\nOutput seconds: %g\n", ((REAL)(tv[11] - tv[10])) / cps); - printf("Total running seconds: %g\n", ((REAL)(tv[11] - tv[0])) / cps); - } + tv[11] = clock(); - if (b->docheck) { - m.checkmesh(0); - if (b->plc || b->refine) { - m.checkshells(); - m.checksegments(); + if (!b->quiet) { + 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 > 1) { - m.checkdelaunay(); + + if (b->docheck) { + m.checkmesh(0); + if (b->plc || b->refine) { + m.checkshells(); + m.checksegments(); + } + if (b->docheck > 1) { + m.checkdelaunay(); + } } - } - if (!b->quiet) { - m.statistics(); - } + if (!b->quiet) { + m.statistics(); + } } #ifndef TETLIBRARY @@ -32455,7 +32065,7 @@ void tetrahedralize(tetgenbehavior *b, tetgenio *in, tetgenio *out, // // /////////////////////////////////////////////////////////////////////////////// -int main(int argc, char *argv[]) +int main(int argc, char* argv[]) #else // with TETLIBRARY @@ -32465,51 +32075,50 @@ int main(int argc, char *argv[]) // // /////////////////////////////////////////////////////////////////////////////// -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 { - tetgenbehavior b; + tetgenbehavior b; #ifndef TETLIBRARY - tetgenio in, addin, bgmin; - - if (!b.parse_commandline(argc, argv)) { - terminatetetgen(NULL, 10); - } + tetgenio in, addin, bgmin; - // Read input files. - if (b.refine) { // -r - if (!in.load_tetmesh(b.infilename, (int) b.object)) { - terminatetetgen(NULL, 10); + if (!b.parse_commandline(argc, argv)) { + terminatetetgen(NULL, 10); } - } else { // -p - if (!in.load_plc(b.infilename, (int) b.object)) { - terminatetetgen(NULL, 10); + + // Read input files. + if (b.refine) { // -r + if (!in.load_tetmesh(b.infilename, (int)b.object)) { + terminatetetgen(NULL, 10); + } + } else { // -p + if (!in.load_plc(b.infilename, (int)b.object)) { + terminatetetgen(NULL, 10); + } + } + if (b.insertaddpoints) { // -i + // Try to read a .a.node file. + addin.load_node(b.addinfilename); + } + 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 (b.insertaddpoints) { // -i - // Try to read a .a.node file. - addin.load_node(b.addinfilename); - } - if (b.metric) { // -m - // Try to read a background mesh in files .b.node, .b.ele. - bgmin.load_tetmesh(b.bgmeshfilename, (int) b.object); - } - tetrahedralize(&b, &in, NULL, &addin, &bgmin); + tetrahedralize(&b, &in, NULL, &addin, &bgmin); - return 0; + return 0; #else // with TETLIBRARY - if (!b.parse_commandline(switches)) { - terminatetetgen(NULL, 10); - } - tetrahedralize(&b, in, out, addin, bgmin); + if (!b.parse_commandline(switches)) { + terminatetetgen(NULL, 10); + } + tetrahedralize(&b, in, out, addin, bgmin); #endif // not TETLIBRARY } @@ -32517,4 +32126,3 @@ void tetrahedralize(char *switches, tetgenio *in, tetgenio *out, //// //// //// //// //// main_cxx ///////////////////////////////////////////////////////////////// - diff --git a/tetgen.h b/tetgen.h index f1d54d3..883963c 100644 --- a/tetgen.h +++ b/tetgen.h @@ -15,21 +15,19 @@ // // /////////////////////////////////////////////////////////////////////////////// - #ifndef tetgenH #define tetgenH // To compile TetGen as a library instead of an executable program, define // the TETLIBRARY symbol. -// #define TETLIBRARY - +#define TETLIBRARY -// TetGen default uses the double precision (64 bit) for a real number. +// 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 +#define REAL double // #define REAL float // Maximum number of characters in a file name (including the null). @@ -41,34 +39,34 @@ // TetGen only uses the C standard library. +#include #include #include #include -#include #include // The types 'intptr_t' and 'uintptr_t' are signed and unsigned integer types, // respectively. They are guaranteed to be the same width as a pointer. -// They are defined in by the C99 Standard. However, Microsoft +// 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. +// file. In such case, we can define them by ourself. // Update (learned from Stack Overflow): Visual Studio 2010 and Visual C++ 2010 // Express both have stdint.h // The following piece of code was provided by Steven Johnson (MIT). Define the -// symbol _MSC_VER if you are using Microsoft Visual C++. Moreover, define +// 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 - typedef __int64 intptr_t; - typedef unsigned __int64 uintptr_t; -# else // not _WIN64 - typedef int intptr_t; - typedef unsigned int uintptr_t; -# endif +#ifdef _WIN64 +typedef __int64 intptr_t; +typedef unsigned __int64 uintptr_t; +#else // not _WIN64 +typedef int intptr_t; +typedef unsigned int uintptr_t; +#endif #else // not Visual C++ -# include +#include #endif /////////////////////////////////////////////////////////////////////////////// @@ -109,471 +107,463 @@ class tetgenio { -public: - - // A "polygon" describes a simple polygon (no holes). It is not necessarily - // convex. Each polygon contains a number of corners (points) and the same - // number of sides (edges). The points of the polygon must be given in - // either counterclockwise or clockwise order and they form a ring, so - // every two consecutive points forms an edge of the polygon. - typedef struct { - int *vertexlist; - int numberofvertices; - } polygon; - - // A "facet" describes a polygonal region possibly with holes, edges, and - // points floating in it. Each facet consists of a list of polygons and - // a list of hole points (which lie strictly inside holes). - typedef struct { - polygon *polygonlist; - int numberofpolygons; - REAL *holelist; + public: + // A "polygon" describes a simple polygon (no holes). It is not necessarily + // convex. Each polygon contains a number of corners (points) and the same + // number of sides (edges). The points of the polygon must be given in + // either counterclockwise or clockwise order and they form a ring, so + // every two consecutive points forms an edge of the polygon. + typedef struct { + int* vertexlist; + int numberofvertices; + } polygon; + + // A "facet" describes a polygonal region possibly with holes, edges, and + // points floating in it. Each facet consists of a list of polygons and + // a list of hole points (which lie strictly inside holes). + typedef struct { + polygon* polygonlist; + int numberofpolygons; + REAL* holelist; + int numberofholes; + } facet; + + // A "voroedge" is an edge of the Voronoi diagram. It corresponds to a + // Delaunay face. Each voroedge is either a line segment connecting + // two Voronoi vertices or a ray starting from a Voronoi vertex to an + // "infinite vertex". 'v1' and 'v2' are two indices pointing to the + // list of Voronoi vertices. 'v1' must be non-negative, while 'v2' may + // be -1 if it is a ray, in this case, the unit normal of this ray is + // given in 'vnormal'. + typedef struct { + int v1, v2; + REAL vnormal[3]; + } voroedge; + + // A "vorofacet" is an facet of the Voronoi diagram. It corresponds to a + // Delaunay edge. Each Voronoi facet is a convex polygon formed by a + // list of Voronoi edges, it may not be closed. 'c1' and 'c2' are two + // indices pointing into the list of Voronoi cells, i.e., the two cells + // share this facet. 'elist' is an array of indices pointing into the + // list of Voronoi edges, 'elist[0]' saves the number of Voronoi edges + // (including rays) of this facet. + typedef struct { + int c1, c2; + int* elist; + } vorofacet; + + // 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); + + // Items are numbered starting from 'firstnumber' (0 or 1), default is 0. + int firstnumber; + + // Dimension of the mesh (2 or 3), default is 3. + int mesh_dim; + + // Does the lines in .node file contain index or not, default is 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 + // z coordinate is at index [2], followed by the coordinates of the + // remaining points. Each point occupies three REALs. + // 'pointattributelist': An array of point attributes. Each point's + // attributes occupy 'numberofpointattributes' REALs. + // 'pointmtrlist': An array of metric tensors at points. Each point's + // tensor occupies 'numberofpointmtr' REALs. + // 'pointmarkerlist': An array of point markers; one integer per point. + // 'point2tetlist': An array of tetrahedra indices; one integer per point. + REAL* pointlist; + REAL* pointattributelist; + REAL* pointmtrlist; + int* pointmarkerlist; + int* point2tetlist; + pointparam* pointparamlist; + int numberofpoints; + int numberofpointattributes; + int numberofpointmtrs; + + // 'tetrahedronlist': An array of tetrahedron corners. The first + // tetrahedron's first corner is at index [0], followed by its other + // corners, followed by six nodes on the edges of the tetrahedron if the + // second order option (-o2) is applied. Each tetrahedron occupies + // 'numberofcorners' ints. The second order nodes are ouput only. + // 'tetrahedronattributelist': An array of tetrahedron attributes. Each + // tetrahedron's attributes occupy 'numberoftetrahedronattributes' REALs. + // 'tetrahedronvolumelist': An array of constraints, i.e. tetrahedron's + // volume; one REAL per element. Input only. + // 'neighborlist': An array of tetrahedron neighbors; 4 ints per element. + // 'tet2facelist': An array of tetrahedron face indices; 4 ints per element. + // 'tet2edgelist': An array of tetrahedron edge indices; 6 ints per element. + int* tetrahedronlist; + REAL* tetrahedronattributelist; + REAL* tetrahedronvolumelist; + int* neighborlist; + int* tet2facelist; + int* tet2edgelist; + int numberoftetrahedra; + int numberofcorners; + int numberoftetrahedronattributes; + + // 'facetlist': An array of facets. Each entry is a structure of facet. + // 'facetmarkerlist': An array of facet markers; one int per facet. + facet* facetlist; + int* facetmarkerlist; + int numberoffacets; + + // 'holelist': An array of holes (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; - } facet; - - // A "voroedge" is an edge of the Voronoi diagram. It corresponds to a - // Delaunay face. Each voroedge is either a line segment connecting - // two Voronoi vertices or a ray starting from a Voronoi vertex to an - // "infinite vertex". 'v1' and 'v2' are two indices pointing to the - // list of Voronoi vertices. 'v1' must be non-negative, while 'v2' may - // be -1 if it is a ray, in this case, the unit normal of this ray is - // given in 'vnormal'. - typedef struct { - int v1, v2; - REAL vnormal[3]; - } voroedge; - - // A "vorofacet" is an facet of the Voronoi diagram. It corresponds to a - // Delaunay edge. Each Voronoi facet is a convex polygon formed by a - // list of Voronoi edges, it may not be closed. 'c1' and 'c2' are two - // indices pointing into the list of Voronoi cells, i.e., the two cells - // share this facet. 'elist' is an array of indices pointing into the - // list of Voronoi edges, 'elist[0]' saves the number of Voronoi edges - // (including rays) of this facet. - typedef struct { - int c1, c2; - int *elist; - } vorofacet; - - - // 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); - - // Items are numbered starting from 'firstnumber' (0 or 1), default is 0. - int firstnumber; - - // Dimension of the mesh (2 or 3), default is 3. - int mesh_dim; - - // Does the lines in .node file contain index or not, default is 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 - // z coordinate is at index [2], followed by the coordinates of the - // remaining points. Each point occupies three REALs. - // 'pointattributelist': An array of point attributes. Each point's - // attributes occupy 'numberofpointattributes' REALs. - // 'pointmtrlist': An array of metric tensors at points. Each point's - // tensor occupies 'numberofpointmtr' REALs. - // 'pointmarkerlist': An array of point markers; one integer per point. - // 'point2tetlist': An array of tetrahedra indices; one integer per point. - REAL *pointlist; - REAL *pointattributelist; - REAL *pointmtrlist; - int *pointmarkerlist; - int *point2tetlist; - pointparam *pointparamlist; - int numberofpoints; - int numberofpointattributes; - int numberofpointmtrs; - - // 'tetrahedronlist': An array of tetrahedron corners. The first - // tetrahedron's first corner is at index [0], followed by its other - // corners, followed by six nodes on the edges of the tetrahedron if the - // second order option (-o2) is applied. Each tetrahedron occupies - // 'numberofcorners' ints. The second order nodes are ouput only. - // 'tetrahedronattributelist': An array of tetrahedron attributes. Each - // tetrahedron's attributes occupy 'numberoftetrahedronattributes' REALs. - // 'tetrahedronvolumelist': An array of constraints, i.e. tetrahedron's - // volume; one REAL per element. Input only. - // 'neighborlist': An array of tetrahedron neighbors; 4 ints per element. - // 'tet2facelist': An array of tetrahedron face indices; 4 ints per element. - // 'tet2edgelist': An array of tetrahedron edge indices; 6 ints per element. - int *tetrahedronlist; - REAL *tetrahedronattributelist; - REAL *tetrahedronvolumelist; - int *neighborlist; - int *tet2facelist; - int *tet2edgelist; - int numberoftetrahedra; - int numberofcorners; - int numberoftetrahedronattributes; - - // 'facetlist': An array of facets. Each entry is a structure of facet. - // 'facetmarkerlist': An array of facet markers; one int per facet. - facet *facetlist; - int *facetmarkerlist; - int numberoffacets; - - // 'holelist': An array of holes (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 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). - REAL *regionlist; - int numberofregions; - - // 'facetconstraintlist': An array of facet constraints. Each constraint - // specifies a maximum area bound on the subfaces of that facet. The - // first facet constraint is given by a facet marker at index [0] and its - // maximum area bound at index [1], followed by the remaining facet con- - // straints. Two REALs per facet constraint. Note: the facet marker is - // actually an integer. - REAL *facetconstraintlist; - int numberoffacetconstraints; - - // 'segmentconstraintlist': An array of segment constraints. Each constraint - // specifies a maximum length bound on the subsegments of that segment. - // The first constraint is given by the two endpoints of the segment at - // index [0] and [1], and the maximum length bound at index [2], followed - // by the remaining segment constraints. Three REALs per constraint. - // Note the segment endpoints are actually integers. - REAL *segmentconstraintlist; - int numberofsegmentconstraints; - - - // 'trifacelist': An array of face (triangle) corners. The first face's - // three corners are at indices [0], [1] and [2], followed by the remaining - // faces. Three ints per face. - // 'trifacemarkerlist': An array of face markers; one int per face. - // 'o2facelist': An array of second order nodes (on the edges) of the face. - // It is output only if the second order option (-o2) is applied. The - // first face's three second order nodes are at [0], [1], and [2], - // followed by the remaining faces. Three ints per face. - // 'face2tetlist': An array of tetrahedra indices; 2 ints per face. - // 'face2edgelist': An array of edge indices; 3 ints per face. - int *trifacelist; - int *trifacemarkerlist; - int *o2facelist; - int *face2tetlist; - int *face2edgelist; - int numberoftrifaces; - - // 'edgelist': An array of edge endpoints. The first edge's endpoints - // are at indices [0] and [1], followed by the remaining edges. - // Two ints per edge. - // 'edgemarkerlist': An array of edge markers; one int per edge. - // 'o2edgelist': An array of midpoints of edges. It is output only if the - // second order option (-o2) is applied. One int per edge. - // 'edge2tetlist': An array of tetrahedra indices. One int per edge. - int *edgelist; - int *edgemarkerlist; - int *o2edgelist; - int *edge2tetlist; - int numberofedges; - - // 'vpointlist': An array of Voronoi vertex coordinates (like pointlist). - // 'vedgelist': An array of Voronoi edges. Each entry is a 'voroedge'. - // 'vfacetlist': An array of Voronoi facets. Each entry is a 'vorofacet'. - // 'vcelllist': An array of Voronoi cells. Each entry is an array of - // indices pointing into 'vfacetlist'. The 0th entry is used to store - // the length of this array. - REAL *vpointlist; - voroedge *vedgelist; - vorofacet *vfacetlist; - int **vcelllist; - int numberofvpoints; - int numberofvedges; - int numberofvfacets; - int numberofvcells; - - - // 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, 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_pbc(char*); - bool load_poly(char*); - bool load_off(char*); - bool load_ply(char*); - bool load_stl(char*); - bool load_vtk(char*); - bool load_medit(char*, int); - bool load_neumesh(char*, int); - bool load_plc(char*, int); - 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); - char *findnextfield(char* string); - 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; - mesh_dim = 3; - useindex = 1; - - pointlist = (REAL *) NULL; - pointattributelist = (REAL *) NULL; - pointmtrlist = (REAL *) NULL; - pointmarkerlist = (int *) NULL; - point2tetlist = (int *) NULL; - pointparamlist = (pointparam *) NULL; - numberofpoints = 0; - numberofpointattributes = 0; - numberofpointmtrs = 0; - - tetrahedronlist = (int *) NULL; - tetrahedronattributelist = (REAL *) NULL; - tetrahedronvolumelist = (REAL *) NULL; - neighborlist = (int *) NULL; - tet2facelist = (int *) NULL; - tet2edgelist = (int *) NULL; - numberoftetrahedra = 0; - numberofcorners = 4; - numberoftetrahedronattributes = 0; - - trifacelist = (int *) NULL; - trifacemarkerlist = (int *) NULL; - o2facelist = (int *) NULL; - face2tetlist = (int *) NULL; - face2edgelist = (int *) NULL; - numberoftrifaces = 0; - - edgelist = (int *) NULL; - edgemarkerlist = (int *) NULL; - o2edgelist = (int *) NULL; - edge2tetlist = (int *) NULL; - numberofedges = 0; - - facetlist = (facet *) NULL; - facetmarkerlist = (int *) NULL; - numberoffacets = 0; - - holelist = (REAL *) NULL; - numberofholes = 0; - - regionlist = (REAL *) NULL; - numberofregions = 0; - - facetconstraintlist = (REAL *) NULL; - numberoffacetconstraints = 0; - segmentconstraintlist = (REAL *) NULL; - numberofsegmentconstraints = 0; - - - vpointlist = (REAL *) NULL; - vedgelist = (voroedge *) NULL; - vfacetlist = (vorofacet *) NULL; - vcelllist = (int **) NULL; - numberofvpoints = 0; - numberofvedges = 0; - numberofvfacets = 0; - numberofvcells = 0; - - - tetunsuitable = NULL; - - geomhandle = NULL; - getvertexparamonedge = NULL; - getsteineronedge = NULL; - getvertexparamonface = NULL; - getedgesteinerparamonface = NULL; - getsteineronface = NULL; - } - - // Free the memory allocated in 'tetgenio'. Note that it assumes that the - // memory was allocated by the "new" operator (C++). - void deinitialize() - { - int i, j; - - if (pointlist != (REAL *) NULL) { - delete [] pointlist; - } - if (pointattributelist != (REAL *) NULL) { - delete [] pointattributelist; - } - if (pointmtrlist != (REAL *) NULL) { - delete [] pointmtrlist; - } - if (pointmarkerlist != (int *) NULL) { - delete [] pointmarkerlist; - } - if (point2tetlist != (int *) NULL) { - delete [] point2tetlist; - } - if (pointparamlist != (pointparam *) NULL) { - delete [] pointparamlist; - } - if (tetrahedronlist != (int *) NULL) { - delete [] tetrahedronlist; - } - if (tetrahedronattributelist != (REAL *) NULL) { - delete [] tetrahedronattributelist; - } - if (tetrahedronvolumelist != (REAL *) NULL) { - delete [] tetrahedronvolumelist; - } - if (neighborlist != (int *) NULL) { - delete [] neighborlist; - } - if (tet2facelist != (int *) NULL) { - delete [] tet2facelist; - } - if (tet2edgelist != (int *) NULL) { - delete [] tet2edgelist; - } - - if (trifacelist != (int *) NULL) { - delete [] trifacelist; - } - if (trifacemarkerlist != (int *) NULL) { - delete [] trifacemarkerlist; - } - if (o2facelist != (int *) NULL) { - delete [] o2facelist; - } - if (face2tetlist != (int *) NULL) { - delete [] face2tetlist; - } - if (face2edgelist != (int *) NULL) { - delete [] face2edgelist; + // '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). + REAL* regionlist; + int numberofregions; + + // 'facetconstraintlist': An array of facet constraints. Each constraint + // specifies a maximum area bound on the subfaces of that facet. The + // first facet constraint is given by a facet marker at index [0] and its + // maximum area bound at index [1], followed by the remaining facet con- + // straints. Two REALs per facet constraint. Note: the facet marker is + // actually an integer. + REAL* facetconstraintlist; + int numberoffacetconstraints; + + // 'segmentconstraintlist': An array of segment constraints. Each constraint + // specifies a maximum length bound on the subsegments of that segment. + // The first constraint is given by the two endpoints of the segment at + // index [0] and [1], and the maximum length bound at index [2], followed + // by the remaining segment constraints. Three REALs per constraint. + // Note the segment endpoints are actually integers. + REAL* segmentconstraintlist; + int numberofsegmentconstraints; + + // 'trifacelist': An array of face (triangle) corners. The first face's + // three corners are at indices [0], [1] and [2], followed by the remaining + // faces. Three ints per face. + // 'trifacemarkerlist': An array of face markers; one int per face. + // 'o2facelist': An array of second order nodes (on the edges) of the face. + // It is output only if the second order option (-o2) is applied. The + // first face's three second order nodes are at [0], [1], and [2], + // followed by the remaining faces. Three ints per face. + // 'face2tetlist': An array of tetrahedra indices; 2 ints per face. + // 'face2edgelist': An array of edge indices; 3 ints per face. + int* trifacelist; + int* trifacemarkerlist; + int* o2facelist; + int* face2tetlist; + int* face2edgelist; + int numberoftrifaces; + + // 'edgelist': An array of edge endpoints. The first edge's endpoints + // are at indices [0] and [1], followed by the remaining edges. + // Two ints per edge. + // 'edgemarkerlist': An array of edge markers; one int per edge. + // 'o2edgelist': An array of midpoints of edges. It is output only if the + // second order option (-o2) is applied. One int per edge. + // 'edge2tetlist': An array of tetrahedra indices. One int per edge. + int* edgelist; + int* edgemarkerlist; + int* o2edgelist; + int* edge2tetlist; + int numberofedges; + + // 'vpointlist': An array of Voronoi vertex coordinates (like pointlist). + // 'vedgelist': An array of Voronoi edges. Each entry is a 'voroedge'. + // 'vfacetlist': An array of Voronoi facets. Each entry is a 'vorofacet'. + // 'vcelllist': An array of Voronoi cells. Each entry is an array of + // indices pointing into 'vfacetlist'. The 0th entry is used to store + // the length of this array. + REAL* vpointlist; + voroedge* vedgelist; + vorofacet* vfacetlist; + int** vcelllist; + int numberofvpoints; + int numberofvedges; + int numberofvfacets; + int numberofvcells; + + // 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, 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_pbc(char*); + bool load_poly(char*); + bool load_off(char*); + bool load_ply(char*); + bool load_stl(char*); + bool load_vtk(char*); + bool load_medit(char*, int); + bool load_neumesh(char*, int); + bool load_plc(char*, int); + 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); + char* findnextfield(char* string); + char* readnumberline(char* string, FILE* infile, char* infilename); + char* findnextnumber(char* string); + + static void init(polygon* p) { + p->vertexlist = (int*)NULL; + p->numberofvertices = 0; } - if (edgelist != (int *) NULL) { - delete [] edgelist; - } - if (edgemarkerlist != (int *) NULL) { - delete [] edgemarkerlist; + static void init(facet* f) { + f->polygonlist = (polygon*)NULL; + f->numberofpolygons = 0; + f->holelist = (REAL*)NULL; + f->numberofholes = 0; } - if (o2edgelist != (int *) NULL) { - delete [] o2edgelist; - } - if (edge2tetlist != (int *) NULL) { - delete [] edge2tetlist; + + // Initialize routine. + void initialize() { + firstnumber = 0; + mesh_dim = 3; + useindex = 1; + + pointlist = (REAL*)NULL; + pointattributelist = (REAL*)NULL; + pointmtrlist = (REAL*)NULL; + pointmarkerlist = (int*)NULL; + point2tetlist = (int*)NULL; + pointparamlist = (pointparam*)NULL; + numberofpoints = 0; + numberofpointattributes = 0; + numberofpointmtrs = 0; + + tetrahedronlist = (int*)NULL; + tetrahedronattributelist = (REAL*)NULL; + tetrahedronvolumelist = (REAL*)NULL; + neighborlist = (int*)NULL; + tet2facelist = (int*)NULL; + tet2edgelist = (int*)NULL; + numberoftetrahedra = 0; + numberofcorners = 4; + numberoftetrahedronattributes = 0; + + trifacelist = (int*)NULL; + trifacemarkerlist = (int*)NULL; + o2facelist = (int*)NULL; + face2tetlist = (int*)NULL; + face2edgelist = (int*)NULL; + numberoftrifaces = 0; + + edgelist = (int*)NULL; + edgemarkerlist = (int*)NULL; + o2edgelist = (int*)NULL; + edge2tetlist = (int*)NULL; + numberofedges = 0; + + facetlist = (facet*)NULL; + facetmarkerlist = (int*)NULL; + numberoffacets = 0; + + holelist = (REAL*)NULL; + numberofholes = 0; + + regionlist = (REAL*)NULL; + numberofregions = 0; + + facetconstraintlist = (REAL*)NULL; + numberoffacetconstraints = 0; + segmentconstraintlist = (REAL*)NULL; + numberofsegmentconstraints = 0; + + vpointlist = (REAL*)NULL; + vedgelist = (voroedge*)NULL; + vfacetlist = (vorofacet*)NULL; + vcelllist = (int**)NULL; + numberofvpoints = 0; + numberofvedges = 0; + numberofvfacets = 0; + numberofvcells = 0; + + tetunsuitable = NULL; + + geomhandle = NULL; + getvertexparamonedge = NULL; + getsteineronedge = NULL; + getvertexparamonface = NULL; + getedgesteinerparamonface = NULL; + getsteineronface = NULL; } - if (facetlist != (facet *) NULL) { - facet *f; - polygon *p; - for (i = 0; i < numberoffacets; i++) { - f = &facetlist[i]; - for (j = 0; j < f->numberofpolygons; j++) { - p = &f->polygonlist[j]; - delete [] p->vertexlist; + // Free the memory allocated in 'tetgenio'. Note that it assumes that the + // memory was allocated by the "new" operator (C++). + void deinitialize() { + int i, j; + + if (pointlist != (REAL*)NULL) { + delete[] pointlist; } - delete [] f->polygonlist; - if (f->holelist != (REAL *) NULL) { - delete [] f->holelist; + if (pointattributelist != (REAL*)NULL) { + delete[] pointattributelist; + } + if (pointmtrlist != (REAL*)NULL) { + delete[] pointmtrlist; + } + if (pointmarkerlist != (int*)NULL) { + delete[] pointmarkerlist; + } + if (point2tetlist != (int*)NULL) { + delete[] point2tetlist; + } + if (pointparamlist != (pointparam*)NULL) { + delete[] pointparamlist; } - } - delete [] facetlist; - } - if (facetmarkerlist != (int *) NULL) { - delete [] facetmarkerlist; - } - if (holelist != (REAL *) NULL) { - delete [] holelist; - } - if (regionlist != (REAL *) NULL) { - delete [] regionlist; - } - if (facetconstraintlist != (REAL *) NULL) { - delete [] facetconstraintlist; - } - if (segmentconstraintlist != (REAL *) NULL) { - delete [] segmentconstraintlist; - } - if (vpointlist != (REAL *) NULL) { - delete [] vpointlist; - } - if (vedgelist != (voroedge *) NULL) { - delete [] vedgelist; - } - if (vfacetlist != (vorofacet *) NULL) { - for (i = 0; i < numberofvfacets; i++) { - delete [] vfacetlist[i].elist; - } - delete [] vfacetlist; - } - if (vcelllist != (int **) NULL) { - for (i = 0; i < numberofvcells; i++) { - delete [] vcelllist[i]; - } - delete [] vcelllist; + if (tetrahedronlist != (int*)NULL) { + delete[] tetrahedronlist; + } + if (tetrahedronattributelist != (REAL*)NULL) { + delete[] tetrahedronattributelist; + } + if (tetrahedronvolumelist != (REAL*)NULL) { + delete[] tetrahedronvolumelist; + } + if (neighborlist != (int*)NULL) { + delete[] neighborlist; + } + if (tet2facelist != (int*)NULL) { + delete[] tet2facelist; + } + if (tet2edgelist != (int*)NULL) { + delete[] tet2edgelist; + } + + if (trifacelist != (int*)NULL) { + delete[] trifacelist; + } + if (trifacemarkerlist != (int*)NULL) { + delete[] trifacemarkerlist; + } + if (o2facelist != (int*)NULL) { + delete[] o2facelist; + } + if (face2tetlist != (int*)NULL) { + delete[] face2tetlist; + } + if (face2edgelist != (int*)NULL) { + delete[] face2edgelist; + } + + if (edgelist != (int*)NULL) { + delete[] edgelist; + } + if (edgemarkerlist != (int*)NULL) { + delete[] edgemarkerlist; + } + if (o2edgelist != (int*)NULL) { + delete[] o2edgelist; + } + if (edge2tetlist != (int*)NULL) { + delete[] edge2tetlist; + } + + if (facetlist != (facet*)NULL) { + facet* f; + polygon* p; + for (i = 0; i < numberoffacets; i++) { + f = &facetlist[i]; + for (j = 0; j < f->numberofpolygons; j++) { + p = &f->polygonlist[j]; + delete[] p->vertexlist; + } + delete[] f->polygonlist; + if (f->holelist != (REAL*)NULL) { + delete[] f->holelist; + } + } + delete[] facetlist; + } + if (facetmarkerlist != (int*)NULL) { + delete[] facetmarkerlist; + } + + if (holelist != (REAL*)NULL) { + delete[] holelist; + } + if (regionlist != (REAL*)NULL) { + delete[] regionlist; + } + if (facetconstraintlist != (REAL*)NULL) { + delete[] facetconstraintlist; + } + if (segmentconstraintlist != (REAL*)NULL) { + delete[] segmentconstraintlist; + } + if (vpointlist != (REAL*)NULL) { + delete[] vpointlist; + } + if (vedgelist != (voroedge*)NULL) { + delete[] vedgelist; + } + if (vfacetlist != (vorofacet*)NULL) { + for (i = 0; i < numberofvfacets; i++) { + delete[] vfacetlist[i].elist; + } + delete[] vfacetlist; + } + if (vcelllist != (int**)NULL) { + for (i = 0; i < numberofvcells; i++) { + delete[] vcelllist[i]; + } + delete[] vcelllist; + } } - } - // Constructor & destructor. - tetgenio() {initialize();} - ~tetgenio() {deinitialize();} + // Constructor & destructor. + tetgenio() { initialize(); } + ~tetgenio() { deinitialize(); } }; // class tetgenio @@ -596,211 +586,205 @@ public: class tetgenbehavior { -public: - - // Switches of TetGen. - int plc; // '-p', 0. - int psc; // '-s', 0. - int refine; // '-r', 0. - int quality; // '-q', 0. - int nobisect; // '-Y', 0. - int 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 cdtrefine; // '-D', 0. - int use_equatorial_lens; // '-Dl', 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 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_nomerge; // '-Y', 1. - int supsteiner_level; // '-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 reversetetori; // '-o/', 0. - 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_separate_ang_tol; // '-p', 179.9. - REAL facet_overlap_ang_tol; // '-p/', 0.1. - REAL facet_small_ang_tol; // '-p//', 15.0. - 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 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]; - - // Read an additional tetrahedral mesh and treat it as holes [2018-07-30]. - int hole_mesh; // '-H', 0. - char hole_mesh_filename[1024]; - int apply_flow_bc; // '-K', 0. - - // 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, 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 command line switch - // (-p or -r) implies the object. - enum objecttype {NODES, POLY, OFF, PLY, STL, MEDIT, VTK, MESH, NEU_MESH} object; - - - void syntax(); - void usage(); - - // Command line parse routine. - bool parse_commandline(int argc, char **argv); - bool parse_commandline(char *switches) { - return parse_commandline(0, &switches); - } - - // Initialize all variables. - tetgenbehavior() - { - plc = 0; - psc = 0; - refine = 0; - quality = 0; - nobisect = 0; - coarsen = 0; - metric = 0; - weighted = 0; - brio_hilbert = 1; - incrflip = 0; - flipinsert = 0; - varvolume = 0; - fixedvolume = 0; - noexact = 0; - nostaticfilter = 0; - insertaddpoints = 0; - regionattrib = 0; - cdtrefine = 0; - use_equatorial_lens = 0; // -Dl - diagnose = 0; - convex = 0; - zeroindex = 0; - facesout = 0; - edgesout = 0; - neighout = 0; - voroout = 0; - meditview = 0; - vtkview = 0; - nobound = 0; - nonodewritten = 0; - noelewritten = 0; - nofacewritten = 0; - noiterationnum = 0; - nomergefacet = 0; - nomergevertex = 0; - nojettison = 0; - docheck = 0; - quiet = 0; - verbose = 0; - - vertexperblock = 4092; - tetrahedraperblock = 8188; - shellfaceperblock = 4092; - nobisect_nomerge = 1; - supsteiner_level = 2; - addsteiner_algo = 1; - coarsen_param = 0; - weighted_param = 0; - fliplinklevel = -1; - flipstarsize = -1; - fliplinklevelinc = 1; - reflevel = 3; - optscheme = 7; - optlevel = 2; - delmaxfliplevel = 1; - order = 1; - reversetetori = 0; - steinerleft = -1; - no_sort = 0; - hilbert_order = 52; //-1; - hilbert_limit = 8; - brio_threshold = 64; - brio_ratio = 0.125; - facet_separate_ang_tol = 179.9; - facet_overlap_ang_tol = 0.1; - facet_small_ang_tol = 15.0; - maxvolume = -1.0; - minratio = 2.0; - mindihedral = 0.0; - optmaxdihedral = 165.00; - optminsmtdihed = 179.00; - optminslidihed = 179.00; - epsilon = 1.0e-8; - coarsen_percent = 1.0; - object = NODES; - - commandline[0] = '\0'; - infilename[0] = '\0'; - outfilename[0] = '\0'; - addinfilename[0] = '\0'; - bgmeshfilename[0] = '\0'; - - hole_mesh = 0; - hole_mesh_filename[0] = '\0'; - apply_flow_bc = 0; - - } + 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 cdtrefine; // '-D', 0. + int use_equatorial_lens; // '-Dl', 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 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_nomerge; // '-Y', 1. + int supsteiner_level; // '-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 reversetetori; // '-o/', 0. + 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_separate_ang_tol; // '-p', 179.9. + REAL facet_overlap_ang_tol; // '-p/', 0.1. + REAL facet_small_ang_tol; // '-p//', 15.0. + 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 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]; + + // Read an additional tetrahedral mesh and treat it as holes [2018-07-30]. + int hole_mesh; // '-H', 0. + char hole_mesh_filename[1024]; + int apply_flow_bc; // '-K', 0. + + // 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, 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 command line switch + // (-p or -r) implies the object. + enum objecttype { NODES, POLY, OFF, PLY, STL, MEDIT, VTK, MESH, NEU_MESH } object; + + void syntax(); + void usage(); + + // Command line parse routine. + bool parse_commandline(int argc, char** argv); + bool parse_commandline(char* switches) { return parse_commandline(0, &switches); } + + // Initialize all variables. + tetgenbehavior() { + plc = 0; + psc = 0; + refine = 0; + quality = 0; + nobisect = 0; + coarsen = 0; + metric = 0; + weighted = 0; + brio_hilbert = 1; + incrflip = 0; + flipinsert = 0; + varvolume = 0; + fixedvolume = 0; + noexact = 0; + nostaticfilter = 0; + insertaddpoints = 0; + regionattrib = 0; + cdtrefine = 0; + use_equatorial_lens = 0; // -Dl + diagnose = 0; + convex = 0; + zeroindex = 0; + facesout = 0; + edgesout = 0; + neighout = 0; + voroout = 0; + meditview = 0; + vtkview = 0; + nobound = 0; + nonodewritten = 0; + noelewritten = 0; + nofacewritten = 0; + noiterationnum = 0; + nomergefacet = 0; + nomergevertex = 0; + nojettison = 0; + docheck = 0; + quiet = 0; + verbose = 0; + + vertexperblock = 4092; + tetrahedraperblock = 8188; + shellfaceperblock = 4092; + nobisect_nomerge = 1; + supsteiner_level = 2; + addsteiner_algo = 1; + coarsen_param = 0; + weighted_param = 0; + fliplinklevel = -1; + flipstarsize = -1; + fliplinklevelinc = 1; + reflevel = 3; + optscheme = 7; + optlevel = 2; + delmaxfliplevel = 1; + order = 1; + reversetetori = 0; + steinerleft = -1; + no_sort = 0; + hilbert_order = 52; //-1; + hilbert_limit = 8; + brio_threshold = 64; + brio_ratio = 0.125; + facet_separate_ang_tol = 179.9; + facet_overlap_ang_tol = 0.1; + facet_small_ang_tol = 15.0; + maxvolume = -1.0; + minratio = 2.0; + mindihedral = 0.0; + optmaxdihedral = 165.00; + optminsmtdihed = 179.00; + optminslidihed = 179.00; + epsilon = 1.0e-8; + coarsen_percent = 1.0; + object = NODES; + + commandline[0] = '\0'; + infilename[0] = '\0'; + outfilename[0] = '\0'; + addinfilename[0] = '\0'; + bgmeshfilename[0] = '\0'; + + hole_mesh = 0; + hole_mesh_filename[0] = '\0'; + apply_flow_bc = 0; + } }; // class tetgenbehavior @@ -832,10 +816,10 @@ public: /////////////////////////////////////////////////////////////////////////////// void exactinit(int, int, int, REAL, REAL, REAL); -REAL orient3d(REAL *pa, REAL *pb, REAL *pc, REAL *pd); -REAL insphere(REAL *pa, REAL *pb, REAL *pc, REAL *pd, REAL *pe); -REAL orient4d(REAL *pa, REAL *pb, REAL *pc, REAL *pd, REAL *pe, - REAL ah, REAL bh, REAL ch, REAL dh, REAL eh); +REAL 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); /////////////////////////////////////////////////////////////////////////////// // // @@ -847,1441 +831,1444 @@ REAL orient4d(REAL *pa, REAL *pb, REAL *pc, REAL *pd, REAL *pe, class tetgenmesh { -public: - -/////////////////////////////////////////////////////////////////////////////// -// // -// 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. // -// // -/////////////////////////////////////////////////////////////////////////////// - - // The tetrahedron data structure. It includes the following fields: - // - a list of four adjoining tetrahedra; - // - a list of four vertices; - // - a pointer to a list of four subfaces (optional, for -p switch); - // - a pointer to a list of six segments (optional, for -p switch); - // - a list of user-defined floating-point attributes (optional); - // - 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 subface data structure. It includes the following fields: - // - a list of three adjoining subfaces; - // - a list of three vertices; - // - 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, flags, etc. - - typedef REAL **shellface; - - // The point data structure. It includes the following fields: - // - x, y and z coordinates; - // - a list of user-defined point attributes (optional); - // - 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; - -/////////////////////////////////////////////////////////////////////////////// -// // -// 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. // -// // -// 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. // -// // -// 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: // -// // -// | 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) // -// // -// 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. // -// // -// 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 ver; // Range from 0 to 11. - triface() : tet(0), ver(0) {} - triface& operator=(const triface& t) { - tet = t.tet; ver = t.ver; - return *this; - } - }; - - class face { - public: - shellface *sh; - int shver; // Range from 0 to 5. - face() : sh(0), shver(0) {} - face& operator=(const face& s) { - sh = s.sh; shver = s.shver; - return *this; - } - }; - -/////////////////////////////////////////////////////////////////////////////// -// // -// Arraypool // -// // -// A dynamic linear array. (It is written by J. Shewchuk) // -// // -// Each arraypool contains an array of pointers to a number of blocks. Each // -// block contains the same fixed number of objects. Each index of the array // -// 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. // -// // -// '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 arraypool { - public: - - int objectbytes; - int objectsperblock; - int log2objectsperblock; - int objectsperblockmark; - int toparraylen; - char **toparray; - long objects; - unsigned long totalmemory; - - void restart(); - void poolinit(int sizeofobject, int log2objperblk); - char* getblock(int objectindex); - void* lookup(int objectindex); - int newindex(void **newptr); - - arraypool(int sizeofobject, int log2objperblk); - ~arraypool(); - }; - -// fastlookup() -- A fast, unsafe operation. Return the pointer to the object -// with a given index. Note: The object's block must have been allocated, -// i.e., by the function newindex(). - -#define fastlookup(pool, index) \ - (void *) ((pool)->toparray[(index) >> (pool)->log2objectsperblock] + \ + /////////////////////////////////////////////////////////////////////////////// + // // + // 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. // + // // + /////////////////////////////////////////////////////////////////////////////// + + // The tetrahedron data structure. It includes the following fields: + // - a list of four adjoining tetrahedra; + // - a list of four vertices; + // - a pointer to a list of four subfaces (optional, for -p switch); + // - a pointer to a list of six segments (optional, for -p switch); + // - a list of user-defined floating-point attributes (optional); + // - 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 subface data structure. It includes the following fields: + // - a list of three adjoining subfaces; + // - a list of three vertices; + // - 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, flags, etc. + + typedef REAL** shellface; + + // The point data structure. It includes the following fields: + // - x, y and z coordinates; + // - a list of user-defined point attributes (optional); + // - 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; + + /////////////////////////////////////////////////////////////////////////////// + // // + // 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. // + // // + // 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. // + // // + // 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: // + // // + // | 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) // + // // + // 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. // + // // + // 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 ver; // Range from 0 to 11. + triface() : tet(0), ver(0) {} + triface& operator=(const triface& t) { + tet = t.tet; + ver = t.ver; + return *this; + } + }; + + class face { + public: + shellface* sh; + int shver; // Range from 0 to 5. + face() : sh(0), shver(0) {} + face& operator=(const face& s) { + sh = s.sh; + shver = s.shver; + return *this; + } + }; + + /////////////////////////////////////////////////////////////////////////////// + // // + // Arraypool // + // // + // A dynamic linear array. (It is written by J. Shewchuk) // + // // + // Each arraypool contains an array of pointers to a number of blocks. Each // + // block contains the same fixed number of objects. Each index of the array // + // 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. // + // // + // '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 arraypool { + + public: + int objectbytes; + int objectsperblock; + int log2objectsperblock; + int objectsperblockmark; + int toparraylen; + char** toparray; + long objects; + unsigned long totalmemory; + + void restart(); + void poolinit(int sizeofobject, int log2objperblk); + char* getblock(int objectindex); + void* lookup(int objectindex); + int newindex(void** newptr); + + arraypool(int sizeofobject, int log2objperblk); + ~arraypool(); + }; + + // fastlookup() -- A fast, unsafe operation. Return the pointer to the object + // with a given index. Note: The object's block must have been allocated, + // i.e., by the function newindex(). + +#define fastlookup(pool, index) \ + (void*)((pool)->toparray[(index) >> (pool)->log2objectsperblock] + \ ((index) & (pool)->objectsperblockmark) * (pool)->objectbytes) -/////////////////////////////////////////////////////////////////////////////// -// // -// Memorypool // -// // -// A structure for memory allocation. (It is written by J. Shewchuk) // -// // -// firstblock is the first block of items. nowblock is the block from which // -// items are currently being allocated. nextitem points to the next slab // -// of free memory for an item. deaditemstack is the head of a linked list // -// (stack) of deallocated items that can be recycled. unallocateditems is // -// the number of items that remain to be allocated from nowblock. // -// // -// Traversal is the process of walking through the entire list of items, and // -// is separate from allocation. Note that a traversal will visit items on // -// the "deaditemstack" stack as well as live items. pathblock points to // -// the block currently being traversed. pathitem points to the next item // -// to be traversed. pathitemsleft is the number of items that remain to // -// be traversed in pathblock. // -// // -/////////////////////////////////////////////////////////////////////////////// - - class memorypool { - - public: - - void **firstblock, **nowblock; - void *nextitem; - void *deaditemstack; - void **pathblock; - void *pathitem; - int alignbytes; - int itembytes, itemwords; - int itemsperblock; - long items, maxitems; - int unallocateditems; - int pathitemsleft; - - memorypool(); - memorypool(int, int, int, int); - ~memorypool(); - - void poolinit(int, int, int, int); - void restart(); - void *alloc(); - void dealloc(void*); - void traversalinit(); - void *traverse(); - }; - -/////////////////////////////////////////////////////////////////////////////// -// // -// badface // -// // -// Despite of its name, a 'badface' can be used to represent one of the // -// following objects: // -// - a 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). // -// // -/////////////////////////////////////////////////////////////////////////////// - - 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) {} - }; - -/////////////////////////////////////////////////////////////////////////////// -// // -// 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; - } - }; - -/////////////////////////////////////////////////////////////////////////////// -// // -// optparameters // -// // -// Optimization options and parameters. // -// // -/////////////////////////////////////////////////////////////////////////////// - - class optparameters { - - public: - - // The one of goals of optimization. - int max_min_volume; // Maximize the minimum volume. - int min_max_aspectratio; // Minimize the maximum 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; - min_max_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}; - - // 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 pointinsradiusindex; // Index to find the insertion radius 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 minedgelength; // = longest * b->epsion. - 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 tetrahedron encode(triface& t); - inline tetrahedron encode2(tetrahedron* ptr, int ver); - inline void decode(tetrahedron ptr, triface& t); - inline void bond(triface& t1, triface& t2); - inline void dissolve(triface& t); - inline void esym(triface& t1, triface& t2); - inline void esymself(triface& t); - inline void enext(triface& t1, triface& t2); - inline void enextself(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 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); - inline bool infected(triface& t); - inline void marktest(triface& t); - inline void unmarktest(triface& t); - inline bool marktested(triface& t); - inline void markface(triface& t); - inline void unmarkface(triface& t); - inline bool facemarked(triface& t); - inline void markedge(triface& t); - inline void unmarkedge(triface& t); - inline bool edgemarked(triface& t); - 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); - inline void sbond1(face& s1, face& s2); - inline void sdissolve(face& s); - inline point sorg(face& s); - inline point sdest(face& s); - inline point sapex(face& s); - inline void setsorg(face& s, point pointptr); - inline void setsdest(face& s, point pointptr); - inline void setsapex(face& s, point pointptr); - inline void sesym(face& s1, face& s2); - inline void sesymself(face& s); - inline void senext(face& s1, face& s2); - inline void senextself(face& s); - inline void senext2(face& s1, face& s2); - inline void senext2self(face& s); - inline REAL areabound(face& s); - inline void setareabound(face& s, REAL value); - inline int shellmark(face& s); - inline void setshellmark(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 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 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 point point2ppt(point pt); - inline void setpoint2ppt(point pt, point value); - inline tetrahedron point2bgmtet(point pt); - inline void setpoint2bgmtet(point pt, tetrahedron value); - inline void setpointinsradius(point pt, REAL value); - inline REAL getpointinsradius(point pt); - inline bool issteinerpoint(point pt); - - // Advanced primitives. - 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 // -// // -/////////////////////////////////////////////////////////////////////////////// - - void tetrahedrondealloc(tetrahedron*); - tetrahedron *tetrahedrontraverse(); - tetrahedron *alltetrahedrontraverse(); - void shellfacedealloc(memorypool*, shellface*); - shellface *shellfacetraverse(memorypool*); - void pointdealloc(point); - point pointtraverse(); - - void makeindex2pointmap(point*&); - void makepoint2submap(memorypool*, int*&, face*&); - void maketetrahedron(triface*); - void makeshellface(memorypool*, face*); - void makepoint(point*, enum verttype); - - void initializepools(); - -/////////////////////////////////////////////////////////////////////////////// -// // -// Advanced geometric predicates and calculations // -// // -// TetGen uses a simplified symbolic perturbation scheme from Edelsbrunner, // -// et al [*]. Hence the point-in-sphere test never returns a zero. The idea // -// is to perturb the weights of vertices in the fourth dimension. TetGen // -// uses the indices of the vertices decide the amount of perturbation. It is // -// implemented in the routine insphere_s(). -// // -// The routine tri_edge_test() determines whether or not a triangle and an // -// edge intersect in 3D. If they intersect, their intersection type is also // -// reported. This test is a combination of n 3D orientation tests (n is bet- // -// ween 3 and 9). It uses the robust orient3d() test to make the branch dec- // -// isions. The routine tri_tri_test() determines whether or not two triang- // -// les intersect in 3D. It also uses the robust orient3d() test. // -// // -// There are a number of routines to calculate geometrical quantities, e.g., // -// circumcenters, angles, dihedral angles, face normals, face areas, etc. // -// They are so far done by the default floating-point arithmetics which are // -// non-robust. They should be improved in the future. // -// // -/////////////////////////////////////////////////////////////////////////////// - - // Symbolic perturbations (robust) - REAL insphere_s(REAL*, REAL*, REAL*, REAL*, REAL*); - REAL orient4d_s(REAL*, REAL*, REAL*, REAL*, REAL*, - REAL, REAL, REAL, REAL, REAL); - - // Triangle-edge intersection test (robust) - int tri_edge_2d(point, point, point, point, point, point, int, int*, int*); - int tri_edge_tail(point, point, point, point, point, point, REAL, REAL, int, - int*, int*); - int tri_edge_test(point, point, point, point, point, point, int, int*, int*); - - // 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); - inline void cross(REAL* v1, REAL* v2, REAL* n); - bool lu_decmp(REAL lu[4][4], int n, int* ps, REAL* d, int N); - void lu_solve(REAL lu[4][4], int n, int* ps, REAL* b, int N); - - // 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 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); - 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); - bool orthosphere(REAL*,REAL*,REAL*,REAL*,REAL,REAL,REAL,REAL,REAL*,REAL*); - void tetcircumcenter(point tetorg, point tetdest, point tetfapex, - point tettapex, REAL *circumcenter, REAL *radius); - 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); - - // PLC error reports. - void report_overlapping_facets(face*, face*, REAL dihedang = 0.0); - int report_selfint_edge(point, point, face* sedge, triface* searchtet, - enum interresult); - int report_selfint_face(point, point, point, face* sface, triface* iedge, - int intflag, int* types, int* poss); - -/////////////////////////////////////////////////////////////////////////////// -// // -// 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 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); - void randomsample(point searchpt, triface *searchtet); - enum locateresult locate(point searchpt, triface *searchtet, - int chkencflag = 0); - - // 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, int, int, int); - void scarveholes(int, REAL*); - int triangulate(int, arraypool*, arraypool*, int, REAL*); - - void unifysegments(); - void identifyinputedges(point*); - void mergefacets(); - void removesmallangles(); - 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, face*, triface*, point*, - arraypool*); - int getsteinerptonsegment(face* seg, point refpt, point steinpt); - void delaunizesegments(); - - int scoutsubface(face* searchsh,triface* searchtet,int shflag); - 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*); - - int insertpoint_cdt(point, triface*, face*, face*, insertvertexflags*, - arraypool*, arraypool*, arraypool*, arraypool*, - arraypool*, arraypool*); - void refineregion(face&, arraypool*, arraypool*, arraypool*, arraypool*, - arraypool*, arraypool*); - void constrainedfacets(); - - void constraineddelaunay(clock_t&); - -/////////////////////////////////////////////////////////////////////////////// -// // -// Constrained tetrahedralizations. // -// // -/////////////////////////////////////////////////////////////////////////////// - - int checkflipeligibility(int fliptype, point, point, point, point, point, - int level, int edgepivot, flipconstraints* fc); - - int removeedgebyflips(triface*, flipconstraints*); - int removefacebyflips(triface*, flipconstraints*); - - int recoveredgebyflips(point, point, face*, 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&); - -/////////////////////////////////////////////////////////////////////////////// -// // -// Mesh reconstruction // -// // -/////////////////////////////////////////////////////////////////////////////// - - void carveholes(); - - void reconstructmesh(); - - int search_face(point p0, point p1, point p2, triface &tetloop); - int search_edge(point p0, point p1, triface &tetloop); - int scoutpoint(point, triface*, int randflag); - REAL getpointmeshsize(point, triface*, int iloc); - void interpolatemeshsize(); - void out_points_to_cells_map(); // in flow_main() - - void insertconstrainedpoints(point *insertarray, int arylen, int rejflag); - void insertconstrainedpoints(tetgenio *addio); - - void collectremovepoints(arraypool *remptlist); - void meshcoarsening(); - -/////////////////////////////////////////////////////////////////////////////// -// // -// 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 *); - void save_segmentpoint_insradius(point segpt, point parentpt, REAL r); - void save_facetpoint_insradius(point facpt, point parentpt, REAL r); - void enqueuesubface(memorypool*, face*); - void enqueuetetrahedron(triface*); - - 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); - - 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); - - 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 // -// // -/////////////////////////////////////////////////////////////////////////////// - - 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 check and statistics // -// // -/////////////////////////////////////////////////////////////////////////////// - - // Mesh validations. - int checkmesh(int topoflag); - int checkshells(); - int checksegments(); - int checkdelaunay(int perturb = 1); - int checkregular(int); - int checkconforming(int); - - // Mesh statistics. - void printfcomma(unsigned long n); - void qualitystatistics(); - void memorystatistics(); - void statistics(); - -/////////////////////////////////////////////////////////////////////////////// -// // -// Mesh output // -// // -/////////////////////////////////////////////////////////////////////////////// - - void jettisonnodes(); - void highorder(); - void indexelements(); - void numberedges(); - void outnodes(tetgenio*); - void outmetrics(tetgenio*); - void outelements(tetgenio*); - void outfaces(tetgenio*); - void outhullfaces(tetgenio*); - void outsubfaces(tetgenio*); - void outedges(tetgenio*); - void outsubsegments(tetgenio*); - void outneighbors(tetgenio*); - void outvoronoi(tetgenio*); - void outsmesh(char*); - void outmesh2medit(char*); - void outmesh2vtk(char*); - - - - -/////////////////////////////////////////////////////////////////////////////// -// // -// Constructor & destructor // -// // -/////////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////////// + // // + // Memorypool // + // // + // A structure for memory allocation. (It is written by J. Shewchuk) // + // // + // firstblock is the first block of items. nowblock is the block from which // + // items are currently being allocated. nextitem points to the next slab // + // of free memory for an item. deaditemstack is the head of a linked list // + // (stack) of deallocated items that can be recycled. unallocateditems is // + // the number of items that remain to be allocated from nowblock. // + // // + // Traversal is the process of walking through the entire list of items, and // + // is separate from allocation. Note that a traversal will visit items on // + // the "deaditemstack" stack as well as live items. pathblock points to // + // the block currently being traversed. pathitem points to the next item // + // to be traversed. pathitemsleft is the number of items that remain to // + // be traversed in pathblock. // + // // + /////////////////////////////////////////////////////////////////////////////// + + class memorypool { + + public: + void **firstblock, **nowblock; + void* nextitem; + void* deaditemstack; + void** pathblock; + void* pathitem; + int alignbytes; + int itembytes, itemwords; + int itemsperblock; + long items, maxitems; + int unallocateditems; + int pathitemsleft; + + memorypool(); + memorypool(int, int, int, int); + ~memorypool(); + + void poolinit(int, int, int, int); + void restart(); + void* alloc(); + void dealloc(void*); + void traversalinit(); + void* traverse(); + }; + + /////////////////////////////////////////////////////////////////////////////// + // // + // badface // + // // + // Despite of its name, a 'badface' can be used to represent one of the // + // following objects: // + // - a 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). // + // // + /////////////////////////////////////////////////////////////////////////////// + + 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) {} + }; + + /////////////////////////////////////////////////////////////////////////////// + // // + // 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; + } + }; + + /////////////////////////////////////////////////////////////////////////////// + // // + // optparameters // + // // + // Optimization options and parameters. // + // // + /////////////////////////////////////////////////////////////////////////////// + + class optparameters { + + public: + // The one of goals of optimization. + int max_min_volume; // Maximize the minimum volume. + int min_max_aspectratio; // Minimize the maximum 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; + min_max_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 + }; + + // 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 pointinsradiusindex; // Index to find the insertion radius 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 minedgelength; // = longest * b->epsion. + 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 tetrahedron encode(triface& t); + inline tetrahedron encode2(tetrahedron* ptr, int ver); + inline void decode(tetrahedron ptr, triface& t); + inline void bond(triface& t1, triface& t2); + inline void dissolve(triface& t); + inline void esym(triface& t1, triface& t2); + inline void esymself(triface& t); + inline void enext(triface& t1, triface& t2); + inline void enextself(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 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); + inline bool infected(triface& t); + inline void marktest(triface& t); + inline void unmarktest(triface& t); + inline bool marktested(triface& t); + inline void markface(triface& t); + inline void unmarkface(triface& t); + inline bool facemarked(triface& t); + inline void markedge(triface& t); + inline void unmarkedge(triface& t); + inline bool edgemarked(triface& t); + 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); + inline void sbond1(face& s1, face& s2); + inline void sdissolve(face& s); + inline point sorg(face& s); + inline point sdest(face& s); + inline point sapex(face& s); + inline void setsorg(face& s, point pointptr); + inline void setsdest(face& s, point pointptr); + inline void setsapex(face& s, point pointptr); + inline void sesym(face& s1, face& s2); + inline void sesymself(face& s); + inline void senext(face& s1, face& s2); + inline void senextself(face& s); + inline void senext2(face& s1, face& s2); + inline void senext2self(face& s); + inline REAL areabound(face& s); + inline void setareabound(face& s, REAL value); + inline int shellmark(face& s); + inline void setshellmark(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 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 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 point point2ppt(point pt); + inline void setpoint2ppt(point pt, point value); + inline tetrahedron point2bgmtet(point pt); + inline void setpoint2bgmtet(point pt, tetrahedron value); + inline void setpointinsradius(point pt, REAL value); + inline REAL getpointinsradius(point pt); + inline bool issteinerpoint(point pt); + + // Advanced primitives. + 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 // + // // + /////////////////////////////////////////////////////////////////////////////// + + void tetrahedrondealloc(tetrahedron*); + tetrahedron* tetrahedrontraverse(); + tetrahedron* alltetrahedrontraverse(); + void shellfacedealloc(memorypool*, shellface*); + shellface* shellfacetraverse(memorypool*); + void pointdealloc(point); + point pointtraverse(); + + void makeindex2pointmap(point*&); + void makepoint2submap(memorypool*, int*&, face*&); + void maketetrahedron(triface*); + void makeshellface(memorypool*, face*); + void makepoint(point*, enum verttype); + + void initializepools(); + + /////////////////////////////////////////////////////////////////////////////// + // // + // Advanced geometric predicates and calculations // + // // + // TetGen uses a simplified symbolic perturbation scheme from Edelsbrunner, // + // et al [*]. Hence the point-in-sphere test never returns a zero. The idea // + // is to perturb the weights of vertices in the fourth dimension. TetGen // + // uses the indices of the vertices decide the amount of perturbation. It is // + // implemented in the routine insphere_s(). + // // + // The routine tri_edge_test() determines whether or not a triangle and an // + // edge intersect in 3D. If they intersect, their intersection type is also // + // reported. This test is a combination of n 3D orientation tests (n is bet- // + // ween 3 and 9). It uses the robust orient3d() test to make the branch dec- // + // isions. The routine tri_tri_test() determines whether or not two triang- // + // les intersect in 3D. It also uses the robust orient3d() test. // + // // + // There are a number of routines to calculate geometrical quantities, e.g., // + // circumcenters, angles, dihedral angles, face normals, face areas, etc. // + // They are so far done by the default floating-point arithmetics which are // + // non-robust. They should be improved in the future. // + // // + /////////////////////////////////////////////////////////////////////////////// + + // Symbolic perturbations (robust) + REAL insphere_s(REAL*, REAL*, REAL*, REAL*, REAL*); + REAL orient4d_s(REAL*, REAL*, REAL*, REAL*, REAL*, REAL, REAL, REAL, REAL, REAL); + + // Triangle-edge intersection test (robust) + int tri_edge_2d(point, point, point, point, point, point, int, int*, int*); + int tri_edge_tail(point, point, point, point, point, point, REAL, REAL, int, int*, int*); + int tri_edge_test(point, point, point, point, point, point, int, int*, int*); + + // 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); + inline void cross(REAL* v1, REAL* v2, REAL* n); + bool lu_decmp(REAL lu[4][4], int n, int* ps, REAL* d, int N); + void lu_solve(REAL lu[4][4], int n, int* ps, REAL* b, int N); + + // 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 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); + 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); + bool orthosphere(REAL*, REAL*, REAL*, REAL*, REAL, REAL, REAL, REAL, REAL*, REAL*); + void tetcircumcenter(point tetorg, point tetdest, point tetfapex, point tettapex, + REAL* circumcenter, REAL* radius); + 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); + + // PLC error reports. + void report_overlapping_facets(face*, face*, REAL dihedang = 0.0); + int report_selfint_edge(point, point, face* sedge, triface* searchtet, enum interresult); + int report_selfint_face(point, point, point, face* sface, triface* iedge, int intflag, + int* types, int* poss); + + /////////////////////////////////////////////////////////////////////////////// + // // + // 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 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); + void randomsample(point searchpt, triface* searchtet); + enum locateresult locate(point searchpt, triface* searchtet, int chkencflag = 0); + + // 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, int, int, int); + void scarveholes(int, REAL*); + int triangulate(int, arraypool*, arraypool*, int, REAL*); + + void unifysegments(); + void identifyinputedges(point*); + void mergefacets(); + void removesmallangles(); + 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, face*, triface*, point*, arraypool*); + int getsteinerptonsegment(face* seg, point refpt, point steinpt); + void delaunizesegments(); + + int scoutsubface(face* searchsh, triface* searchtet, int shflag); + 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*); + + int insertpoint_cdt(point, triface*, face*, face*, insertvertexflags*, arraypool*, arraypool*, + arraypool*, arraypool*, arraypool*, arraypool*); + void refineregion(face&, arraypool*, arraypool*, arraypool*, arraypool*, arraypool*, + arraypool*); + void constrainedfacets(); + + void constraineddelaunay(clock_t&); + + /////////////////////////////////////////////////////////////////////////////// + // // + // Constrained tetrahedralizations. // + // // + /////////////////////////////////////////////////////////////////////////////// + + int checkflipeligibility(int fliptype, point, point, point, point, point, int level, + int edgepivot, flipconstraints* fc); + + int removeedgebyflips(triface*, flipconstraints*); + int removefacebyflips(triface*, flipconstraints*); + + int recoveredgebyflips(point, point, face*, 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&); + + /////////////////////////////////////////////////////////////////////////////// + // // + // Mesh reconstruction // + // // + /////////////////////////////////////////////////////////////////////////////// + + void carveholes(); + + void reconstructmesh(); + + int search_face(point p0, point p1, point p2, triface& tetloop); + int search_edge(point p0, point p1, triface& tetloop); + int scoutpoint(point, triface*, int randflag); + REAL getpointmeshsize(point, triface*, int iloc); + void interpolatemeshsize(); + void out_points_to_cells_map(); // in flow_main() + + void insertconstrainedpoints(point* insertarray, int arylen, int rejflag); + void insertconstrainedpoints(tetgenio* addio); + + void collectremovepoints(arraypool* remptlist); + void meshcoarsening(); + + /////////////////////////////////////////////////////////////////////////////// + // // + // 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*); + void save_segmentpoint_insradius(point segpt, point parentpt, REAL r); + void save_facetpoint_insradius(point facpt, point parentpt, REAL r); + void enqueuesubface(memorypool*, face*); + void enqueuetetrahedron(triface*); + + 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); + + 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); + + 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 // + // // + /////////////////////////////////////////////////////////////////////////////// + + 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 check and statistics // + // // + /////////////////////////////////////////////////////////////////////////////// + + // Mesh validations. + int checkmesh(int topoflag); + int checkshells(); + int checksegments(); + int checkdelaunay(int perturb = 1); + int checkregular(int); + int checkconforming(int); + + // Mesh statistics. + void printfcomma(unsigned long n); + void qualitystatistics(); + void memorystatistics(); + void statistics(); + + /////////////////////////////////////////////////////////////////////////////// + // // + // Mesh output // + // // + /////////////////////////////////////////////////////////////////////////////// + + void jettisonnodes(); + void highorder(); + void indexelements(); + void numberedges(); + void outnodes(tetgenio*); + void outmetrics(tetgenio*); + void outelements(tetgenio*); + void outfaces(tetgenio*); + void outhullfaces(tetgenio*); + void outsubfaces(tetgenio*); + void outedges(tetgenio*); + void outsubsegments(tetgenio*); + void outneighbors(tetgenio*); + void outvoronoi(tetgenio*); + void outsmesh(char*); + void outmesh2medit(char*); + void outmesh2vtk(char*); + + /////////////////////////////////////////////////////////////////////////////// + // // + // Constructor & destructor // + // // + /////////////////////////////////////////////////////////////////////////////// + + void initializetetgenmesh() { + in = addin = NULL; + b = NULL; + bgm = NULL; + + tetrahedrons = subfaces = subsegs = points = NULL; + badtetrahedrons = badsubfacs = badsubsegs = NULL; + tet2segpool = tet2subpool = NULL; + flippool = NULL; + + dummypoint = NULL; + flipstack = NULL; + unflipqueue = NULL; + + cavetetlist = cavebdrylist = caveoldtetlist = NULL; + cavetetshlist = cavetetseglist = cavetetvertlist = NULL; + caveencshlist = caveencseglist = NULL; + caveshlist = caveshbdlist = cavesegshlist = NULL; + + subsegstack = subfacstack = subvertstack = NULL; + encseglist = encshlist = NULL; + idx2facetlist = NULL; + facetverticeslist = NULL; + segmentendpointslist = NULL; + + highordertable = NULL; + + numpointattrib = numelemattrib = 0; + sizeoftensor = 0; + pointmtrindex = 0; + pointparamindex = 0; + pointmarkindex = 0; + point2simindex = 0; + pointinsradiusindex = 0; + elemattribindex = 0; + volumeboundindex = 0; + shmarkindex = 0; + areaboundindex = 0; + checksubsegflag = 0; + checksubfaceflag = 0; + checkconstraints = 0; + nonconvex = 0; + autofliplinklevel = 1; + useinsertradius = 0; + samples = 0l; + randomseed = 1l; + minfaceang = minfacetdihed = PI; + tetprism_vol_sum = 0.0; + longest = minedgelength = 0.0; + xmax = xmin = ymax = ymin = zmax = zmin = 0.0; + + insegments = 0l; + hullsize = 0l; + meshedges = meshhulledges = 0l; + steinerleft = -1; + dupverts = 0l; + unuverts = 0l; + nonregularcount = 0l; + st_segref_count = st_facref_count = st_volref_count = 0l; + fillregioncount = cavitycount = cavityexpcount = 0l; + flip14count = flip26count = flipn2ncount = 0l; + flip23count = flip32count = flip44count = flip41count = 0l; + flip22count = flip31count = 0l; + totalworkmemory = 0l; + + } // tetgenmesh() + + void freememory() { + if (bgm != NULL) { + delete bgm; + } - void initializetetgenmesh() - { - in = addin = NULL; - b = NULL; - bgm = NULL; - - tetrahedrons = subfaces = subsegs = points = NULL; - badtetrahedrons = badsubfacs = badsubsegs = NULL; - tet2segpool = tet2subpool = NULL; - flippool = NULL; - - dummypoint = NULL; - flipstack = NULL; - unflipqueue = NULL; - - cavetetlist = cavebdrylist = caveoldtetlist = NULL; - cavetetshlist = cavetetseglist = cavetetvertlist = NULL; - caveencshlist = caveencseglist = NULL; - caveshlist = caveshbdlist = cavesegshlist = NULL; - - subsegstack = subfacstack = subvertstack = NULL; - encseglist = encshlist = NULL; - idx2facetlist = NULL; - facetverticeslist = NULL; - segmentendpointslist = NULL; - - highordertable = NULL; - - numpointattrib = numelemattrib = 0; - sizeoftensor = 0; - pointmtrindex = 0; - pointparamindex = 0; - pointmarkindex = 0; - point2simindex = 0; - pointinsradiusindex = 0; - elemattribindex = 0; - volumeboundindex = 0; - shmarkindex = 0; - areaboundindex = 0; - checksubsegflag = 0; - checksubfaceflag = 0; - checkconstraints = 0; - nonconvex = 0; - autofliplinklevel = 1; - useinsertradius = 0; - samples = 0l; - randomseed = 1l; - minfaceang = minfacetdihed = PI; - tetprism_vol_sum = 0.0; - longest = minedgelength = 0.0; - xmax = xmin = ymax = ymin = zmax = zmin = 0.0; - - insegments = 0l; - hullsize = 0l; - meshedges = meshhulledges = 0l; - steinerleft = -1; - dupverts = 0l; - unuverts = 0l; - nonregularcount = 0l; - st_segref_count = st_facref_count = st_volref_count = 0l; - fillregioncount = cavitycount = cavityexpcount = 0l; - flip14count = flip26count = flipn2ncount = 0l; - flip23count = flip32count = flip44count = flip41count = 0l; - flip22count = flip31count = 0l; - totalworkmemory = 0l; - - - } // tetgenmesh() - - void freememory() - { - 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; + delete subsegs; + } + if (tet2segpool != NULL) { + delete tet2segpool; + delete tet2subpool; + } - if (points != (memorypool *) NULL) { - delete points; - delete [] dummypoint; - } - if (tetrahedrons != (memorypool *) NULL) { - delete tetrahedrons; - } - if (subfaces != (memorypool *) NULL) { - delete subfaces; - delete subsegs; - } - if (tet2segpool != NULL) { - delete tet2segpool; - delete tet2subpool; - } + if (badtetrahedrons) { + delete badtetrahedrons; + } + if (badsubfacs) { + delete badsubfacs; + } + if (badsubsegs) { + delete badsubsegs; + } + if (encseglist) { + delete encseglist; + } + if (encshlist) { + delete encshlist; + } - if (badtetrahedrons) { - delete badtetrahedrons; - } - if (badsubfacs) { - delete badsubfacs; - } - if (badsubsegs) { - delete badsubsegs; - } - if (encseglist) { - delete encseglist; - } - if (encshlist) { - delete encshlist; - } + if (flippool != NULL) { + delete flippool; + delete unflipqueue; + } - if (flippool != NULL) { - delete flippool; - delete unflipqueue; - } + if (cavetetlist != NULL) { + delete cavetetlist; + delete cavebdrylist; + delete caveoldtetlist; + delete cavetetvertlist; + } - 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 (caveshlist != NULL) { - delete caveshlist; - delete caveshbdlist; - delete cavesegshlist; - delete cavetetshlist; - delete cavetetseglist; - delete caveencshlist; - delete caveencseglist; - } + if (subsegstack != NULL) { + delete subsegstack; + delete subfacstack; + delete subvertstack; + } - if (subsegstack != NULL) { - delete subsegstack; - delete subfacstack; - delete subvertstack; - } + if (idx2facetlist != NULL) { + delete[] idx2facetlist; + delete[] facetverticeslist; + } - if (idx2facetlist != NULL) { - delete [] idx2facetlist; - delete [] facetverticeslist; - } + if (segmentendpointslist != NULL) { + delete[] segmentendpointslist; + } - if (segmentendpointslist != NULL) { - delete [] segmentendpointslist; - } + if (highordertable != NULL) { + delete[] highordertable; + } - if (highordertable != NULL) { - delete [] highordertable; + initializetetgenmesh(); } - initializetetgenmesh(); - } - - tetgenmesh() - { - initializetetgenmesh(); - } + tetgenmesh() { initializetetgenmesh(); } - ~tetgenmesh() - { - freememory(); - } // ~tetgenmesh() + ~tetgenmesh() { freememory(); } // ~tetgenmesh() -}; // End of class tetgenmesh. +}; // End of class tetgenmesh. /////////////////////////////////////////////////////////////////////////////// // // @@ -2298,12 +2285,12 @@ public: // // /////////////////////////////////////////////////////////////////////////////// -void tetrahedralize(tetgenbehavior *b, tetgenio *in, tetgenio *out, - tetgenio *addin = NULL, tetgenio *bgmin = NULL); +void tetrahedralize(tetgenbehavior* b, tetgenio* in, tetgenio* out, tetgenio* addin = NULL, + tetgenio* bgmin = NULL); #ifdef TETLIBRARY -void tetrahedralize(char *switches, tetgenio *in, tetgenio *out, - tetgenio *addin = NULL, tetgenio *bgmin = NULL); +void tetrahedralize(char* switches, tetgenio* in, tetgenio* out, tetgenio* addin = NULL, + tetgenio* bgmin = NULL); #endif // #ifdef TETLIBRARY /////////////////////////////////////////////////////////////////////////////// @@ -2314,7 +2301,7 @@ void tetrahedralize(char *switches, tetgenio *in, tetgenio *out, // selfint_event, a structure to report self-intersections. // -// - e_type, report the type of self-intersections, +// - e_type, report the type of self-intersections, // it may be one of: // 0, reserved. // 1, two edges intersect, @@ -2324,61 +2311,60 @@ void tetrahedralize(char *switches, tetgenio *in, tetgenio *out, // 5, an edge and a triangle are overlapping, // 6, two triangles are overlapping, // 7, a vertex lies in an edge, -// 8, a vertex lies in a facet, +// 8, a vertex lies in a facet, class selfint_event { -public: - int e_type; - int f_marker1; // Tag of the 1st facet. - int s_marker1; // Tag of the 1st segment. - int f_vertices1[3]; - int f_marker2; // Tag of the 2nd facet. - int s_marker2; // Tag of the 2nd segment. - int f_vertices2[3]; - REAL int_point[3]; - selfint_event() { - e_type = 0; - f_marker1 = f_marker2 = 0; - s_marker1 = s_marker2 = 0; - } + public: + int e_type; + int f_marker1; // Tag of the 1st facet. + int s_marker1; // Tag of the 1st segment. + int f_vertices1[3]; + int f_marker2; // Tag of the 2nd facet. + int s_marker2; // Tag of the 2nd segment. + int f_vertices2[3]; + REAL int_point[3]; + selfint_event() { + e_type = 0; + f_marker1 = f_marker2 = 0; + s_marker1 = s_marker2 = 0; + } }; static selfint_event sevent; -inline void terminatetetgen(tetgenmesh *m, int x) -{ +inline void terminatetetgen(tetgenmesh* m, int x) { #ifdef TETLIBRARY - throw x; + throw x; #else - switch (x) { - case 1: // Out of memory. - printf("Error: Out of memory.\n"); - break; - case 2: // Encounter an internal error. - printf("Please report this bug to 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"); - if (m) { - printf("Hint: use -T option to set a smaller tolerance. Current is %g\n", - m->b->epsilon); - } - 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; - } // switch (x) - exit(x); + switch (x) { + case 1: // Out of memory. + printf("Error: Out of memory.\n"); + break; + case 2: // Encounter an internal error. + printf("Please report this bug to 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"); + if (m) { + printf("Hint: use -T option to set a smaller tolerance. Current is %g\n", + m->b->epsilon); + } + 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; + } // switch (x) + exit(x); #endif // #ifdef TETLIBRARY } @@ -2388,354 +2374,302 @@ inline void terminatetetgen(tetgenmesh *m, int x) // // /////////////////////////////////////////////////////////////////////////////// -// encode() compress a handle into a single pointer. It relies on the +// 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).ver); + return (tetrahedron)((uintptr_t)(t).tet | (uintptr_t)(t).ver); } inline tetgenmesh::tetrahedron tetgenmesh::encode2(tetrahedron* ptr, int ver) { - return (tetrahedron) ((uintptr_t) (ptr) | (uintptr_t) (ver)); + return (tetrahedron)((uintptr_t)(ptr) | (uintptr_t)(ver)); } // decode() converts a pointer to a handle. The version is extracted from // the four least significant bits of the pointer. inline void tetgenmesh::decode(tetrahedron ptr, triface& t) { - (t).ver = (int) ((uintptr_t) (ptr) & (uintptr_t) 15); - (t).tet = (tetrahedron *) ((uintptr_t) (ptr) ^ (uintptr_t) (t).ver); + (t).ver = (int)((uintptr_t)(ptr) & (uintptr_t)15); + (t).tet = (tetrahedron*)((uintptr_t)(ptr) ^ (uintptr_t)(t).ver); } -// bond() connects two tetrahedra together. (t1,v1) and (t2,v2) must -// refer to the same face and the same edge. +// bond() connects two 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.ver & 3] = encode2(t2.tet, bondtbl[t1.ver][t2.ver]); - t2.tet[t2.ver & 3] = encode2(t1.tet, bondtbl[t2.ver][t1.ver]); + t1.tet[t1.ver & 3] = encode2(t2.tet, bondtbl[t1.ver][t2.ver]); + t2.tet[t2.ver & 3] = encode2(t1.tet, bondtbl[t2.ver][t1.ver]); } - // dissolve() a bond (from one side). -inline void tetgenmesh::dissolve(triface& t) { - t.tet[t.ver & 3] = NULL; -} +inline void tetgenmesh::dissolve(triface& t) { t.tet[t.ver & 3] = NULL; } // enext() finds the next edge (counterclockwise) in the same face. inline void tetgenmesh::enext(triface& t1, triface& t2) { - t2.tet = t1.tet; - t2.ver = enexttbl[t1.ver]; + t2.tet = t1.tet; + t2.ver = enexttbl[t1.ver]; } -inline void tetgenmesh::enextself(triface& t) { - t.ver = enexttbl[t.ver]; -} +inline void tetgenmesh::enextself(triface& t) { t.ver = enexttbl[t.ver]; } // eprev() finds the next edge (clockwise) in the same face. inline void tetgenmesh::eprev(triface& t1, triface& t2) { - t2.tet = t1.tet; - t2.ver = eprevtbl[t1.ver]; + t2.tet = t1.tet; + t2.ver = eprevtbl[t1.ver]; } -inline void tetgenmesh::eprevself(triface& t) { - t.ver = eprevtbl[t.ver]; -} +inline void tetgenmesh::eprevself(triface& t) { t.ver = eprevtbl[t.ver]; } // esym() finds the reversed edge. It is in the other face of the // same tetrahedron. inline void tetgenmesh::esym(triface& t1, triface& t2) { - (t2).tet = (t1).tet; - (t2).ver = esymtbl[(t1).ver]; + (t2).tet = (t1).tet; + (t2).ver = esymtbl[(t1).ver]; } -inline void tetgenmesh::esymself(triface& t) { - (t).ver = esymtbl[(t).ver]; -} +inline void tetgenmesh::esymself(triface& t) { (t).ver = esymtbl[(t).ver]; } // enextesym() finds the reversed edge of the next edge. It is in the other -// face of the same tetrahedron. It is the combination esym() * enext(). +// face of the same tetrahedron. It is the combination esym() * enext(). inline void tetgenmesh::enextesym(triface& t1, triface& t2) { - t2.tet = t1.tet; - t2.ver = enextesymtbl[t1.ver]; + t2.tet = t1.tet; + t2.ver = enextesymtbl[t1.ver]; } -inline void tetgenmesh::enextesymself(triface& t) { - t.ver = enextesymtbl[t.ver]; -} +inline void tetgenmesh::enextesymself(triface& t) { t.ver = enextesymtbl[t.ver]; } // eprevesym() finds the reversed edge of the previous edge. inline void tetgenmesh::eprevesym(triface& t1, triface& t2) { - t2.tet = t1.tet; - t2.ver = eprevesymtbl[t1.ver]; + t2.tet = t1.tet; + t2.ver = eprevesymtbl[t1.ver]; } -inline void tetgenmesh::eprevesymself(triface& t) { - t.ver = eprevesymtbl[t.ver]; -} +inline void tetgenmesh::eprevesymself(triface& t) { t.ver = eprevesymtbl[t.ver]; } // eorgoppo() Finds the opposite face of the origin of the current edge. // Return the opposite edge of the current edge. inline void tetgenmesh::eorgoppo(triface& t1, triface& t2) { - t2.tet = t1.tet; - t2.ver = eorgoppotbl[t1.ver]; + t2.tet = t1.tet; + t2.ver = eorgoppotbl[t1.ver]; } -inline void tetgenmesh::eorgoppoself(triface& t) { - t.ver = eorgoppotbl[t.ver]; -} +inline void tetgenmesh::eorgoppoself(triface& t) { t.ver = eorgoppotbl[t.ver]; } -// edestoppo() Finds the opposite face of the destination of the current +// edestoppo() Finds the opposite face of the destination of the current // edge. Return the opposite edge of the current edge. inline void tetgenmesh::edestoppo(triface& t1, triface& t2) { - t2.tet = t1.tet; - t2.ver = edestoppotbl[t1.ver]; + t2.tet = t1.tet; + t2.ver = edestoppotbl[t1.ver]; } -inline void tetgenmesh::edestoppoself(triface& t) { - t.ver = edestoppotbl[t.ver]; -} +inline void tetgenmesh::edestoppoself(triface& t) { t.ver = edestoppotbl[t.ver]; } // fsym() finds the adjacent tetrahedron at the same face and the same edge. inline void tetgenmesh::fsym(triface& t1, triface& t2) { - decode((t1).tet[(t1).ver & 3], t2); - t2.ver = fsymtbl[t1.ver][t2.ver]; + decode((t1).tet[(t1).ver & 3], t2); + t2.ver = fsymtbl[t1.ver][t2.ver]; } - -#define fsymself(t) \ - t1ver = (t).ver; \ - decode((t).tet[(t).ver & 3], (t));\ - (t).ver = fsymtbl[t1ver][(t).ver] +#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]; + decode(t1.tet[facepivot1[t1.ver]], t2); + t2.ver = facepivot2[t1.ver][t2.ver]; } - -#define fnextself(t) \ - t1ver = (t).ver; \ - decode((t).tet[facepivot1[(t).ver]], (t)); \ - (t).ver = facepivot2[t1ver][(t).ver] - +#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 tetgenmesh::point tetgenmesh::org(triface& t) { return (point)(t).tet[orgpivot[(t).ver]]; } -inline tetgenmesh::point tetgenmesh:: dest(triface& t) { - return (point) (t).tet[destpivot[(t).ver]]; -} +inline tetgenmesh::point tetgenmesh::dest(triface& t) { return (point)(t).tet[destpivot[(t).ver]]; } -inline tetgenmesh::point tetgenmesh:: apex(triface& t) { - return (point) (t).tet[apexpivot[(t).ver]]; -} +inline tetgenmesh::point tetgenmesh::apex(triface& t) { return (point)(t).tet[apexpivot[(t).ver]]; } -inline tetgenmesh::point tetgenmesh:: oppo(triface& t) { - return (point) (t).tet[oppopivot[(t).ver]]; -} +inline tetgenmesh::point tetgenmesh::oppo(triface& t) { return (point)(t).tet[oppopivot[(t).ver]]; } -inline void tetgenmesh:: setorg(triface& t, point p) { - (t).tet[orgpivot[(t).ver]] = (tetrahedron) (p); +inline void tetgenmesh::setorg(triface& t, point p) { + (t).tet[orgpivot[(t).ver]] = (tetrahedron)(p); } -inline void tetgenmesh:: setdest(triface& t, point p) { - (t).tet[destpivot[(t).ver]] = (tetrahedron) (p); +inline void tetgenmesh::setdest(triface& t, point p) { + (t).tet[destpivot[(t).ver]] = (tetrahedron)(p); } -inline void tetgenmesh:: setapex(triface& t, point p) { - (t).tet[apexpivot[(t).ver]] = (tetrahedron) (p); +inline void tetgenmesh::setapex(triface& t, point p) { + (t).tet[apexpivot[(t).ver]] = (tetrahedron)(p); } -inline void tetgenmesh:: setoppo(triface& t, point p) { - (t).tet[oppopivot[(t).ver]] = (tetrahedron) (p); +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) +#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]; + return ((REAL*)(ptr))[elemattribindex + attnum]; } -inline void tetgenmesh::setelemattribute(tetrahedron* ptr, int attnum, - REAL value) { - ((REAL *) (ptr))[elemattribindex + attnum] = value; +inline void tetgenmesh::setelemattribute(tetrahedron* ptr, int attnum, REAL value) { + ((REAL*)(ptr))[elemattribindex + attnum] = value; } // Check or set a tetrahedron's maximum volume bound. -inline REAL tetgenmesh::volumebound(tetrahedron* ptr) { - return ((REAL *) (ptr))[volumeboundindex]; -} +inline REAL tetgenmesh::volumebound(tetrahedron* ptr) { return ((REAL*)(ptr))[volumeboundindex]; } inline void tetgenmesh::setvolumebound(tetrahedron* ptr, REAL value) { - ((REAL *) (ptr))[volumeboundindex] = value; + ((REAL*)(ptr))[volumeboundindex] = value; } // 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]; + int* iptr = (int*)&(ptr[10]); + return iptr[0]; } inline void tetgenmesh::setelemindex(tetrahedron* ptr, int value) { - int *iptr = (int *) &(ptr[10]); - iptr[0] = value; + int* iptr = (int*)&(ptr[10]); + iptr[0] = value; } -// Get or set a tetrahedron's marker. +// Get or set a tetrahedron's marker. // Set 'value = 0' cleans all the face/edge flags. -inline int tetgenmesh::elemmarker(tetrahedron* ptr) { - return ((int *) (ptr))[elemmarkerindex]; -} +inline int tetgenmesh::elemmarker(tetrahedron* ptr) { return ((int*)(ptr))[elemmarkerindex]; } inline void tetgenmesh::setelemmarker(tetrahedron* ptr, int value) { - ((int *) (ptr))[elemmarkerindex] = value; + ((int*)(ptr))[elemmarkerindex] = value; } // infect(), infected(), uninfect() -- primitives to flag or unflag a // tetrahedron. The last bit of the element marker is flagged (1) // or unflagged (0). -inline void tetgenmesh::infect(triface& t) { - ((int *) (t.tet))[elemmarkerindex] |= 1; -} +inline void tetgenmesh::infect(triface& t) { ((int*)(t.tet))[elemmarkerindex] |= 1; } -inline void tetgenmesh::uninfect(triface& t) { - ((int *) (t.tet))[elemmarkerindex] &= ~1; -} +inline void tetgenmesh::uninfect(triface& t) { ((int*)(t.tet))[elemmarkerindex] &= ~1; } -inline bool tetgenmesh::infected(triface& t) { - return (((int *) (t.tet))[elemmarkerindex] & 1) != 0; -} +inline bool tetgenmesh::infected(triface& t) { return (((int*)(t.tet))[elemmarkerindex] & 1) != 0; } // marktest(), marktested(), unmarktest() -- primitives to flag or unflag a // tetrahedron. Use the second lowerest bit of the element marker. -inline void tetgenmesh::marktest(triface& t) { - ((int *) (t.tet))[elemmarkerindex] |= 2; -} +inline void tetgenmesh::marktest(triface& t) { ((int*)(t.tet))[elemmarkerindex] |= 2; } + +inline void tetgenmesh::unmarktest(triface& t) { ((int*)(t.tet))[elemmarkerindex] &= ~2; } -inline void tetgenmesh::unmarktest(triface& t) { - ((int *) (t.tet))[elemmarkerindex] &= ~2; -} - inline bool tetgenmesh::marktested(triface& t) { - return (((int *) (t.tet))[elemmarkerindex] & 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. +// 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)); + ((int*)(t.tet))[elemmarkerindex] |= (4 << (t.ver & 3)); } inline void tetgenmesh::unmarkface(triface& t) { - ((int *) (t.tet))[elemmarkerindex] &= ~(4 << (t.ver & 3)); + ((int*)(t.tet))[elemmarkerindex] &= ~(4 << (t.ver & 3)); } inline bool tetgenmesh::facemarked(triface& t) { - return (((int *) (t.tet))[elemmarkerindex] & (4 << (t.ver & 3))) != 0; + 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. +// 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]); + ((int*)(t.tet))[elemmarkerindex] |= (int)(64 << ver2edge[(t).ver]); } inline void tetgenmesh::unmarkedge(triface& t) { - ((int *) (t.tet))[elemmarkerindex] &= ~(int) (64 << ver2edge[(t).ver]); + ((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; + return (((int*)(t.tet))[elemmarkerindex] & (int)(64 << ver2edge[(t).ver])) != 0; } // marktest2(), unmarktest2(), marktest2ed() -- primitives to flag and unflag // a tetrahedron. The 13th bit (2^12 = 4096) is used for this flag. -inline void tetgenmesh::marktest2(triface& t) { - ((int *) (t.tet))[elemmarkerindex] |= (int) (4096); -} +inline void tetgenmesh::marktest2(triface& t) { ((int*)(t.tet))[elemmarkerindex] |= (int)(4096); } inline void tetgenmesh::unmarktest2(triface& t) { - ((int *) (t.tet))[elemmarkerindex] &= ~(int) (4096); + ((int*)(t.tet))[elemmarkerindex] &= ~(int)(4096); } inline bool tetgenmesh::marktest2ed(triface& t) { - return (((int *) (t.tet))[elemmarkerindex] & (int) (4096)) != 0; + return (((int*)(t.tet))[elemmarkerindex] & (int)(4096)) != 0; } // 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]. +// system, the range of the counter is [0, 2^15 = 32768]. -inline int tetgenmesh::elemcounter(triface& t) { - return (((int *) (t.tet))[elemmarkerindex]) >> 16; -} +inline int tetgenmesh::elemcounter(triface& t) { return (((int*)(t.tet))[elemmarkerindex]) >> 16; } inline void tetgenmesh::setelemcounter(triface& t, int value) { - int c = ((int *) (t.tet))[elemmarkerindex]; - // Clear the old counter while keep the other flags. - c &= 65535; // sum_{i=0^15} 2^i - c |= (value << 16); - ((int *) (t.tet))[elemmarkerindex] = c; + int c = ((int*)(t.tet))[elemmarkerindex]; + // Clear the old counter while keep the other flags. + c &= 65535; // sum_{i=0^15} 2^i + c |= (value << 16); + ((int*)(t.tet))[elemmarkerindex] = c; } inline void tetgenmesh::increaseelemcounter(triface& t) { - int c = elemcounter(t); - setelemcounter(t, c + 1); + int c = elemcounter(t); + setelemcounter(t, c + 1); } inline void tetgenmesh::decreaseelemcounter(triface& t) { - int c = elemcounter(t); - setelemcounter(t, c - 1); + int c = elemcounter(t); + setelemcounter(t, c - 1); } // ishulltet() tests if t is a hull tetrahedron. -inline bool tetgenmesh::ishulltet(triface& t) { - return (point) (t).tet[7] == dummypoint; -} +inline bool tetgenmesh::ishulltet(triface& t) { return (point)(t).tet[7] == dummypoint; } // isdeadtet() tests if t is a tetrahedron is dead. -inline bool tetgenmesh::isdeadtet(triface& t) { - return ((t.tet == NULL) || (t.tet[4] == NULL)); -} +inline bool tetgenmesh::isdeadtet(triface& t) { return ((t.tet == NULL) || (t.tet[4] == NULL)); } /////////////////////////////////////////////////////////////////////////////// // // @@ -2752,257 +2686,186 @@ inline bool tetgenmesh::isdeadtet(triface& t) { // extracting an edge version and a pointer to the beginning of a subface. inline void tetgenmesh::sdecode(shellface sptr, face& s) { - s.shver = (int) ((uintptr_t) (sptr) & (uintptr_t) 7); - s.sh = (shellface *) ((uintptr_t) (sptr) ^ (uintptr_t) (s.shver)); + s.shver = (int)((uintptr_t)(sptr) & (uintptr_t)7); + s.sh = (shellface*)((uintptr_t)(sptr) ^ (uintptr_t)(s.shver)); } inline tetgenmesh::shellface tetgenmesh::sencode(face& s) { - return (shellface) ((uintptr_t) s.sh | (uintptr_t) s.shver); + return (shellface)((uintptr_t)s.sh | (uintptr_t)s.shver); } -inline tetgenmesh::shellface tetgenmesh::sencode2(shellface *sh, int shver) { - return (shellface) ((uintptr_t) sh | (uintptr_t) shver); +inline tetgenmesh::shellface tetgenmesh::sencode2(shellface* sh, int shver) { + return (shellface)((uintptr_t)sh | (uintptr_t)shver); } // sbond() bonds two subfaces (s1) and (s2) together. s1 and s2 must refer // to the same edge. No requirement is needed on their orientations. -inline void tetgenmesh::sbond(face& s1, face& s2) -{ - s1.sh[s1.shver >> 1] = sencode(s2); - s2.sh[s2.shver >> 1] = sencode(s1); +inline void tetgenmesh::sbond(face& s1, face& s2) { + s1.sh[s1.shver >> 1] = sencode(s2); + s2.sh[s2.shver >> 1] = sencode(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[s1.shver >> 1] = 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[s.shver >> 1] = NULL; -} +inline void tetgenmesh::sdissolve(face& s) { s.sh[s.shver >> 1] = NULL; } // spivot() finds the adjacent subface (s2) for a given subface (s1). // s1 and s2 share at the same edge. -inline void tetgenmesh::spivot(face& s1, face& s2) -{ - shellface sptr = s1.sh[s1.shver >> 1]; - sdecode(sptr, s2); +inline void tetgenmesh::spivot(face& s1, face& s2) { + shellface sptr = s1.sh[s1.shver >> 1]; + sdecode(sptr, s2); } -inline void tetgenmesh::spivotself(face& s) -{ - shellface sptr = s.sh[s.shver >> 1]; - sdecode(sptr, s); +inline void tetgenmesh::spivotself(face& s) { + shellface sptr = s.sh[s.shver >> 1]; + sdecode(sptr, s); } // These primitives determine or set the origin, destination, or apex // of a subface with respect to the edge version. -inline tetgenmesh::point tetgenmesh::sorg(face& s) -{ - return (point) s.sh[sorgpivot[s.shver]]; -} +inline tetgenmesh::point tetgenmesh::sorg(face& s) { return (point)s.sh[sorgpivot[s.shver]]; } -inline tetgenmesh::point tetgenmesh::sdest(face& s) -{ - return (point) s.sh[sdestpivot[s.shver]]; -} +inline tetgenmesh::point tetgenmesh::sdest(face& s) { return (point)s.sh[sdestpivot[s.shver]]; } -inline tetgenmesh::point tetgenmesh::sapex(face& s) -{ - return (point) s.sh[sapexpivot[s.shver]]; -} +inline tetgenmesh::point tetgenmesh::sapex(face& s) { return (point)s.sh[sapexpivot[s.shver]]; } -inline void tetgenmesh::setsorg(face& s, point pointptr) -{ - s.sh[sorgpivot[s.shver]] = (shellface) pointptr; +inline void tetgenmesh::setsorg(face& s, point pointptr) { + s.sh[sorgpivot[s.shver]] = (shellface)pointptr; } -inline void tetgenmesh::setsdest(face& s, point pointptr) -{ - s.sh[sdestpivot[s.shver]] = (shellface) pointptr; +inline void tetgenmesh::setsdest(face& s, point pointptr) { + s.sh[sdestpivot[s.shver]] = (shellface)pointptr; } -inline void tetgenmesh::setsapex(face& s, point pointptr) -{ - s.sh[sapexpivot[s.shver]] = (shellface) pointptr; +inline void tetgenmesh::setsapex(face& s, point pointptr) { + s.sh[sapexpivot[s.shver]] = (shellface)pointptr; } -#define setshvertices(s, pa, pb, pc)\ - setsorg(s, pa);\ - setsdest(s, pb);\ - setsapex(s, pc) +#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::sesym(face& s1, face& s2) -{ - s2.sh = s1.sh; - s2.shver = (s1.shver ^ 1); // Inverse the last bit. +inline void tetgenmesh::sesym(face& s1, face& s2) { + s2.sh = s1.sh; + s2.shver = (s1.shver ^ 1); // Inverse the last bit. } -inline void tetgenmesh::sesymself(face& s) -{ - s.shver ^= 1; -} +inline void tetgenmesh::sesymself(face& s) { s.shver ^= 1; } // senext() finds the next edge (counterclockwise) in the same orientation // of this face. -inline void tetgenmesh::senext(face& s1, face& s2) -{ - s2.sh = s1.sh; - s2.shver = snextpivot[s1.shver]; +inline void tetgenmesh::senext(face& s1, face& s2) { + s2.sh = s1.sh; + s2.shver = snextpivot[s1.shver]; } -inline void tetgenmesh::senextself(face& s) -{ - s.shver = snextpivot[s.shver]; -} +inline void tetgenmesh::senextself(face& s) { s.shver = snextpivot[s.shver]; } -inline void tetgenmesh::senext2(face& s1, face& s2) -{ - s2.sh = s1.sh; - s2.shver = snextpivot[snextpivot[s1.shver]]; -} - -inline void tetgenmesh::senext2self(face& s) -{ - s.shver = snextpivot[snextpivot[s.shver]]; +inline void tetgenmesh::senext2(face& s1, face& s2) { + s2.sh = s1.sh; + s2.shver = snextpivot[snextpivot[s1.shver]]; } +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) -{ - return ((REAL *) (s.sh))[areaboundindex]; -} +inline REAL tetgenmesh::areabound(face& s) { return ((REAL*)(s.sh))[areaboundindex]; } -inline void tetgenmesh::setareabound(face& s, REAL value) -{ - ((REAL *) (s.sh))[areaboundindex] = 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. -inline int tetgenmesh::shellmark(face& s) -{ - return ((int *) (s.sh))[shmarkindex]; -} - -inline void tetgenmesh::setshellmark(face& s, int value) -{ - ((int *) (s.sh))[shmarkindex] = value; -} - +inline int tetgenmesh::shellmark(face& s) { return ((int*)(s.sh))[shmarkindex]; } +inline void tetgenmesh::setshellmark(face& s, int value) { ((int*)(s.sh))[shmarkindex] = value; } // 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::sinfect(face& s) { + ((int*)((s).sh))[shmarkindex + 1] = (((int*)((s).sh))[shmarkindex + 1] | (int)1); } -inline void tetgenmesh::suninfect(face& s) -{ - ((int *) ((s).sh))[shmarkindex+1] = - (((int *) ((s).sh))[shmarkindex+1] & ~(int) 1); +inline void tetgenmesh::suninfect(face& s) { + ((int*)((s).sh))[shmarkindex + 1] = (((int*)((s).sh))[shmarkindex + 1] & ~(int)1); } // Test a subface for viral infection. -inline bool tetgenmesh::sinfected(face& s) -{ - return (((int *) ((s).sh))[shmarkindex+1] & (int) 1) != 0; +inline bool tetgenmesh::sinfected(face& s) { + return (((int*)((s).sh))[shmarkindex + 1] & (int)1) != 0; } // smarktest(), smarktested(), sunmarktest() -- primitives to flag or unflag // a subface. The last 2nd bit of the integer is flagged. -inline void tetgenmesh::smarktest(face& s) -{ - ((int *) ((s).sh))[shmarkindex+1] = - (((int *)((s).sh))[shmarkindex+1] | (int) 2); +inline void tetgenmesh::smarktest(face& s) { + ((int*)((s).sh))[shmarkindex + 1] = (((int*)((s).sh))[shmarkindex + 1] | (int)2); } -inline void tetgenmesh::sunmarktest(face& s) -{ - ((int *) ((s).sh))[shmarkindex+1] = - (((int *)((s).sh))[shmarkindex+1] & ~(int)2); +inline void tetgenmesh::sunmarktest(face& s) { + ((int*)((s).sh))[shmarkindex + 1] = (((int*)((s).sh))[shmarkindex + 1] & ~(int)2); } -inline bool tetgenmesh::smarktested(face& s) -{ - return ((((int *) ((s).sh))[shmarkindex+1] & (int) 2) != 0); +inline bool tetgenmesh::smarktested(face& s) { + return ((((int*)((s).sh))[shmarkindex + 1] & (int)2) != 0); } -// smarktest2(), smarktest2ed(), sunmarktest2() -- primitives to flag or +// smarktest2(), smarktest2ed(), sunmarktest2() -- primitives to flag or // unflag a subface. The last 3rd bit of the integer is flagged. -inline void tetgenmesh::smarktest2(face& s) -{ - ((int *) ((s).sh))[shmarkindex+1] = - (((int *)((s).sh))[shmarkindex+1] | (int) 4); +inline void tetgenmesh::smarktest2(face& s) { + ((int*)((s).sh))[shmarkindex + 1] = (((int*)((s).sh))[shmarkindex + 1] | (int)4); } -inline void tetgenmesh::sunmarktest2(face& s) -{ - ((int *) ((s).sh))[shmarkindex+1] = - (((int *)((s).sh))[shmarkindex+1] & ~(int)4); +inline void tetgenmesh::sunmarktest2(face& s) { + ((int*)((s).sh))[shmarkindex + 1] = (((int*)((s).sh))[shmarkindex + 1] & ~(int)4); } -inline bool tetgenmesh::smarktest2ed(face& s) -{ - return ((((int *) ((s).sh))[shmarkindex+1] & (int) 4) != 0); +inline bool tetgenmesh::smarktest2ed(face& s) { + return ((((int*)((s).sh))[shmarkindex + 1] & (int)4) != 0); } // The last 4th bit of ((int *) ((s).sh))[shmarkindex+1] is flagged. -inline void tetgenmesh::smarktest3(face& s) -{ - ((int *) ((s).sh))[shmarkindex+1] = - (((int *)((s).sh))[shmarkindex+1] | (int) 8); +inline void tetgenmesh::smarktest3(face& s) { + ((int*)((s).sh))[shmarkindex + 1] = (((int*)((s).sh))[shmarkindex + 1] | (int)8); } -inline void tetgenmesh::sunmarktest3(face& s) -{ - ((int *) ((s).sh))[shmarkindex+1] = - (((int *)((s).sh))[shmarkindex+1] & ~(int)8); +inline void tetgenmesh::sunmarktest3(face& s) { + ((int*)((s).sh))[shmarkindex + 1] = (((int*)((s).sh))[shmarkindex + 1] & ~(int)8); } -inline bool tetgenmesh::smarktest3ed(face& s) -{ - return ((((int *) ((s).sh))[shmarkindex+1] & (int) 8) != 0); +inline bool tetgenmesh::smarktest3ed(face& s) { + return ((((int*)((s).sh))[shmarkindex + 1] & (int)8) != 0); } - // Each facet has a unique index (automatically indexed). Starting from '0'. -// We save this index in the same field of the shell type. +// We save this index in the same field of the shell type. -inline void tetgenmesh::setfacetindex(face& s, int value) -{ - ((int *) (s.sh))[shmarkindex + 2] = value; +inline void tetgenmesh::setfacetindex(face& s, int value) { + ((int*)(s.sh))[shmarkindex + 2] = value; } -inline int tetgenmesh::getfacetindex(face& s) -{ - return ((int *) (s.sh))[shmarkindex + 2]; -} +inline int tetgenmesh::getfacetindex(face& s) { return ((int*)(s.sh))[shmarkindex + 2]; } /////////////////////////////////////////////////////////////////////////////// // // @@ -3012,26 +2875,23 @@ inline int tetgenmesh::getfacetindex(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. +// 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(); - // Initialize. - for (int i = 0; i < 4; i++) { - ((shellface *) (t).tet[9])[i] = NULL; +inline void tetgenmesh::tsbond(triface& t, face& s) { + if ((t).tet[9] == NULL) { + // Allocate space for this tet. + (t).tet[9] = (tetrahedron)tet2subpool->alloc(); + // Initialize. + for (int i = 0; i < 4; i++) { + ((shellface*)(t).tet[9])[i] = NULL; + } } - } - // 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]); + // 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]); } // tspivot() finds a subface (s) abutting on the given tetrahdera (t). @@ -3039,54 +2899,48 @@ inline void tetgenmesh::tsbond(triface& t, face& s) // the subface s, and s and t must be at the same edge wth the same // orientation. -inline void tetgenmesh::tspivot(triface& t, face& s) -{ - if ((t).tet[9] == NULL) { - (s).sh = NULL; - return; - } - // Get the attached subface s. - sdecode(((shellface *) (t).tet[9])[(t).ver & 3], (s)); - (s).shver = tspivottbl[t.ver][s.shver]; +inline void tetgenmesh::tspivot(triface& t, face& s) { + if ((t).tet[9] == NULL) { + (s).sh = NULL; + return; + } + // Get the attached subface s. + sdecode(((shellface*)(t).tet[9])[(t).ver & 3], (s)); + (s).shver = tspivottbl[t.ver][s.shver]; } // Quickly check if the handle (t, v) is a subface. -#define issubface(t) \ - ((t).tet[9] && ((t).tet[9])[(t).ver & 3]) +#define issubface(t) ((t).tet[9] && ((t).tet[9])[(t).ver & 3]) // stpivot() finds a tetrahedron (t) abutting a given subface (s). // Return the t (if it exists) with the same edge and the same // orientation of s. -inline void tetgenmesh::stpivot(face& s, triface& t) -{ - decode((tetrahedron) s.sh[9 + (s.shver & 1)], t); - if ((t).tet == NULL) { - return; - } - (t).ver = stpivottbl[t.ver][s.shver]; +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]; } // Quickly check if this subface is attached to a tetrahedron. -#define isshtet(s) \ - ((s).sh[9 + ((s).shver & 1)]) +#define isshtet(s) ((s).sh[9 + ((s).shver & 1)]) // tsdissolve() dissolve a bond (from the tetrahedron side). -inline void tetgenmesh::tsdissolve(triface& t) -{ - if ((t).tet[9] != NULL) { - ((shellface *) (t).tet[9])[(t).ver & 3] = NULL; - } +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; +inline void tetgenmesh::stdissolve(face& s) { + (s).sh[9] = NULL; + (s).sh[10] = NULL; } /////////////////////////////////////////////////////////////////////////////// @@ -3097,36 +2951,29 @@ inline void tetgenmesh::stdissolve(face& s) // ssbond() bond a subface to a subsegment. -inline void tetgenmesh::ssbond(face& s, face& edge) -{ - s.sh[6 + (s.shver >> 1)] = sencode(edge); - edge.sh[0] = sencode(s); +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); +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[6 + (s.shver >> 1)] = NULL; -} +inline void tetgenmesh::ssdissolve(face& s) { s.sh[6 + (s.shver >> 1)] = NULL; } // sspivot() finds a subsegment abutting a subface. -inline void tetgenmesh::sspivot(face& s, face& edge) -{ - sdecode((shellface) s.sh[6 + (s.shver >> 1)], edge); +inline void tetgenmesh::sspivot(face& s, face& edge) { + sdecode((shellface)s.sh[6 + (s.shver >> 1)], edge); } // Quickly check if the edge is a subsegment. -#define isshsubseg(s) \ - ((s).sh[6 + ((s).shver >> 1)]) +#define isshsubseg(s) ((s).sh[6 + ((s).shver >> 1)]) /////////////////////////////////////////////////////////////////////////////// // // @@ -3134,54 +2981,41 @@ inline void tetgenmesh::sspivot(face& s, face& edge) // // /////////////////////////////////////////////////////////////////////////////// -inline void tetgenmesh::tssbond1(triface& t, face& s) -{ - if ((t).tet[8] == NULL) { - // Allocate space for this tet. - (t).tet[8] = (tetrahedron) tet2segpool->alloc(); - // Initialization. - for (int i = 0; i < 6; i++) { - ((shellface *) (t).tet[8])[i] = NULL; +inline void tetgenmesh::tssbond1(triface& t, face& s) { + if ((t).tet[8] == NULL) { + // Allocate space for this tet. + (t).tet[8] = (tetrahedron)tet2segpool->alloc(); + // Initialization. + for (int i = 0; i < 6; i++) { + ((shellface*)(t).tet[8])[i] = NULL; + } } - } - ((shellface *) (t).tet[8])[ver2edge[(t).ver]] = sencode((s)); + ((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::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])[ver2edge[(t).ver]] = NULL; - } +inline void tetgenmesh::tssdissolve1(triface& t) { + if ((t).tet[8] != NULL) { + ((shellface*)(t).tet[8])[ver2edge[(t).ver]] = NULL; + } } -inline void tetgenmesh::sstdissolve1(face& s) -{ - ((tetrahedron *) (s).sh)[9] = 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; - } +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; + } } // Quickly check whether 't' is a segment or not. -#define issubseg(t) \ - ((t).tet[8] && ((t).tet[8])[ver2edge[(t).ver]]) +#define issubseg(t) ((t).tet[8] && ((t).tet[8])[ver2edge[(t).ver]]) -inline void tetgenmesh::sstpivot1(face& s, triface& t) -{ - decode((tetrahedron) s.sh[9], t); -} +inline void tetgenmesh::sstpivot1(face& s, triface& t) { decode((tetrahedron)s.sh[9], t); } /////////////////////////////////////////////////////////////////////////////// // // @@ -3189,214 +3023,177 @@ inline void tetgenmesh::sstpivot1(face& s, triface& t) // // /////////////////////////////////////////////////////////////////////////////// -inline int tetgenmesh::pointmark(point pt) { - return ((int *) (pt))[pointmarkindex]; -} - -inline void tetgenmesh::setpointmark(point pt, int value) { - ((int *) (pt))[pointmarkindex] = value; -} +inline int tetgenmesh::pointmark(point pt) { return ((int*)(pt))[pointmarkindex]; } +inline void tetgenmesh::setpointmark(point pt, int value) { ((int*)(pt))[pointmarkindex] = value; } // These two primitives set and read the type of the point. inline enum tetgenmesh::verttype tetgenmesh::pointtype(point pt) { - return (enum verttype) (((int *) (pt))[pointmarkindex + 1] >> (int) 8); + return (enum verttype)(((int*)(pt))[pointmarkindex + 1] >> (int)8); } inline void tetgenmesh::setpointtype(point pt, enum verttype value) { - ((int *) (pt))[pointmarkindex + 1] = - ((int) value << 8) + (((int *) (pt))[pointmarkindex + 1] & (int) 255); + ((int*)(pt))[pointmarkindex + 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 int tetgenmesh::pointgeomtag(point pt) { return ((int*)(pt))[pointmarkindex + 2]; } inline void tetgenmesh::setpointgeomtag(point pt, int value) { - ((int *) (pt))[pointmarkindex + 2] = 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 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; + pt[pointparamindex + i] = value; } // pinfect(), puninfect(), pinfected() -- primitives to flag or unflag // a point. The last bit of the integer '[pointindex+1]' is flagged. -inline void tetgenmesh::pinfect(point pt) { - ((int *) (pt))[pointmarkindex + 1] |= (int) 1; -} +inline void tetgenmesh::pinfect(point pt) { ((int*)(pt))[pointmarkindex + 1] |= (int)1; } -inline void tetgenmesh::puninfect(point pt) { - ((int *) (pt))[pointmarkindex + 1] &= ~(int) 1; -} +inline void tetgenmesh::puninfect(point pt) { ((int*)(pt))[pointmarkindex + 1] &= ~(int)1; } inline bool tetgenmesh::pinfected(point pt) { - return (((int *) (pt))[pointmarkindex + 1] & (int) 1) != 0; + return (((int*)(pt))[pointmarkindex + 1] & (int)1) != 0; } -// pmarktest(), punmarktest(), pmarktested() -- more primitives to -// flag or unflag a point. +// pmarktest(), punmarktest(), pmarktested() -- more primitives to +// flag or unflag a point. -inline void tetgenmesh::pmarktest(point pt) { - ((int *) (pt))[pointmarkindex + 1] |= (int) 2; -} +inline void tetgenmesh::pmarktest(point pt) { ((int*)(pt))[pointmarkindex + 1] |= (int)2; } -inline void tetgenmesh::punmarktest(point pt) { - ((int *) (pt))[pointmarkindex + 1] &= ~(int) 2; -} +inline void tetgenmesh::punmarktest(point pt) { ((int*)(pt))[pointmarkindex + 1] &= ~(int)2; } inline bool tetgenmesh::pmarktested(point pt) { - return (((int *) (pt))[pointmarkindex + 1] & (int) 2) != 0; + return (((int*)(pt))[pointmarkindex + 1] & (int)2) != 0; } -inline void tetgenmesh::pmarktest2(point pt) { - ((int *) (pt))[pointmarkindex + 1] |= (int) 4; -} +inline void tetgenmesh::pmarktest2(point pt) { ((int*)(pt))[pointmarkindex + 1] |= (int)4; } -inline void tetgenmesh::punmarktest2(point pt) { - ((int *) (pt))[pointmarkindex + 1] &= ~(int) 4; -} +inline void tetgenmesh::punmarktest2(point pt) { ((int*)(pt))[pointmarkindex + 1] &= ~(int)4; } inline bool tetgenmesh::pmarktest2ed(point pt) { - return (((int *) (pt))[pointmarkindex + 1] & (int) 4) != 0; + return (((int*)(pt))[pointmarkindex + 1] & (int)4) != 0; } -inline void tetgenmesh::pmarktest3(point pt) { - ((int *) (pt))[pointmarkindex + 1] |= (int) 8; -} +inline void tetgenmesh::pmarktest3(point pt) { ((int*)(pt))[pointmarkindex + 1] |= (int)8; } -inline void tetgenmesh::punmarktest3(point pt) { - ((int *) (pt))[pointmarkindex + 1] &= ~(int) 8; -} +inline void tetgenmesh::punmarktest3(point pt) { ((int*)(pt))[pointmarkindex + 1] &= ~(int)8; } inline bool tetgenmesh::pmarktest3ed(point pt) { - return (((int *) (pt))[pointmarkindex + 1] & (int) 8) != 0; + return (((int*)(pt))[pointmarkindex + 1] & (int)8) != 0; } // These following primitives set and read a pointer to a tetrahedron // a subface/subsegment, a point, or a tet of background mesh. inline tetgenmesh::tetrahedron tetgenmesh::point2tet(point pt) { - return ((tetrahedron *) (pt))[point2simindex]; + return ((tetrahedron*)(pt))[point2simindex]; } inline void tetgenmesh::setpoint2tet(point pt, tetrahedron value) { - ((tetrahedron *) (pt))[point2simindex] = value; + ((tetrahedron*)(pt))[point2simindex] = value; } inline tetgenmesh::point tetgenmesh::point2ppt(point pt) { - return (point) ((tetrahedron *) (pt))[point2simindex + 1]; + return (point)((tetrahedron*)(pt))[point2simindex + 1]; } inline void tetgenmesh::setpoint2ppt(point pt, point value) { - ((tetrahedron *) (pt))[point2simindex + 1] = (tetrahedron) value; + ((tetrahedron*)(pt))[point2simindex + 1] = (tetrahedron)value; } inline tetgenmesh::shellface tetgenmesh::point2sh(point pt) { - return (shellface) ((tetrahedron *) (pt))[point2simindex + 2]; + return (shellface)((tetrahedron*)(pt))[point2simindex + 2]; } inline void tetgenmesh::setpoint2sh(point pt, shellface value) { - ((tetrahedron *) (pt))[point2simindex + 2] = (tetrahedron) value; + ((tetrahedron*)(pt))[point2simindex + 2] = (tetrahedron)value; } - inline tetgenmesh::tetrahedron tetgenmesh::point2bgmtet(point pt) { - return ((tetrahedron *) (pt))[point2simindex + 3]; + return ((tetrahedron*)(pt))[point2simindex + 3]; } inline void tetgenmesh::setpoint2bgmtet(point pt, tetrahedron value) { - ((tetrahedron *) (pt))[point2simindex + 3] = value; + ((tetrahedron*)(pt))[point2simindex + 3] = value; } - // The primitives for saving and getting the insertion radius. -inline void tetgenmesh::setpointinsradius(point pt, REAL value) -{ - pt[pointinsradiusindex] = value; -} +inline void tetgenmesh::setpointinsradius(point pt, REAL value) { pt[pointinsradiusindex] = value; } -inline REAL tetgenmesh::getpointinsradius(point pt) -{ - return pt[pointinsradiusindex]; -} +inline REAL tetgenmesh::getpointinsradius(point pt) { return pt[pointinsradiusindex]; } inline bool tetgenmesh::issteinerpoint(point pt) { - return (pointtype(pt) == FREESEGVERTEX) || (pointtype(pt) == FREEFACETVERTEX) - || (pointtype(pt) == FREEVOLVERTEX); + return (pointtype(pt) == FREESEGVERTEX) || (pointtype(pt) == FREEFACETVERTEX) || + (pointtype(pt) == FREEVOLVERTEX); } // point2tetorg() Get the tetrahedron whose origin is the point. -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 { - searchtet.ver = 0; - } +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 { + searchtet.ver = 0; + } } // 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 { - searchsh.shver = 4; - } +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 { + searchsh.shver = 4; + } } // farsorg() Return the origin of the subsegment. // farsdest() Return the destination of the subsegment. -inline tetgenmesh::point tetgenmesh::farsorg(face& s) -{ - face travesh, neighsh; +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); + 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); } -inline tetgenmesh::point tetgenmesh::farsdest(face& s) -{ - face travesh, neighsh; +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); + 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); } /////////////////////////////////////////////////////////////////////////////// @@ -3406,33 +3203,23 @@ inline tetgenmesh::point tetgenmesh::farsdest(face& s) /////////////////////////////////////////////////////////////////////////////// // 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]; +inline REAL tetgenmesh::dot(REAL* v1, REAL* v2) { + return v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2]; } // cross() computes the cross product: n = v1 cross v2. -inline void tetgenmesh::cross(REAL* v1, REAL* v2, REAL* n) -{ - n[0] = v1[1] * v2[2] - v2[1] * v1[2]; - n[1] = -(v1[0] * v2[2] - v2[0] * v1[2]); - n[2] = v1[0] * v2[1] - v2[0] * v1[1]; +inline void tetgenmesh::cross(REAL* v1, REAL* v2, REAL* n) { + n[0] = v1[1] * v2[2] - v2[1] * v1[2]; + n[1] = -(v1[0] * v2[2] - v2[0] * v1[2]); + n[2] = v1[0] * v2[1] - v2[0] * v1[1]; } // distance() 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]) + - (p2[1] - p1[1]) * (p2[1] - p1[1]) + - (p2[2] - p1[2]) * (p2[2] - p1[2])); -} - -inline REAL tetgenmesh::norm2(REAL x, REAL y, REAL z) -{ - return (x) * (x) + (y) * (y) + (z) * (z); +inline REAL tetgenmesh::distance(REAL* p1, REAL* p2) { + return sqrt((p2[0] - p1[0]) * (p2[0] - p1[0]) + (p2[1] - p1[1]) * (p2[1] - p1[1]) + + (p2[2] - p1[2]) * (p2[2] - p1[2])); } - +inline REAL tetgenmesh::norm2(REAL x, REAL y, REAL z) { return (x) * (x) + (y) * (y) + (z) * (z); } #endif // #ifndef tetgenH -