Select Git revision
Forked from
gmsh / gmsh
Source project has a limited visibility.
tetgen.cxx 1.09 MiB
///////////////////////////////////////////////////////////////////////////////
// //
// TetGen //
// //
// A Quality Tetrahedral Mesh Generator and 3D Delaunay Triangulator //
// //
// Version 1.5 //
// February 21, 2012 //
// //
// PRE-RELEASE TEST CODE. //
// PLEASE DO NOT DISTRIBUTE !! //
// PLEASE HELP ME TO IMPROVE IT !! //
// //
// Copyright (C) 2002--2012 //
// Hang Si //
// Research Group: Numerical Mathematics and Scientific Computing //
// Weierstrass Institute for Applied Analysis and Stochastics (WIAS) //
// Mohrenstr. 39, 10117 Berlin, Germany //
// Hang.Si@wias-berlin.de //
// //
// TetGen is freely available through the website: http://www.tetgen.org. //
// It may be copied, modified, and redistributed for non-commercial use. //
// Please consult the file LICENSE for the detailed copyright notices. //
// //
///////////////////////////////////////////////////////////////////////////////
#include "tetgen.h"
//// io_cxx ///////////////////////////////////////////////////////////////////
//// ////
//// ////
///////////////////////////////////////////////////////////////////////////////
// //
// load_node_call() Read a list of points from a file. //
// //
// 'infile' is the file handle contains the node list. It may point to a //
// .node, or .poly or .smesh file. 'markers' indicates each node contains an //
// additional marker (integer) or not. 'uvflag' indicates each node contains //
// u,v coordinates or not. It is reuqired by a PSC. 'infilename' is the name //
// of the file being read, it is only used in error messages. //
// //
// The 'firstnumber' (0 or 1) is automatically determined by the number of //
// the first index of the first point. //
// //
///////////////////////////////////////////////////////////////////////////////
bool tetgenio::load_node_call(FILE* infile, int markers, 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(1);
}
if (numberofpointattributes > 0) {
pointattributelist = new REAL[numberofpoints * numberofpointattributes];
if (pointattributelist == (REAL *) NULL) {
terminatetetgen(1);
}
}
if (markers) {
pointmarkerlist = new int[numberofpoints];
if (pointmarkerlist == (int *) NULL) {
terminatetetgen(1);
}
}
if (uvflag) {
pointparamlist = new pointparam[numberofpoints];
if (pointparamlist == NULL) {
terminatetetgen(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;
}
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 (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;
}
return true;
}
///////////////////////////////////////////////////////////////////////////////
// //
// load_node() Load a list of points from a .node file. //
// //
///////////////////////////////////////////////////////////////////////////////
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 (reuqired by a PSC).
// Read the first line of the file.
stringptr = readnumberline(inputline, infile, innodefilename);
// Does this file contain an index colume?
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);
fclose(infile);
return okflag;
}
///////////////////////////////////////////////////////////////////////////////
// //
// load_edge() Load a list of edges from a .edge file. //
// //
///////////////////////////////////////////////////////////////////////////////
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(1);
}
stringptr = findnextnumber(stringptr);
if (*stringptr == '\0') {
markers = 0; // Default value.
} else {
markers = (int) strtol (stringptr, &stringptr, 0);
}
if (markers > 0) {
edgemarkerlist = new int[numberofedges];
}
}
// Read the list of 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(1);
}
corner = (int) strtol(stringptr, &stringptr, 0);
if (corner < firstnumber || corner >= numberofpoints + firstnumber) {
printf("Error: Edge %d has an invalid vertex index.\n",
i + firstnumber);
terminatetetgen(1);
}
edgelist[index++] = corner;
}
// Read the edge marker if it has.
if (markers) {
stringptr = findnextnumber(stringptr);
edgemarkerlist[i] = (int) strtol(stringptr, &stringptr, 0);
}
}
fclose(infile);
return true;
}
///////////////////////////////////////////////////////////////////////////////
// //
// load_face() Load a list of faces (triangles) from a .face file. //
// //
///////////////////////////////////////////////////////////////////////////////
bool tetgenio::load_face(char* filebasename)
{
FILE *infile;
char infilename[FILENAMESIZE];
char inputline[INPUTLINESIZE];
char *stringptr;
REAL attrib;
int markers, corner;
int index;
int i, j;
strcpy(infilename, filebasename);
strcat(infilename, ".face");
infile = fopen(infilename, "r");
if (infile != (FILE *) NULL) {
printf("Opening %s.\n", infilename);
} else {
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(1);
}
if (markers) {
trifacemarkerlist = new int[numberoftrifaces];
if (trifacemarkerlist == (int *) NULL) {
terminatetetgen(1);
}
}
}
// Read the list of faces.
index = 0;
for (i = 0; i < numberoftrifaces; i++) {
// Read face index and the face's three corners.
stringptr = readnumberline(inputline, infile, infilename);
for (j = 0; j < 3; j++) {
stringptr = findnextnumber(stringptr);
if (*stringptr == '\0') {
printf("Error: Face %d is missing vertex %d in %s.\n",
i + firstnumber, j + 1, infilename);
terminatetetgen(1);
}
corner = (int) strtol(stringptr, &stringptr, 0);
if (corner < firstnumber || corner >= numberofpoints + firstnumber) {
printf("Error: Face %d has an invalid vertex index.\n",
i + firstnumber);
terminatetetgen(1);
}
trifacelist[index++] = corner;
}
// Read the boundary marker if it exists.
if (markers) {
stringptr = findnextnumber(stringptr);
if (*stringptr == '\0') {
attrib = 0.0;
} else {
attrib = (REAL) strtod(stringptr, &stringptr);
}
trifacemarkerlist[i] = (int) attrib;
}
}
fclose(infile);
return true;
}
///////////////////////////////////////////////////////////////////////////////
// //
// load_tet() Load a list of tetrahedra from a .ele file. //
// //
///////////////////////////////////////////////////////////////////////////////
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(1);
}
// Allocate memory for output tetrahedron attributes if necessary.
if (numberoftetrahedronattributes > 0) {
tetrahedronattributelist = new REAL[numberoftetrahedra *
numberoftetrahedronattributes];
if (tetrahedronattributelist == (REAL *) NULL) {
terminatetetgen(1);
}
}
// Read the list of tetrahedra.
index = 0;
attribindex = 0;
for (i = 0; i < numberoftetrahedra; i++) {
// Read tetrahedron index and the tetrahedron's corners.
stringptr = readnumberline(inputline, infile, infilename);
for (j = 0; j < numberofcorners; j++) {
stringptr = findnextnumber(stringptr);
if (*stringptr == '\0') {
printf("Error: Tetrahedron %d is missing vertex %d in %s.\n",
i + firstnumber, j + 1, infilename);
terminatetetgen(1);
}
corner = (int) strtol(stringptr, &stringptr, 0);
if (corner < firstnumber || corner >= numberofpoints + firstnumber) {
printf("Error: Tetrahedron %d has an invalid vertex index.\n",
i + firstnumber);
terminatetetgen(1);
}
tetrahedronlist[index++] = corner;
}
// Read the tetrahedron's attributes.
for (j = 0; j < numberoftetrahedronattributes; j++) {
stringptr = findnextnumber(stringptr);
if (*stringptr == '\0') {
attrib = 0.0;
} else {
attrib = (REAL) strtod(stringptr, &stringptr);
}
tetrahedronattributelist[attribindex++] = attrib;
}
}
fclose(infile);
return true;
}
///////////////////////////////////////////////////////////////////////////////
// //
// load_vol() Load a list of volume constraints from a .vol file. //
// //
///////////////////////////////////////////////////////////////////////////////
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;
}
tetrahedronvolumelist = new REAL[volelements];
if (tetrahedronvolumelist == (REAL *) NULL) {
terminatetetgen(1);
}
// Read the list of volume constraints.
for (i = 0; i < volelements; i++) {
stringptr = readnumberline(inputline, infile, infilename);
stringptr = findnextnumber(stringptr);
if (*stringptr == '\0') {
volume = -1.0; // No constraint on this tetrahedron.
} else {
volume = (REAL) strtod(stringptr, &stringptr);
}
tetrahedronvolumelist[i] = volume;
}
fclose(infile);
return true;
}
///////////////////////////////////////////////////////////////////////////////
// //
// load_var() Load constraints applied on facets, segments, and nodes //
// from a .var file. //
// //
///////////////////////////////////////////////////////////////////////////////
bool tetgenio::load_var(char* filebasename)
{
FILE *infile;
char varfilename[FILENAMESIZE];
char inputline[INPUTLINESIZE];
char *stringptr;
int index;
int i;
// Variant constraints are saved in file "filename.var".
strcpy(varfilename, filebasename);
strcat(varfilename, ".var");
infile = fopen(varfilename, "r");
if (infile != (FILE *) NULL) {
printf("Opening %s.\n", varfilename);
} else {
// No such file. Ignore it without a message.
return false;
}
// Read the facet constraint section.
stringptr = readnumberline(inputline, infile, varfilename);
if (*stringptr != '\0') {
numberoffacetconstraints = (int) strtol (stringptr, &stringptr, 0);
} else {
numberoffacetconstraints = 0;
}
if (numberoffacetconstraints > 0) {
// Initialize 'facetconstraintlist'.
facetconstraintlist = new REAL[numberoffacetconstraints * 2];
index = 0;
for (i = 0; i < numberoffacetconstraints; i++) {
stringptr = readnumberline(inputline, infile, varfilename);
stringptr = findnextnumber(stringptr);
if (*stringptr == '\0') {
printf("Error: facet constraint %d has no facet marker.\n",
firstnumber + i);
break;
} else {
facetconstraintlist[index++] = (REAL) strtod(stringptr, &stringptr);
}
stringptr = findnextnumber(stringptr);
if (*stringptr == '\0') {
printf("Error: facet constraint %d has no maximum area bound.\n",
firstnumber + i);
break;
} else {
facetconstraintlist[index++] = (REAL) strtod(stringptr, &stringptr);
}
}
if (i < numberoffacetconstraints) {
// This must be caused by an error.
fclose(infile);
return false;
}
}
// Read the segment constraint section.
stringptr = readnumberline(inputline, infile, varfilename);
if (*stringptr != '\0') {
numberofsegmentconstraints = (int) strtol (stringptr, &stringptr, 0);
} else {
numberofsegmentconstraints = 0;
}
if (numberofsegmentconstraints > 0) {
// Initialize 'segmentconstraintlist'.
segmentconstraintlist = new REAL[numberofsegmentconstraints * 3];
index = 0;
for (i = 0; i < numberofsegmentconstraints; i++) {
stringptr = readnumberline(inputline, infile, varfilename);
stringptr = findnextnumber(stringptr);
if (*stringptr == '\0') {
printf("Error: segment constraint %d has no frist endpoint.\n",
firstnumber + i);
break;
} else {
segmentconstraintlist[index++] = (REAL) strtod(stringptr, &stringptr);
}
stringptr = findnextnumber(stringptr);
if (*stringptr == '\0') {
printf("Error: segment constraint %d has no second endpoint.\n",
firstnumber + i);
break;
} else {
segmentconstraintlist[index++] = (REAL) strtod(stringptr, &stringptr);
}
stringptr = findnextnumber(stringptr);
if (*stringptr == '\0') {
printf("Error: segment constraint %d has no maximum length bound.\n",
firstnumber + i);
break;
} else {
segmentconstraintlist[index++] = (REAL) strtod(stringptr, &stringptr);
}
}
if (i < numberofsegmentconstraints) {
// This must be caused by an error.
fclose(infile);
return false;
}
}
fclose(infile);
return true;
}
///////////////////////////////////////////////////////////////////////////////
// //
// load_mtr() Load a size specification map from a .mtr file. //
// //
///////////////////////////////////////////////////////////////////////////////
bool tetgenio::load_mtr(char* filebasename)
{
FILE *infile;
char mtrfilename[FILENAMESIZE];
char inputline[INPUTLINESIZE];
char *stringptr;
REAL mtr;
int ptnum;
int mtrindex;
int i, j;
strcpy(mtrfilename, filebasename);
strcat(mtrfilename, ".mtr");
infile = fopen(mtrfilename, "r");
if (infile != (FILE *) NULL) {
printf("Opening %s.\n", mtrfilename);
} else {
// No such file. Return.
return false;
}
// Read the number of points.
stringptr = readnumberline(inputline, infile, mtrfilename);
ptnum = (int) strtol (stringptr, &stringptr, 0);
if (ptnum != numberofpoints) {
printf(" !! Point numbers are not equal. Ignored.\n");
fclose(infile);
return false;
}
// Read the number of columns (1, 3, or 6).
stringptr = findnextnumber(stringptr); // Skip number of points.
if (*stringptr != '\0') {
numberofpointmtrs = (int) strtol (stringptr, &stringptr, 0);
}
if (numberofpointmtrs == 0) {
// Column number doesn't match. Set a default number (1).
numberofpointmtrs = 1;
}
// Allocate space for pointmtrlist.
pointmtrlist = new REAL[numberofpoints * numberofpointmtrs];
if (pointmtrlist == (REAL *) NULL) {
terminatetetgen(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(1);
}
mtr = (REAL) strtod(stringptr, &stringptr);
pointmtrlist[mtrindex++] = mtr;
stringptr = findnextnumber(stringptr);
}
}
fclose(infile);
return true;
}
///////////////////////////////////////////////////////////////////////////////
// //
// load_poly() Load a PL complex from a .poly or a .smesh file. //
// //
///////////////////////////////////////////////////////////////////////////////
bool tetgenio::load_poly(char* filebasename)
{
FILE *infile;
char inpolyfilename[FILENAMESIZE];
char insmeshfilename[FILENAMESIZE];
char inputline[INPUTLINESIZE];
char *stringptr, *infilename;
int smesh, markers, 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 {
printf("Opening %s.\n", inpolyfilename);
infilename = inpolyfilename;
}
// Initialize the default values.
mesh_dim = 3; // Three-dimemsional accoordinates.
numberofpointattributes = 0; // no point attribute.
markers = 0; // no boundary marker.
uvflag = 0; // no uv parameters (reuqired 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;
}
}
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);
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);
}
// 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;
}
}
// 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 {
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;
}
}
// 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;
}
}
} else {
// Read a PSLG from Triangle's poly file.
assert(mesh_dim == 2);
// A PSLG is a facet of a PLC.
numberoffacets = 1;
// Initialize the 'facetlist'.
facetlist = new facet[numberoffacets];
facetmarkerlist = (int *) NULL; // No facet markers.
f = &(facetlist[0]);
init(f);
// Read number of segments.
stringptr = readnumberline(inputline, infile, infilename);
// Segments are degenerate polygons.
f->numberofpolygons = (int) strtol (stringptr, &stringptr, 0);
if (f->numberofpolygons > 0) {
f->polygonlist = new polygon[f->numberofpolygons];
}
// Go through all segments, read in their vertices.
for (j = 0; j < f->numberofpolygons; j++) {
p = &(f->polygonlist[j]);
init(p);
// Read in a segment.
stringptr = readnumberline(inputline, infile, infilename);
stringptr = findnextnumber(stringptr); // Skip its index.
p->numberofvertices = 2; // A segment always has two vertices.
p->vertexlist = new int[p->numberofvertices];
p->vertexlist[0] = (int) strtol (stringptr, &stringptr, 0);
stringptr = findnextnumber(stringptr);
p->vertexlist[1] = (int) strtol (stringptr, &stringptr, 0);
}
// Read number of holes.
stringptr = readnumberline(inputline, infile, infilename);
f->numberofholes = (int) strtol (stringptr, &stringptr, 0);
if (f->numberofholes > 0) {
// Initialize 'f->holelist'.
f->holelist = new REAL[f->numberofholes * 3];
// Read the holes' coordinates.
for (j = 0; j < f->numberofholes; j++) {
// Read a 2D hole point.
stringptr = readnumberline(inputline, infile, infilename);
stringptr = findnextnumber(stringptr); // Skip its index.
f->holelist[j * 3] = (REAL) strtod (stringptr, &stringptr);
stringptr = findnextnumber(stringptr);
f->holelist[j * 3 + 1] = (REAL) strtod (stringptr, &stringptr);
f->holelist[j * 3 + 2] = 0.0; // The z-coord.
}
}
// The regions are skipped.
}
// End of reading poly/smesh file.
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. //
// //
///////////////////////////////////////////////////////////////////////////////
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++;
} 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);
// 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;
}
// Check whether read all points
if (iverts != nverts) {
printf("Expected %d vertices, but read only %d vertices in file %s\n",
nverts, iverts, infilename);
return false;
}
// Check whether read all faces
if (ifaces != nfaces) {
printf("Expected %d faces, but read only %d faces in file %s\n",
nfaces, ifaces, infilename);
return false;
}
return true;
}
///////////////////////////////////////////////////////////////////////////////
// //
// 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. //
// //
///////////////////////////////////////////////////////////////////////////////
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);
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);
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);
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);
// 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;
}
// Check whether read all points
if (iverts != nverts) {
printf("Expected %d vertices, but read only %d vertices in file %s\n",
nverts, iverts, infilename);
return false;
}
// Check whether read all faces
if (ifaces != nfaces) {
printf("Expected %d faces, but read only %d faces in file %s\n",
nfaces, ifaces, infilename);
return false;
}
return true;
}
///////////////////////////////////////////////////////////////////////////////
// //
// 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_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, "r"))) {
printf("Error: Unable to open file %s\n", infilename);
return false;
}
printf("Opening %s.\n", infilename);
// STL file has no number of points available. Use a list to read points.
plist = new tetgenmesh::arraypool(sizeof(double) * 3, 10);
while ((bufferp = readline(buffer, fp, &line_count)) != NULL) {
// The ASCII .stl file must start with the lower case keyword solid and
// 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);
}
}
}
}
}
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;
}
///////////////////////////////////////////////////////////////////////////////
// //
// 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. //
// //
///////////////////////////////////////////////////////////////////////////////
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 {
assert((j == 2) && (dimension == 2));
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 {
printf("A wrong smallest index (%d) was detected in file %s\n",
smallestidx, infilename);
return false;
}
return true;
}
///////////////////////////////////////////////////////////////////////////////
// //
// load_vtk() Load VTK surface mesh from file (.vtk ascii or binary). //
// //
// This function is contributed by: Bryn Lloyd, Computer Vision Laborator, //
// ETH, Zuerich. May 7, 2007. //
// //
///////////////////////////////////////////////////////////////////////////////
// Two inline functions used in read/write VTK files.
void swapBytes(unsigned char* var, int size)
{
int i = 0;
int j = size - 1;
char c;
while (i < j) {
c = var[i]; var[i] = var[j]; var[j] = c;
i++, j--;
}
}
bool testIsBigEndian()
{
short word = 0x4321;
if((*(char *)& word) != 0x21)
return true;
else
return false;
}
bool tetgenio::load_vtk(char* filebasename)
{
FILE *fp;
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;
}
///////////////////////////////////////////////////////////////////////////////
// //
// load_plc() Load a piecewise linear complex from file(s). //
// //
///////////////////////////////////////////////////////////////////////////////
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) {
load_edge(filebasename);
load_var(filebasename);
load_mtr(filebasename);
}
return success;
}
///////////////////////////////////////////////////////////////////////////////
// //
// load_mesh() Load a tetrahedral mesh from file(s). //
// //
///////////////////////////////////////////////////////////////////////////////
bool tetgenio::load_tetmesh(char* filebasename, int object)
{
bool success;
if (object == (int) tetgenbehavior::MEDIT) {
success = load_medit(filebasename, 1);
} else {
success = load_node(filebasename);
if (success) {
success = load_tet(filebasename);
}
if (success) {
load_face(filebasename);
load_edge(filebasename);
load_vol(filebasename);
}
}
if (success) {
load_var(filebasename);
load_mtr(filebasename);
}
return success;
}
///////////////////////////////////////////////////////////////////////////////
// //
// save_nodes() Save points to a .node file. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenio::save_nodes(char* filebasename)
{
FILE *fout;
char outnodefilename[FILENAMESIZE];
char outmtrfilename[FILENAMESIZE];
int i, j;
sprintf(outnodefilename, "%s.node", filebasename);
printf("Saving nodes to %s\n", outnodefilename);
fout = fopen(outnodefilename, "w");
fprintf(fout, "%d %d %d %d\n", numberofpoints, mesh_dim,
numberofpointattributes, pointmarkerlist != NULL ? 1 : 0);
for (i = 0; i < numberofpoints; i++) {
if (mesh_dim == 2) {
fprintf(fout, "%d %.16g %.16g", i + firstnumber, pointlist[i * 3],
pointlist[i * 3 + 1]);
} else {
fprintf(fout, "%d %.16g %.16g %.16g", i + firstnumber,
pointlist[i * 3], pointlist[i * 3 + 1], pointlist[i * 3 + 2]);
}
for (j = 0; j < numberofpointattributes; j++) {
fprintf(fout, " %.16g",
pointattributelist[i * numberofpointattributes + j]);
}
if (pointmarkerlist != NULL) {
fprintf(fout, " %d", pointmarkerlist[i]);
}
fprintf(fout, "\n");
}
fclose(fout);
// If the point metrics exist, output them to a .mtr file.
if ((numberofpointmtrs > 0) && (pointmtrlist != (REAL *) NULL)) {
sprintf(outmtrfilename, "%s.mtr", filebasename);
printf("Saving metrics to %s\n", outmtrfilename);
fout = fopen(outmtrfilename, "w");
fprintf(fout, "%d %d\n", numberofpoints, numberofpointmtrs);
for (i = 0; i < numberofpoints; i++) {
for (j = 0; j < numberofpointmtrs; j++) {
fprintf(fout, "%.16g ", pointmtrlist[i * numberofpointmtrs + j]);
}
fprintf(fout, "\n");
}
fclose(fout);
}
}
///////////////////////////////////////////////////////////////////////////////
// //
// save_elements() Save elements to a .ele 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_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);
}
///////////////////////////////////////////////////////////////////////////////
// //
// readline() Read a nonempty line from a file. //
// //
// A line is considered "nonempty" if it contains something more than white //
// spaces. If a line is considered empty, it will be dropped and the next //
// line will be read, this process ends until reaching the end-of-file or a //
// non-empty line. Return NULL if it is the end-of-file, otherwise, return //
// a pointer to the first non-whitespace character of the line. //
// //
///////////////////////////////////////////////////////////////////////////////
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;
}
///////////////////////////////////////////////////////////////////////////////
// //
// findnextfield() Find the next field of a string. //
// //
// Jumps past the current field by searching for whitespace or a comma, then //
// jumps past the whitespace or the comma to find the next field. //
// //
///////////////////////////////////////////////////////////////////////////////
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;
}
///////////////////////////////////////////////////////////////////////////////
// //
// readnumberline() Read a nonempty number line from a file. //
// //
// A line is considered "nonempty" if it contains something that looks like //
// a number. Comments (prefaced by `#') are ignored. //
// //
///////////////////////////////////////////////////////////////////////////////
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;
}
///////////////////////////////////////////////////////////////////////////////
// //
// findnextnumber() Find the next field of a number string. //
// //
// Jumps past the current field by searching for whitespace or a comma, then //
// jumps past the whitespace or the comma to find the next field that looks //
// like a number. //
// //
///////////////////////////////////////////////////////////////////////////////
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 /////////////////////////////////////////////////////////////
//// ////
//// ////
///////////////////////////////////////////////////////////////////////////////
// //
// syntax() Print list of command line switches. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenbehavior::syntax()
{
printf(" tetgen [-pYrq_a_AiS_T_dzfenvgKJBNEFICQVh] input_file\n");
printf(" -p Tetrahedralizes a piecewise linear complex (PLC).\n");
printf(" -Y No splitting of input boundaries (facets and segments).\n");
printf(" -r Reconstructs a previously generated mesh.\n");
printf(" -q Refines mesh (to improve mesh quality).\n");
printf(" -a Applies a maximum tetrahedron volume constraint.\n");
printf(" -A Assigns attributes to tetrahedra in different regions.\n");
printf(" -i Inserts a list of additional points into mesh.\n");
printf(" -S Specifies maximum number of added points.\n");
printf(" -T Sets a tolerance for coplanar test (default 1e-8).\n");
printf(" -d Detects self-intersections of facets of the PLC.\n");
printf(" -z Numbers all output items starting from zero.\n");
printf(" -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 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");
}
///////////////////////////////////////////////////////////////////////////////
// //
// usage() Print a brief instruction for using TetGen. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenbehavior::usage()
{
printf("TetGen\n");
printf("A Quality Tetrahedral Mesh Generator and 3D Delaunay ");
printf("Triangulator\n");
printf("Version 1.5 (February 21, 2012).\n");
printf("\n");
printf("Copyright (C) 2002 - 2012\n");
printf("Hang Si\n");
printf("Mohrenstr. 39, 10117 Berlin, Germany\n");
printf("Hang.Si@wias-berlin.de\n");
printf("\n");
printf("What Can TetGen Do?\n");
printf("\n");
printf(" TetGen generates exact Delaunay tetrahedralizations, exact\n");
printf(" constrained Delaunay tetrahedralizations, and quality ");
printf("tetrahedral\n meshes. The latter are nicely graded and whose ");
printf("tetrahedra have\n radius-edge ratio bounded, thus are suitable ");
printf("for finite element and\n finite volume analysis.\n");
printf("\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 and ");
printf("object.1.ele.\n");
printf("\n");
printf(" \'tetgen -p object\' reads a PLC from object.poly or object.");
printf("smesh (and\n possibly object.node) and writes its constrained ");
printf("Delaunay\n tetrahedralization to object.1.node, object.1.ele and ");
printf("object.1.face.\n");
printf("\n");
printf(" \'tetgen -pq1.414a.1 object\' reads a PLC from object.poly or\n");
printf(" object.smesh (and possibly object.node), generates a mesh ");
printf("whose\n tetrahedra have radius-edge ratio smaller than 1.414 and ");
printf("have volume\n of 0.1 or less, and writes the mesh to ");
printf("object.1.node, object.1.ele\n and object.1.face.\n");
printf("\n");
printf("Please send bugs/comments to Hang Si <si@wias-berlin.de>\n");
terminatetetgen(0);
}
///////////////////////////////////////////////////////////////////////////////
// //
// parse_commandline() Read the command line, identify switches, and set //
// up options and file names. //
// //
// 'argc' and 'argv' are the same parameters passed to the function main() //
// of a C/C++ program. They together represent the command line user invoked //
// from an environment in which TetGen is running. //
// //
// When TetGen is invoked from an environment. 'argc' is nonzero, switches //
// and input filename should be supplied as zero-terminated strings in //
// argv[0] through argv[argc - 1] and argv[0] shall be the name used to //
// invoke TetGen, i.e. "tetgen". Switches are previously started with a //
// dash '-' to identify them from the input filename. //
// //
// When TetGen is called from within another program. 'argc' is set to zero. //
// switches are given in one zero-terminated string (no previous dash is //
// required.), and 'argv' is a pointer points to this string. No input //
// filename is required (usually the input data has been directly created by //
// user in the 'tetgenio' structure). A default filename 'tetgen-tmpfile' //
// will be created for debugging output purpose. //
// //
///////////////////////////////////////////////////////////////////////////////
bool tetgenbehavior::parse_commandline(int argc, char **argv)
{
int startindex;
int increment;
int meshnumber;
int scount, ocount;
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, " ");
}
// Count the number of '-O' and '-o' be used.
scount = ocount = 0;
for (i = startindex; i < argc; i++) {
// Remember the command line switches.
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';
// Go to the next string directly.
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_ang_tol = (REAL) strtod(workstring, (char **) NULL);
}
} else if (argv[i][j] == 's') {
psc = 1;
} else if (argv[i][j] == 'r') {
refine++;
} else if (argv[i][j] == 'q') {
quality++;
if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) ||
(argv[i][j + 1] == '.')) {
k = 0;
while (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) ||
(argv[i][j + 1] == '.')) {
j++;
workstring[k] = argv[i][j];
k++;
}
workstring[k] = '\0';
if (quality == 1) { // -q#
minratio = (REAL) strtod(workstring, (char **) NULL);
} else if (quality == 2) { // -qq#
mindihedral = (REAL) strtod(workstring, (char **) NULL);
}
}
} else if (argv[i][j] == 'm') {
metric++;
} else if (argv[i][j] == 'Y') {
nobisect = 1;
if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) ||
(argv[i][j + 1] == '.')) {
k = 0;
while (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) ||
(argv[i][j + 1] == '.') || (argv[i][j + 1] == 'e')) {
j++;
workstring[k] = argv[i][j];
k++;
}
workstring[k] = '\0';
nobisect_param = (int) strtol(workstring, (char **) NULL, 0);
}
} else if (argv[i][j] == 'w') {
weighted = 1;
if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) ||
(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';
weighted_param = (int) strtol(workstring, (char **) NULL, 0);
}
} 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++;
} else if (argv[i][j] == 'l') {
incrflip = 1;
// Check if a smallest edge length is given.
if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) ||
(argv[i][j + 1] == '.')) {
k = 0;
while (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) ||
(argv[i][j + 1] == '.') || (argv[i][j + 1] == 'e') ||
(argv[i][j + 1] == '-') || (argv[i][j + 1] == '+')) {
j++;
workstring[k] = argv[i][j];
k++;
}
workstring[k] = '\0';
minedgelength = (REAL) strtod(workstring, (char **) NULL);
}
} else if (argv[i][j] == 'L') {
flipinsert++;
if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) ||
(argv[i][j + 1] == '.')) {
k = 0;
while (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) ||
(argv[i][j + 1] == '.')) {
j++;
workstring[k] = argv[i][j];
k++;
}
workstring[k] = '\0';
if (flipinsert == 1) { // -L
fliplinklevel = (int) strtol(workstring, (char **) NULL, 0);
} else if (flipinsert == 2) { // -LL
flipstarsize = (int) strtol(workstring, (char **) NULL, 0);
}
}
} else if (argv[i][j] == 'u') {
// Set the maximum btree node size, -u0 means do not use btree.
if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) ||
(argv[i][j + 1] == '.')) {
k = 0;
while (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) ||
(argv[i][j + 1] == '.')) {
j++;
workstring[k] = argv[i][j];
k++;
}
workstring[k] = '\0';
max_btreenode_size = (int) strtol(workstring, (char **) NULL, 0);
}
if (max_btreenode_size == 0) {
btree = 0;
}
} else if (argv[i][j] == 'U') {
hilbertcurve = 1;
btree = 0;
} else if (argv[i][j] == 'i') {
insertaddpoints = 1;
} else if (argv[i][j] == 'd') {
diagnose = 1;
} else if (argv[i][j] == 'c') {
convex = 1;
} else if (argv[i][j] == 'z') {
zeroindex = 1;
} else if (argv[i][j] == 'f') {
facesout = 1;
} 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] == 'M') {
nomerge = 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;
if (argv[i][j + 1] == '2') {
j++;
noelewritten = 2;
}
} else if (argv[i][j] == 'F') {
nofacewritten = 1;
} else if (argv[i][j] == 'I') {
noiterationnum = 1;
} else if (argv[i][j] == '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') {
ocount++;
if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) ||
(argv[i][j + 1] == '.')) {
k = 0;
while (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) ||
(argv[i][j + 1] == '.') || (argv[i][j + 1] == 'e') ||
(argv[i][j + 1] == '-') || (argv[i][j + 1] == '+')) {
j++;
workstring[k] = argv[i][j];
k++;
}
workstring[k] = '\0';
if (ocount == 1) { // -o#
optmaxdihedral = (REAL) strtod(workstring, (char **) NULL);
} else if (ocount == 2) { // -oo#
optminsmtdihed = (REAL) strtod(workstring, (char **) NULL);
} else if (ocount == 3) { // -ooo#
optminslidihed = (REAL) strtod(workstring, (char **) NULL);
}
}
} else if (argv[i][j] == 'O') {
scount++;
if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) ||
(argv[i][j + 1] == '.')) {
k = 0;
while (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) ||
(argv[i][j + 1] == '.') || (argv[i][j + 1] == 'e') ||
(argv[i][j + 1] == '-') || (argv[i][j + 1] == '+')) {
j++;
workstring[k] = argv[i][j];
k++;
}
workstring[k] = '\0';
if (scount == 1) { // -O
optlevel = (int) strtol(workstring, (char **) NULL, 0);
} else if (scount == 2) { // -OO
optpasses = (int) strtol(workstring, (char **) NULL, 0);
} else if (scount == 3) { // -OOO
optmaxfliplevel = (int) strtol(workstring, (char **) NULL, 0);
} else if (scount == 4) { // -OOOO
delmaxfliplevel = (int) strtol(workstring, (char **) NULL, 0);
} else if (scount == 5) { // -OOOOO (5 Os)
optmaxflipstarsize = (int) strtol(workstring, (char **) NULL, 0);
}
}
} else if (argv[i][j] == 'D') {
conforming = 1;
if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) ||
(argv[i][j + 1] == '.')) {
k = 0;
while (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) ||
(argv[i][j + 1] == '.') || (argv[i][j + 1] == 'e') ||
(argv[i][j + 1] == '-') || (argv[i][j + 1] == '+')) {
j++;
workstring[k] = argv[i][j];
k++;
}
workstring[k] = '\0';
reflevel = (int) strtol(workstring, (char **) NULL, 0);
}
} 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] == '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(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;
}
}
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;
}
// Detect improper combinations of switches.
if (plc && refine) {
printf("Error: Switch -r cannot use together with -p.\n");
return false;
}
if (refine && (plc || noiterationnum)) {
printf("Error: Switches %s cannot use together with -r.\n",
"-p, -d, and -I");
return false;
}
if ((refine || plc) && weighted) {
printf("Error: Switches -w cannot use together with -p or -r.\n");
return false;
}
// Be careful not to allocate space for element area constraints that
// will never be assigned any value (other than the default -1.0).
if (!refine && !plc) {
varvolume = 0;
}
// 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;
}
// 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.
}
}
}
if (ocount == 0) {
// No user-specified dihedral angle bound. Use default ones.
if (!quality) {
if (optmaxdihedral < 179.0) {
optmaxdihedral = 179.0;
}
if (optminsmtdihed < 179.999) {
optminsmtdihed = 179.999;
}
if (optminslidihed < 179.999) {
optminslidihed = 179.99;
}
}
}
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;
}
//// ////
//// ////
//// behavior_cxx /////////////////////////////////////////////////////////////
//// mempool_cxx //////////////////////////////////////////////////////////////
//// ////
//// ////
// Initialize fast lookup tables for mesh maniplulation primitives.
int tetgenmesh::mod12[36] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
int tetgenmesh::mod6[18] = {0, 1, 2, 3, 4, 5,
0, 1, 2, 3, 4, 5,
0, 1, 2, 3, 4, 5};
// Table 'edgepivot' takes an directed edge (version) as input, returns the
// inversed edge (version) of it.
int tetgenmesh::edgepivot[12] = {9, 6, 11, 4, 3, 7, 1, 5, 10, 0, 8, 2};
// The following four tables give the 12 permutations of the set {0,1,2,3}.
// An offset 4 is added to each element for a direct access of the points
// in the tetrahedron data structure.
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};
// The twelve versions correspond to six undirected edges. The following two
// 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};
// Table 'snextpivot' takes an edge version as input, returns the next edge
// version in the same edge ring.
int tetgenmesh::snextpivot[6] = {2, 5, 4, 1, 0, 3};
// The following three tables give the 6 permutations of the set {0,1,2}.
// An offset 3 is added to each element for a direct access of the points
// in the triangle data structure.
int tetgenmesh::sorgpivot [6] = {3, 4, 4, 5, 5, 3};
int tetgenmesh::sdestpivot[6] = {4, 3, 5, 4, 3, 5};
int tetgenmesh::sapexpivot[6] = {5, 5, 3, 3, 4, 4};
// Edge versions whose apex or opposite may be dummypoint.
int tetgenmesh::epivot[4] = {4, 5, 2, 11};
///////////////////////////////////////////////////////////////////////////////
// //
// restart() Deallocate all objects in this pool. //
// //
// The pool returns to a fresh state, like after it was initialized, except //
// that no memory is freed to the operating system. Rather, the previously //
// allocated blocks are ready to be used. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::arraypool::restart()
{
objects = 0l;
}
///////////////////////////////////////////////////////////////////////////////
// //
// poolinit() Initialize an arraypool for allocation of objects. //
// //
// Before the pool may be used, it must be initialized by this procedure. //
// After initialization, memory can be allocated and freed in this pool. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::arraypool::poolinit(int sizeofobject, int log2objperblk)
{
// Each object must be at least one byte long.
objectbytes = sizeofobject > 1 ? sizeofobject : 1;
log2objectsperblock = log2objperblk;
// Compute the number of objects in each block.
objectsperblock = ((int) 1) << log2objectsperblock;
// No memory has been allocated.
totalmemory = 0l;
// The top array has not been allocated yet.
toparray = (char **) NULL;
toparraylen = 0;
// Ready all indices to be allocated.
restart();
}
///////////////////////////////////////////////////////////////////////////////
// //
// arraypool() The constructor and destructor. //
// //
///////////////////////////////////////////////////////////////////////////////
tetgenmesh::arraypool::arraypool(int sizeofobject, int log2objperblk)
{
poolinit(sizeofobject, log2objperblk);
}
tetgenmesh::arraypool::~arraypool()
{
int i;
// Has anything been allocated at all?
if (toparray != (char **) NULL) {
// Walk through the top array.
for (i = 0; i < toparraylen; i++) {
// Check every pointer; NULLs may be scattered randomly.
if (toparray[i] != (char *) NULL) {
// Free an allocated block.
free((void *) toparray[i]);
}
}
// Free the top array.
free((void *) toparray);
}
// The top array is no longer allocated.
toparray = (char **) NULL;
toparraylen = 0;
objects = 0;
totalmemory = 0;
}
///////////////////////////////////////////////////////////////////////////////
// //
// getblock() Return (and perhaps create) the block containing the object //
// with a given index. //
// //
// This function takes care of allocating or resizing the top array if nece- //
// ssary, and of allocating the block if it hasn't yet been allocated. //
// //
// Return a pointer to the beginning of the block (NOT the object). //
// //
///////////////////////////////////////////////////////////////////////////////
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;
}
///////////////////////////////////////////////////////////////////////////////
// //
// lookup() Return the pointer to the object with a given index, or NULL //
// if the object's block doesn't exist yet. //
// //
///////////////////////////////////////////////////////////////////////////////
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);
}
///////////////////////////////////////////////////////////////////////////////
// //
// newindex() Allocate space for a fresh object from the pool. //
// //
///////////////////////////////////////////////////////////////////////////////
int tetgenmesh::arraypool::newindex(void **newptr)
{
void *newobject;
int newindex;
// Allocate an object at index 'firstvirgin'.
newindex = objects;
newobject = (void *) (getblock(objects) +
(objects & (objectsperblock - 1)) * objectbytes);
objects++;
// If 'newptr' is not NULL, use it to return a pointer to the object.
if (newptr != (void **) NULL) {
*newptr = newobject;
}
return newindex;
}
///////////////////////////////////////////////////////////////////////////////
// //
// memorypool() The constructors of memorypool. //
// //
///////////////////////////////////////////////////////////////////////////////
tetgenmesh::memorypool::memorypool()
{
firstblock = nowblock = (void **) NULL;
nextitem = (void *) NULL;
deaditemstack = (void *) NULL;
pathblock = (void **) NULL;
pathitem = (void *) NULL;
itemwordtype = POINTER;
alignbytes = 0;
itembytes = itemwords = 0;
itemsperblock = 0;
items = maxitems = 0l;
unallocateditems = 0;
pathitemsleft = 0;
}
tetgenmesh::memorypool::
memorypool(int bytecount, int itemcount, enum wordtype wtype, int alignment)
{
poolinit(bytecount, itemcount, wtype, alignment);
}
///////////////////////////////////////////////////////////////////////////////
// //
// ~memorypool() Free to the operating system all memory taken by a pool. //
// //
///////////////////////////////////////////////////////////////////////////////
tetgenmesh::memorypool::~memorypool()
{
while (firstblock != (void **) NULL) {
nowblock = (void **) *(firstblock);
free(firstblock);
firstblock = nowblock;
}
}
///////////////////////////////////////////////////////////////////////////////
// //
// poolinit() Initialize a pool of memory for allocation of items. //
// //
// A `pool' is created whose records have size at least `bytecount'. Items //
// will be allocated in `itemcount'-item blocks. Each item is assumed to be //
// a collection of words, and either pointers or floating-point values are //
// assumed to be the "primary" word type. (The "primary" word type is used //
// to determine alignment of items.) If `alignment' isn't zero, all items //
// will be `alignment'-byte aligned in memory. `alignment' must be either a //
// multiple or a factor of the primary word size; powers of two are safe. //
// `alignment' is normally used to create a few unused bits at the bottom of //
// each item's pointer, in which information may be stored. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::memorypool::
poolinit(int bytecount, int itemcount, enum wordtype wtype, int alignment)
{
int wordsize;
// Initialize values in the pool.
itemwordtype = wtype;
wordsize = (itemwordtype == POINTER) ? sizeof(void *) : sizeof(REAL);
// Find the proper alignment, which must be at least as large as:
// - The parameter `alignment'.
// - The primary word type, to avoid unaligned accesses.
// - 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(1);
}
// Set the next block pointer to NULL.
*(firstblock) = (void *) NULL;
restart();
}
///////////////////////////////////////////////////////////////////////////////
// //
// restart() Deallocate all items in this pool. //
// //
// The pool is returned to its starting state, except that no memory is //
// freed to the operating system. Rather, the previously allocated blocks //
// are ready to be reused. //
// //
///////////////////////////////////////////////////////////////////////////////
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;
}
///////////////////////////////////////////////////////////////////////////////
// //
// alloc() Allocate space for an item. //
// //
///////////////////////////////////////////////////////////////////////////////
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(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.
if (itemwordtype == POINTER) {
nextitem = (void *) ((void **) nextitem + itemwords);
} else {
nextitem = (void *) ((REAL *) nextitem + itemwords);
}
unallocateditems--;
maxitems++;
}
items++;
return newitem;
}
///////////////////////////////////////////////////////////////////////////////
// //
// dealloc() Deallocate space for an item. //
// //
// The deallocated space is stored in a queue for later reuse. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::memorypool::dealloc(void *dyingitem)
{
// Push freshly killed item onto stack.
*((void **) dyingitem) = deaditemstack;
deaditemstack = dyingitem;
items--;
}
///////////////////////////////////////////////////////////////////////////////
// //
// traversalinit() Prepare to traverse the entire list of items. //
// //
// This routine is used in conjunction with traverse(). //
// //
///////////////////////////////////////////////////////////////////////////////
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;
}
///////////////////////////////////////////////////////////////////////////////
// //
// traverse() Find the next item in the list. //
// //
// This routine is used in conjunction with traversalinit(). Be forewarned //
// that this routine successively returns all items in the list, including //
// deallocated ones on the deaditemqueue. It's up to you to figure out which //
// ones are actually dead. It can usually be done more space-efficiently by //
// a routine that knows something about the structure of the item. //
// //
///////////////////////////////////////////////////////////////////////////////
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.
if (itemwordtype == POINTER) {
pathitem = (void *) ((void **) pathitem + itemwords);
} else {
pathitem = (void *) ((REAL *) pathitem + itemwords);
}
pathitemsleft--;
return newitem;
}
///////////////////////////////////////////////////////////////////////////////
// //
// makeindex2pointmap() Create a map from index to vertices. //
// //
// 'idx2verlist' returns the created map. Traverse all vertices, a pointer //
// to each vertex is set into the array. The pointer to the first vertex is //
// saved in 'idx2verlist[0]'. Don't forget to minus 'in->firstnumber' when //
// to get the vertex form its index. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::makeindex2pointmap(point*& idx2verlist)
{
point pointloop;
int idx;
if (b->verbose > 1) {
printf(" Constructing mapping from indices to points.\n");
}
idx2verlist = new point[points->items + 1];
points->traversalinit();
pointloop = pointtraverse();
idx = in->firstnumber;;
while (pointloop != (point) NULL) {
idx2verlist[idx++] = pointloop;
pointloop = pointtraverse();
}
}
///////////////////////////////////////////////////////////////////////////////
// //
// makesubfacemap() Create a map from vertex to subfaces incident at it. //
// //
// The map is returned in two arrays 'idx2faclist' and 'facperverlist'. All //
// subfaces incident at i-th vertex (i is counted from 0) are found in the //
// array facperverlist[j], where idx2faclist[i] <= j < idx2faclist[i + 1]. //
// Each entry in facperverlist[j] is a subface whose origin is the vertex. //
// //
// NOTE: These two arrays will be created inside this routine, don't forget //
// to free them after using. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::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]++;
}
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;
}
///////////////////////////////////////////////////////////////////////////////
// //
// tetrahedrondealloc() Deallocate space for a tet., marking it dead. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::tetrahedrondealloc(tetrahedron *dyingtetrahedron)
{
// Set tetrahedron's vertices to NULL. This makes it possible to detect
// dead tetrahedra when traversing the list of all tetrahedra.
dyingtetrahedron[4] = (tetrahedron) NULL;
//if (b->plc || b->refine) { //if (b->useshelles) {
// Dealloc the space to subfaces/subsegments.
if (dyingtetrahedron[8] != NULL) {
tet2segpool->dealloc((shellface *) dyingtetrahedron[8]);
}
if (dyingtetrahedron[9] != NULL) {
tet2subpool->dealloc((shellface *) dyingtetrahedron[9]);
}
//}
tetrahedrons->dealloc((void *) dyingtetrahedron);
}
///////////////////////////////////////////////////////////////////////////////
// //
// tetrahedrontraverse() Traverse the tetrahedra, skipping dead ones. //
// //
///////////////////////////////////////////////////////////////////////////////
tetgenmesh::tetrahedron* tetgenmesh::tetrahedrontraverse()
{
tetrahedron *newtetrahedron;
do {
newtetrahedron = (tetrahedron *) tetrahedrons->traverse();
if (newtetrahedron == (tetrahedron *) NULL) {
return (tetrahedron *) NULL;
}
} while ((newtetrahedron[4] == (tetrahedron) NULL) ||
((point) newtetrahedron[7] == dummypoint));
return 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;
}
///////////////////////////////////////////////////////////////////////////////
// //
// shellfacedealloc() Deallocate space for a shellface, marking it dead. //
// Used both for dealloc a subface and subsegment. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::shellfacedealloc(memorypool *pool, shellface *dyingsh)
{
// Set shellface's vertices to NULL. This makes it possible to detect dead
// shellfaces when traversing the list of all shellfaces.
dyingsh[3] = (shellface) NULL;
pool->dealloc((void *) dyingsh);
}
///////////////////////////////////////////////////////////////////////////////
// //
// shellfacetraverse() Traverse the subfaces, skipping dead ones. Used //
// for both subfaces and subsegments pool traverse. //
// //
///////////////////////////////////////////////////////////////////////////////
tetgenmesh::shellface* tetgenmesh::shellfacetraverse(memorypool *pool)
{
shellface *newshellface;
do {
newshellface = (shellface *) pool->traverse();
if (newshellface == (shellface *) NULL) {
return (shellface *) NULL;
}
} while (newshellface[3] == (shellface) NULL); // Skip dead ones.
return newshellface;
}
///////////////////////////////////////////////////////////////////////////////
// //
// badfacedealloc() Deallocate space for a badface, marking it dead. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::badfacedealloc(memorypool *pool, badface *dying)
{
// Set badface's forg to NULL. This makes it possible to detect dead
// ones when traversing the list of all items.
dying->forg = (point) NULL;
pool->dealloc((void *) dying);
}
///////////////////////////////////////////////////////////////////////////////
// //
// badfacetraverse() Traverse the pools, skipping dead ones. //
// //
///////////////////////////////////////////////////////////////////////////////
tetgenmesh::badface* tetgenmesh::badfacetraverse(memorypool *pool)
{
badface *newsh;
do {
newsh = (badface *) pool->traverse();
if (newsh == (badface *) NULL) {
return (badface *) NULL;
}
} while (newsh->forg == (point) NULL); // Skip dead ones.
return newsh;
}
///////////////////////////////////////////////////////////////////////////////
// //
// pointdealloc() Deallocate space for a point, marking it dead. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::pointdealloc(point dyingpoint)
{
// Mark the point as dead. This makes it possible to detect dead points
// when traversing the list of all points.
setpointtype(dyingpoint, DEADVERTEX);
points->dealloc((void *) dyingpoint);
}
///////////////////////////////////////////////////////////////////////////////
// //
// pointtraverse() Traverse the points, skipping dead ones. //
// //
///////////////////////////////////////////////////////////////////////////////
tetgenmesh::point tetgenmesh::pointtraverse()
{
point newpoint;
do {
newpoint = (point) points->traverse();
if (newpoint == (point) NULL) {
return (point) NULL;
}
} while (pointtype(newpoint) == DEADVERTEX); // Skip dead ones.
return newpoint;
}
///////////////////////////////////////////////////////////////////////////////
// //
// maketetrahedron() Create a new tetrahedron. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::maketetrahedron(triface *newtet)
{
newtet->tet = (tetrahedron *) tetrahedrons->alloc();
// Initialize the four adjoining tetrahedra to be "outer space".
newtet->tet[0] = 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 sbfaces yet.
newtet->tet[8] = NULL;
newtet->tet[9] = NULL;
// Initialize the marker (for flags).
setelemmarker(newtet->tet, 0);
for (int i = 0; i < in->numberoftetrahedronattributes; i++) {
setelemattribute(newtet->tet, i, 0.0);
}
if (b->varvolume) {
setvolumebound(newtet->tet, -1.0);
}
// Initialize the version to be Zero.
newtet->ver = 11;
}
///////////////////////////////////////////////////////////////////////////////
// //
// makeshellface() Create a new shellface with version zero. Used for //
// both subfaces and seusegments. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::makeshellface(memorypool *pool, face *newface)
{
newface->sh = (shellface *) pool->alloc();
// 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 (b->quality && checkconstraints) {
// Initialize the maximum area bound.
setareabound(*newface, 0.0);
}
// Clear the infection and marktest bits.
((int *) (newface->sh))[shmarkindex + 1] = 0;
// Set the boundary marker to zero.
setshellmark(*newface, 0);
// Set the default face type.
setshelltype(*newface, NSHARP);
// Initialize the version to be Zero.
newface->shver = 0;
}
///////////////////////////////////////////////////////////////////////////////
// //
// makepoint() Create a new point. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::makepoint(point* pnewpoint, enum verttype vtype)
{
int ptmark, i;
*pnewpoint = (point) points->alloc();
// 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->psc || 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).
ptmark = (int) points->items - (in->firstnumber == 1 ? 0 : 1);
setpointmark(*pnewpoint, ptmark);
// Initialize the point type.
setpointtype(*pnewpoint, vtype);
// Clear the point flags.
puninfect(*pnewpoint);
punmarktest(*pnewpoint);
if (b->psc) {
// Initialize the u,v coordinates.
setpointgeomuv(*pnewpoint, 0, 0);
setpointgeomuv(*pnewpoint, 1, 0);
// Initialize the geometry tag.
setpointgeomtag(*pnewpoint, 0);
}
}
///////////////////////////////////////////////////////////////////////////////
// //
// initializepools() Calculate the sizes of the point, tetrahedron, and //
// subface. Initialize their memory pools. //
// //
// This routine also computes the indices 'pointmarkindex', 'point2simindex',//
// 'point2pbcptindex', 'elemattribindex', and 'volumeboundindex'. They are //
// used to find values within each point and tetrahedron, respectively. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::initializepools()
{
enum memorypool::wordtype wtype;
int pointsize, elesize, shsize;
if (b->verbose) {
printf(" Initializing memorypools.\n");
}
// Default varconstraint = 0;
if (in->segmentconstraintlist || in->facetconstraintlist) {
checkconstraints = 1;
}
// Each vertex has three coordinates plus a weight, hence 4 REALs.
// The index within each point at which its metric tensor is found.
if (b->psc) {
// For '-s' option (PSC), the u,v coordinates are provided. It is
// saved directly after the list of point attributes.
pointmtrindex = 6 + in->numberofpointattributes;
} else {
pointmtrindex = 4 + in->numberofpointattributes;
}
// The index within each point at which its u, v coordinates are found.
pointparamindex = pointmtrindex - 2;
// For '-m' option. A tensor field is provided (*.mtr or *.b.mtr file).
if (b->metric) {
// Decide the size (1, 3, or 6) of the metric tensor.
if (bgm != (tetgenmesh *) NULL) {
// A background mesh is allocated. It may not exist though.
sizeoftensor = (bgm->in != (tetgenio *) NULL) ?
bgm->in->numberofpointmtrs : in->numberofpointmtrs;
} else {
// No given background mesh - Itself is a background mesh.
sizeoftensor = in->numberofpointmtrs;
}
// Make sure sizeoftensor is at least 1.
sizeoftensor = (sizeoftensor > 0) ? sizeoftensor : 1;
} else {
// For '-q' option. Make sure to have space for saving a scalar value.
sizeoftensor = b->quality ? 1 : 0;
}
// The index within each point at which an element pointer is found, where
// the index is measured in pointers. Ensure the index is aligned to a
// sizeof(tetrahedron)-byte address.
point2simindex = ((pointmtrindex + sizeoftensor) * sizeof(REAL)
+ sizeof(tetrahedron) - 1) / sizeof(tetrahedron);
if (b->plc || b->refine || b->voroout) {
// Increase the point size by 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 to into the background mesh, point2bgmtet().
pointsize = (point2simindex + 4) * sizeof(tetrahedron);
} else {
pointsize = (point2simindex + 3) * sizeof(tetrahedron);
}
// The index within each point at which a pbc point is found.
point2pbcptindex = (pointsize + sizeof(tetrahedron) - 1)
/ sizeof(tetrahedron);
if (checkpbcs) {
// Increase the size by one pointer to a corresponding pbc point,
// read by point2pbcpt().
pointsize = (point2pbcptindex + 1) * sizeof(tetrahedron);
}
} else {
// Increase the point size by two pointer, which are:
// - a pointer to a tet, read by point2tet();
// - a pointer to a parent point, read by point2ppt()). -- Used by btree.
pointsize = (point2simindex + 2) * sizeof(tetrahedron);
}
// The index within each point at which the boundary marker is found,
// Ensure the point marker is aligned to a sizeof(int)-byte address.
pointmarkindex = (pointsize + sizeof(int) - 1) / sizeof(int);
// Now point size is the ints (inidcated by pointmarkindex) plus:
// - an integer for boundary marker;
// - an integer for vertex type;
// - an integer for geometry tag (optional, -s option).
//pointsize = (pointmarkindex + 2)*sizeof(int); // Wrong for 64 bit system.
pointsize = (pointmarkindex + 2 + (b->psc ? 1 : 0)) * sizeof(tetrahedron);
// Decide the wordtype used in vertex pool.
wtype = (sizeof(REAL) >= sizeof(tetrahedron)) ?
memorypool::FLOATINGPOINT : memorypool::POINTER;
// Initialize the pool of vertices.
points = new memorypool(pointsize, b->vertexperblock, wtype, 0);
if (b->verbose) {
printf(" Size of a point: %d bytes.\n", points->itembytes);
}
// Initialize the infinite vertex.
dummypoint = (point) new char[pointsize];
setpointmark(dummypoint, -1);
// The number of bytes occupied by a tetrahedron is varying by the user-
// specified options. The contents of the first 12 pointers are listed
// in the following table:
// [0] |__ neighbor at f0 __|
// [1] |__ neighbor at f1 __|
// [2] |__ neighbor at f2 __|
// [3] |__ neighbor at f3 __|
// [4] |_____ vertex p0 ____|
// [5] |_____ vertex p1 ____|
// [6] |_____ vertex p2 ____|
// [7] |_____ vertex p3 ____|
// [8] |__ segments array __| (used by -p)
// [9] |__ subfaces array __| (used by -p)
// [10] |_____ reserved _____|
// [11] |___ elem marker ____| (used as an integer)
elesize = 12 * sizeof(tetrahedron);
// The index to find the element markers. An integer containing varies
// flags and element counter.
assert(sizeof(int) <= sizeof(tetrahedron));
assert((sizeof(tetrahedron) % sizeof(int)) == 0);
elemmarkerindex = (elesize - sizeof(tetrahedron)) / sizeof(int);
// The index within each element at which its attributes are found, where
// the index is measured in REALs.
elemattribindex = (elesize + sizeof(REAL) - 1) / sizeof(REAL);
// The index within each element at which the maximum voulme bound is
// found, where the index is measured in REALs. Note that if the
// `b->regionattrib' flag is set, an additional attribute will be added.
volumeboundindex = elemattribindex + in->numberoftetrahedronattributes
+ (b->regionattrib > 0);
// If element attributes or an constraint are needed, increase the number
// of bytes occupied by an element.
if (b->varvolume) {
elesize = (volumeboundindex + 1) * sizeof(REAL);
} else if (in->numberoftetrahedronattributes + b->regionattrib > 0) {
elesize = volumeboundindex * sizeof(REAL);
}
// Having determined the memory size of an element, initialize the pool.
tetrahedrons = new memorypool(elesize, b->tetrahedraperblock,
memorypool::POINTER, 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
// found, where the index is measured in REALs.
areaboundindex = (shsize + sizeof(REAL) - 1) / sizeof(REAL);
// If -q switch is in use, increase the number of bytes occupied by
// a subface for saving maximum area bound.
if (b->quality && 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 optionally one for pbc group.
shsize = (shmarkindex + 2 + checkpbcs) * sizeof(shellface);
// Initialize the pool of subfaces. Each subface record is eight-byte
// aligned so it has room to store an edge version (from 0 to 5) in
// the least three bits.
subfaces = new memorypool(shsize, b->shellfaceperblock,
memorypool::POINTER, 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,
memorypool::POINTER, 8);
// Initialize the pool for tet-subseg connections.
tet2segpool = new memorypool(6 * sizeof(shellface), b->shellfaceperblock,
memorypool::POINTER, 0);
// Initialize the pool for tet-subface connections.
tet2subpool = new memorypool(4 * sizeof(shellface), b->shellfaceperblock,
memorypool::POINTER, 0);
// Initialize arraypools for segment & facet recovery.
subsegstack = new arraypool(sizeof(face), 10);
subfacstack = new arraypool(sizeof(face), 10);
subvertstack = new arraypool(sizeof(point), 8);
suppsteinerptlist = new arraypool(sizeof(point), 8);
// Initialize arraypools for surface Bowyer-Watson algorithm.
caveshlist = new arraypool(sizeof(face), 8);
caveshbdlist = new arraypool(sizeof(face), 8);
cavesegshlist = new arraypool(sizeof(face), 4);
cavetetshlist = new arraypool(sizeof(face), 8);
cavetetseglist = new arraypool(sizeof(face), 8);
caveencshlist = new arraypool(sizeof(face), 8);
caveencseglist = new arraypool(sizeof(face), 8);
}
// Initialize the pool for flips.
flippool = new memorypool(sizeof(badface), 1024, memorypool::POINTER, 0);
unflipqueue = new arraypool(sizeof(badface), 10);
// Initialize the arraypools for Bowyer-Watson algorithm.
cavetetlist = new arraypool(sizeof(triface), 10);
cavebdrylist = new arraypool(sizeof(triface), 10);
caveoldtetlist = new arraypool(sizeof(triface), 10);
cavetetvertlist = new arraypool(sizeof(point), 10);
}
//// ////
//// ////
//// mempool_cxx //////////////////////////////////////////////////////////////
//// geom_cxx /////////////////////////////////////////////////////////////////
//// ////
//// ////
// PI is the ratio of a circle's circumference to its diameter.
REAL tetgenmesh::PI = 3.14159265358979323846264338327950288419716939937510582;
///////////////////////////////////////////////////////////////////////////////
// //
// tri_edge_test() Triangle-edge intersection test. //
// //
// This routine takes a triangle T (with vertices A, B, C) and an edge E (P, //
// Q) in 3D, and tests if they intersect each other. //
// //
// If the point 'R' is not NULL, it lies strictly above the plane defined by //
// A, B, C. It is used in test when T and E are coplanar. //
// //
// If T and E intersect each other, they may intersect in different ways. If //
// 'level' > 0, their intersection type will be reported 'types' and 'pos'. //
// //
// The retrun value indicates one of the following cases: //
// - 0, T and E are disjoint. //
// - 1, T and E intersect each other. //
// - 2, T and E are not coplanar. They intersect at a single point. //
// - 4, T and E are coplanar. They intersect at a single point or a line //
// segment (if types[1] != DISJOINT). //
// //
///////////////////////////////////////////////////////////////////////////////
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 = DIST(A, B);
len += DIST(B, C);
len += DIST(C, A);
len /= 3.0;
R = abovept; //dummypoint;
R[0] = A[0] + len * n[0];
R[1] = A[1] + len * n[1];
R[2] = A[2] + len * n[2];
} else {
// The triangle [A,B,C] is (nearly) degenerate, i.e., it is (close)
// to a line. We need a line-line intersection test.
//assert(0);
// !!! A non-save return value.!!!
return 0; // DISJOINT
}
}
}
// 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);
triedgcopcount++;
if (sA < 0) {
if (sB < 0) {
if (sC < 0) { // (---).
return 0;
} else {
if (sC > 0) { // (--+).
// All points are in the right positions.
SETVECTOR3(U, A, B, C); // I3
SETVECTOR3(V, P, Q, R); // I2
SETVECTOR3(pu, 0, 1, 2);
SETVECTOR3(pv, 0, 1, 2);
z1 = 0;
} else { // (--0).
SETVECTOR3(U, A, B, C); // I3
SETVECTOR3(V, P, Q, R); // I2
SETVECTOR3(pu, 0, 1, 2);
SETVECTOR3(pv, 0, 1, 2);
z1 = 1;
}
}
} else {
if (sB > 0) {
if (sC < 0) { // (-+-).
SETVECTOR3(U, C, A, B); // PT = ST
SETVECTOR3(V, P, Q, R); // I2
SETVECTOR3(pu, 2, 0, 1);
SETVECTOR3(pv, 0, 1, 2);
z1 = 0;
} else {
if (sC > 0) { // (-++).
SETVECTOR3(U, B, C, A); // PT = ST x ST
SETVECTOR3(V, Q, P, R); // PL = SL
SETVECTOR3(pu, 1, 2, 0);
SETVECTOR3(pv, 1, 0, 2);
z1 = 0;
} else { // (-+0).
SETVECTOR3(U, C, A, B); // PT = ST
SETVECTOR3(V, P, Q, R); // I2
SETVECTOR3(pu, 2, 0, 1);
SETVECTOR3(pv, 0, 1, 2);
z1 = 2;
}
}
} else {
if (sC < 0) { // (-0-).
SETVECTOR3(U, C, A, B); // PT = ST
SETVECTOR3(V, P, Q, R); // I2
SETVECTOR3(pu, 2, 0, 1);
SETVECTOR3(pv, 0, 1, 2);
z1 = 1;
} else {
if (sC > 0) { // (-0+).
SETVECTOR3(U, B, C, A); // PT = ST x ST
SETVECTOR3(V, Q, P, R); // PL = SL
SETVECTOR3(pu, 1, 2, 0);
SETVECTOR3(pv, 1, 0, 2);
z1 = 2;
} else { // (-00).
SETVECTOR3(U, B, C, A); // PT = ST x ST
SETVECTOR3(V, Q, P, R); // PL = SL
SETVECTOR3(pu, 1, 2, 0);
SETVECTOR3(pv, 1, 0, 2);
z1 = 3;
}
}
}
}
} else {
if (sA > 0) {
if (sB < 0) {
if (sC < 0) { // (+--).
SETVECTOR3(U, B, C, A); // PT = ST x ST
SETVECTOR3(V, P, Q, R); // I2
SETVECTOR3(pu, 1, 2, 0);
SETVECTOR3(pv, 0, 1, 2);
z1 = 0;
} else {
if (sC > 0) { // (+-+).
SETVECTOR3(U, C, A, B); // PT = ST
SETVECTOR3(V, Q, P, R); // PL = SL
SETVECTOR3(pu, 2, 0, 1);
SETVECTOR3(pv, 1, 0, 2);
z1 = 0;
} else { // (+-0).
SETVECTOR3(U, C, A, B); // PT = ST
SETVECTOR3(V, Q, P, R); // PL = SL
SETVECTOR3(pu, 2, 0, 1);
SETVECTOR3(pv, 1, 0, 2);
z1 = 2;
}
}
} else {
if (sB > 0) {
if (sC < 0) { // (++-).
SETVECTOR3(U, A, B, C); // I3
SETVECTOR3(V, Q, P, R); // PL = SL
SETVECTOR3(pu, 0, 1, 2);
SETVECTOR3(pv, 1, 0, 2);
z1 = 0;
} else {
if (sC > 0) { // (+++).
return 0;
} else { // (++0).
SETVECTOR3(U, A, B, C); // I3
SETVECTOR3(V, Q, P, R); // PL = SL
SETVECTOR3(pu, 0, 1, 2);
SETVECTOR3(pv, 1, 0, 2);
z1 = 1;
}
}
} else { // (+0#)
if (sC < 0) { // (+0-).
SETVECTOR3(U, B, C, A); // PT = ST x ST
SETVECTOR3(V, P, Q, R); // I2
SETVECTOR3(pu, 1, 2, 0);
SETVECTOR3(pv, 0, 1, 2);
z1 = 2;
} else {
if (sC > 0) { // (+0+).
SETVECTOR3(U, C, A, B); // PT = ST
SETVECTOR3(V, Q, P, R); // PL = SL
SETVECTOR3(pu, 2, 0, 1);
SETVECTOR3(pv, 1, 0, 2);
z1 = 1;
} else { // (+00).
SETVECTOR3(U, B, C, A); // PT = ST x ST
SETVECTOR3(V, P, Q, R); // I2
SETVECTOR3(pu, 1, 2, 0);
SETVECTOR3(pv, 0, 1, 2);
z1 = 3;
}
}
}
}
} else {
if (sB < 0) {
if (sC < 0) { // (0--).
SETVECTOR3(U, B, C, A); // PT = ST x ST
SETVECTOR3(V, P, Q, R); // I2
SETVECTOR3(pu, 1, 2, 0);
SETVECTOR3(pv, 0, 1, 2);
z1 = 1;
} else {
if (sC > 0) { // (0-+).
SETVECTOR3(U, A, B, C); // I3
SETVECTOR3(V, P, Q, R); // I2
SETVECTOR3(pu, 0, 1, 2);
SETVECTOR3(pv, 0, 1, 2);
z1 = 2;
} else { // (0-0).
SETVECTOR3(U, C, A, B); // PT = ST
SETVECTOR3(V, Q, P, R); // PL = SL
SETVECTOR3(pu, 2, 0, 1);
SETVECTOR3(pv, 1, 0, 2);
z1 = 3;
}
}
} else {
if (sB > 0) {
if (sC < 0) { // (0+-).
SETVECTOR3(U, A, B, C); // I3
SETVECTOR3(V, Q, P, R); // PL = SL
SETVECTOR3(pu, 0, 1, 2);
SETVECTOR3(pv, 1, 0, 2);
z1 = 2;
} else {
if (sC > 0) { // (0++).
SETVECTOR3(U, B, C, A); // PT = ST x ST
SETVECTOR3(V, Q, P, R); // PL = SL
SETVECTOR3(pu, 1, 2, 0);
SETVECTOR3(pv, 1, 0, 2);
z1 = 1;
} else { // (0+0).
SETVECTOR3(U, C, A, B); // PT = ST
SETVECTOR3(V, P, Q, R); // I2
SETVECTOR3(pu, 2, 0, 1);
SETVECTOR3(pv, 0, 1, 2);
z1 = 3;
}
}
} else { // (00#)
if (sC < 0) { // (00-).
SETVECTOR3(U, A, B, C); // I3
SETVECTOR3(V, Q, P, R); // PL = SL
SETVECTOR3(pu, 0, 1, 2);
SETVECTOR3(pv, 1, 0, 2);
z1 = 3;
} else {
if (sC > 0) { // (00+).
SETVECTOR3(U, A, B, C); // I3
SETVECTOR3(V, P, Q, R); // I2
SETVECTOR3(pu, 0, 1, 2);
SETVECTOR3(pv, 0, 1, 2);
z1 = 3;
} else { // (000)
// Not possible unless ABC is degenerate.
// 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.
}
assert(z1 != 4); // SELF_CHECK
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;
}
}
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) {
assert(s2 > 0); // SELF_CHECK
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 {
if (s3 == 0) {
assert(s2 > 0); // SELF_CHECK
if (s4 > 0) {
// P = k, [P, Q] in [k, l] (-+0+).
types[0] = (int) TOUCHEDGE;
pos[0] = pu[2]; // [C, A]
pos[1] = pv[0]; // P
types[1] = (int) TOUCHFACE;
pos[2] = 3; // [A, B, C]
pos[3] = pv[1]; // Q
} else {
if (s4 == 0) {
// [P, Q] = [k, l] (-+00).
types[0] = (int) TOUCHEDGE;
pos[0] = pu[2]; // [C, A]
pos[1] = pv[0]; // P
types[1] = (int) TOUCHEDGE;
pos[2] = pu[1]; // [B, C]
pos[3] = pv[1]; // Q
} else {
// P = k, [P, Q] contains [k, l] (-+0-).
types[0] = (int) TOUCHEDGE;
pos[0] = pu[2]; // [C, A]
pos[1] = pv[0]; // P
types[1] = (int) 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 { // s1 == 0
// Q = k (0####)
types[0] = (int) TOUCHEDGE;
pos[0] = pu[2]; // [C, A]
pos[1] = pv[1]; // Q
types[1] = (int) DISJOINT;
}
} else if (z1 == 2) { // (tritri-23)
if (s1 < 0) {
if (s3 > 0) {
assert(s2 > 0); // SELF_CHECK
if (s4 > 0) {
// [P, Q] overlaps [A, l] (-+++).
types[0] = (int) 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) {
assert(s2 > 0); // SELF_CHECK
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 { // 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) {
assert(s2 > 0); // SELF_CHECK
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 (s3 == 0) {
assert(s2 > 0); // SELF_CHECK
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;
}
}
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;
triedgcount++;
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;
}
s3 = orient3d(U[2], U[0], V[0], V[1]);
if (s3 < 0) {
return 0;
}
if (level == 0) {
return 1; // The are intersected.
}
types[1] = (int) DISJOINT; // No second intersection point.
if (z1 == 0) {
if (s1 > 0) {
if (s2 > 0) {
if (s3 > 0) { // (+++)
// [P, Q] passes interior of [A, B, C].
types[0] = (int) 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 { // s3 == 0 (000)
// Impossible.
assert(0);
}
}
}
} else { // z1 == 1
if (s1 > 0) {
if (s2 > 0) {
if (s3 > 0) { // (+++)
// Q lies in [A, B, C].
types[0] = (int) TOUCHFACE;
pos[0] = 0; // [A, B, C]
pos[1] = pv[1]; // Q
} else { // s3 == 0 (++0)
// Q lies on [C, A].
types[0] = (int) TOUCHEDGE;
pos[0] = pu[2]; // [C, A]
pos[1] = pv[1]; // Q
}
} else { // s2 == 0
if (s3 > 0) { // (+0+)
// Q lies on [B, C].
types[0] = (int) TOUCHEDGE;
pos[0] = pu[1]; // [B, C]
pos[1] = pv[1]; // Q
} else { // s3 == 0 (+00)
// Q = C.
types[0] = (int) 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
} else { // s3 == 0 (000)
// Impossible.
assert(0);
}
}
}
}
// 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;
// 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);
}
///////////////////////////////////////////////////////////////////////////////
// //
// tri_tri_inter() Test whether two triangle (abc) and (opq) are //
// intersecting or not. //
// //
// Return 0 if they are disjoint. Otherwise, return 1. 'type' returns one of //
// the four cases: SHAREVERTEX, SHAREEDGE, SHAREFACE, and 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 {
assert(types[1] != (int) SHAREVERT);
return (int) INTERSECT;
}
} else {
if (types[0] == (int) SHAREEDGE) {
return (int) SHAREEDGE;
} else {
return (int) INTERSECT;
}
}
} else {
assert(0);
}
}
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;
}
// It is only possible either no share edge or one.
assert(shareedge == 0 || shareedge == 1);
// 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) {
assert((abcpq == (int) SHAREVERT) && (abcqo == (int) SHAREVERT));
// op is coincident with an edge of abc.
return (int) SHAREEDGE;
}
if (abcpq == (int) SHAREEDGE) {
assert((abcop == (int) SHAREVERT) && (abcqo == (int) SHAREVERT));
// pq is coincident with an edge of abc.
return (int) SHAREEDGE;
}
if (abcqo == (int) SHAREEDGE) {
assert((abcop == (int) SHAREVERT) && (abcpq == (int) SHAREVERT));
// qo is coincident with an edge of abc.
return (int) SHAREEDGE;
}
// They may share a vertex or disjoint.
if (abcop == (int) SHAREVERT) {
// o or p is coincident with a vertex of abc.
if (abcpq == (int) SHAREVERT) {
// p is the coincident vertex.
assert(abcqo != (int) SHAREVERT);
} else {
// o is the coincident vertex.
assert(abcqo == (int) SHAREVERT);
}
return (int) SHAREVERT;
}
if (abcpq == (int) SHAREVERT) {
// q is the coincident vertex.
assert(abcqo == (int) SHAREVERT);
return (int) SHAREVERT;
}
// They are disjoint.
return (int) DISJOINT;
}
///////////////////////////////////////////////////////////////////////////////
// //
// lu_decmp() Compute the LU decomposition of a matrix. //
// //
// Compute the LU decomposition of a (non-singular) square matrix A using //
// partial pivoting and implicit row exchanges. The result is: //
// A = P * L * U, //
// where P is a permutation matrix, L is unit lower triangular, and U is //
// upper triangular. The factored form of A is used in combination with //
// 'lu_solve()' to solve linear equations: Ax = b, or invert a matrix. //
// //
// The inputs are a square matrix 'lu[N..n+N-1][N..n+N-1]', it's size is 'n'.//
// On output, 'lu' is replaced by the LU decomposition of a rowwise permuta- //
// tion of itself, 'ps[N..n+N-1]' is an output vector that records the row //
// permutation effected by the partial pivoting, effectively, 'ps' array //
// tells the user what the permutation matrix P is; 'd' is output as +1/-1 //
// depending on whether the number of row interchanges was even or odd, //
// respectively. //
// //
// Return true if the LU decomposition is successfully computed, otherwise, //
// return false in case that A is a singular matrix. //
// //
///////////////////////////////////////////////////////////////////////////////
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;
}
///////////////////////////////////////////////////////////////////////////////
// //
// lu_solve() Solves the linear equation: Ax = b, after the matrix A //
// has been decomposed into the lower and upper triangular //
// matrices L and U, where A = LU. //
// //
// 'lu[N..n+N-1][N..n+N-1]' is input, not as the matrix 'A' but rather as //
// its LU decomposition, computed by the routine 'lu_decmp'; 'ps[N..n+N-1]' //
// is input as the permutation vector returned by 'lu_decmp'; 'b[N..n+N-1]' //
// is input as the right-hand side vector, and returns with the solution //
// vector. 'lu', 'n', and 'ps' are not modified by this routine and can be //
// left in place for successive calls with different right-hand sides 'b'. //
// //
///////////////////////////////////////////////////////////////////////////////
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];
}
///////////////////////////////////////////////////////////////////////////////
// //
// incircle3d() 3D in-circle test. //
// //
// Return a negative value if pd is inside the circumcircle of the triangle //
// pa, pb, and pc. //
// //
///////////////////////////////////////////////////////////////////////////////
REAL tetgenmesh::incircle3d(point pa, point pb, point pc, point pd)
{
REAL area2[2], n1[3], n2[3], c[3];
REAL sign, r, d;
// Calculate the areas of the two triangles [a, b, c] and [b, a, d].
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 = DIST(c, pd);
} else {
// Choose [b, a, d] as the base triangle.
if (area2[1] > 0) {
circumsphere(pb, pa, pd, NULL, c, &r);
d = DIST(c, pc);
} else {
// The four points are collinear. This case only happens on the boundary.
return 0; // Return "not inside".
}
}
sign = d - r;
if (fabs(sign) / r < b->epsilon) {
sign = 0;
}
return sign;
}
///////////////////////////////////////////////////////////////////////////////
// //
// insphere_s() Insphere test with symbolic perturbation. //
// //
// Given four points pa, pb, pc, and pd, test if the point pe lies inside or //
// outside the circumscirbed sphere of the four points. //
// //
// Here we assume that the 3d orientation of the point sequence {pa, pb, pc, //
// pd} is positive (NOT zero), i.e., pd lies above the plane passing through //
// points pa, pb, and pc. Otherwise, the returned sign is flipped. //
// //
// Return a positive value (> 0) if pe lies inside, a negative value (< 0) //
// if pe lies outside the sphere, the returned value will not be zero. //
// //
///////////////////////////////////////////////////////////////////////////////
REAL tetgenmesh::insphere_s(REAL* pa, REAL* pb, REAL* pc, REAL* pd, REAL* pe)
{
REAL sign;
inspherecount++;
sign = insphere(pa, pb, pc, pd, pe);
if (sign != 0.0) {
return sign;
}
insphere_sos_count++;
// Symbolic perturbation.
point pt[5], swappt;
REAL oriA, oriB;
int swaps, count;
int n, i;
pt[0] = pa;
pt[1] = pb;
pt[2] = pc;
pt[3] = pd;
pt[4] = pe;
// Sort the five points such that their indices are in the increasing
// order. An optimized bubble sort algorithm is used, i.e., it has
// the worst case O(n^2) runtime, but it is usually much faster.
swaps = 0; // Record the total number of swaps.
n = 5;
do {
count = 0;
n = n - 1;
for (i = 0; i < n; i++) {
if (pointmark(pt[i]) > pointmark(pt[i+1])) {
swappt = pt[i]; pt[i] = pt[i+1]; pt[i+1] = swappt;
count++;
}
}
swaps += count;
} while (count > 0); // Continue if some points are swapped.
oriA = orient3d(pt[1], pt[2], pt[3], pt[4]);
if (oriA != 0.0) {
// Flip the sign if there are odd number of swaps.
if ((swaps % 2) != 0) oriA = -oriA;
return oriA;
}
oriB = -orient3d(pt[0], pt[2], pt[3], pt[4]);
assert(oriB != 0.0); // SELF_CHECK
// Flip the sign if there are odd number of swaps.
if ((swaps % 2) != 0) oriB = -oriB;
return oriB;
}
///////////////////////////////////////////////////////////////////////////////
// //
// orient4d_s() 4d orientation test with symbolic perturbation. //
// //
// Given four lifted points pa', pb', pc', and pd' in R^4,test if the lifted //
// point pe' in R^4 lies below or above the hyperplance passing through the //
// four points pa', pb', pc', and pd'. //
// //
// Here we assume that the 3d orientation of the point sequence {pa, pb, pc, //
// pd} is positive (NOT zero), i.e., pd lies above the plane passing through //
// the points pa, pb, and pc. Otherwise, the returned sign is flipped. //
// //
// Return a positive value (> 0) if pe' lies below, a negative value (< 0) //
// if pe' lies above the hyperplane, the returned value should not be zero. //
// //
///////////////////////////////////////////////////////////////////////////////
REAL tetgenmesh::orient4d_s(REAL* pa, REAL* pb, REAL* pc, REAL* pd, REAL* pe,
REAL aheight, REAL bheight, REAL cheight,
REAL dheight, REAL eheight)
{
REAL sign;
inspherecount++;
sign = orient4d(pa, pb, pc, pd, pe,
aheight, bheight, cheight, dheight, eheight);
if (sign != 0.0) {
return sign;
}
insphere_sos_count++;
// Symbolic perturbation.
point pt[5], swappt;
REAL oriA, oriB;
int swaps, count;
int n, i;
pt[0] = pa;
pt[1] = pb;
pt[2] = pc;
pt[3] = pd;
pt[4] = pe;
// Sort the five points such that their indices are in the increasing
// order. An optimized bubble sort algorithm is used, i.e., it has
// the worst case O(n^2) runtime, but it is usually much faster.
swaps = 0; // Record the total number of swaps.
n = 5;
do {
count = 0;
n = n - 1;
for (i = 0; i < n; i++) {
if (pointmark(pt[i]) > pointmark(pt[i+1])) {
swappt = pt[i]; pt[i] = pt[i+1]; pt[i+1] = swappt;
count++;
}
}
swaps += count;
} while (count > 0); // Continue if some points are swapped.
oriA = orient3d(pt[1], pt[2], pt[3], pt[4]);
if (oriA != 0.0) {
// Flip the sign if there are odd number of swaps.
if ((swaps % 2) != 0) oriA = -oriA;
return oriA;
}
oriB = -orient3d(pt[0], pt[2], pt[3], pt[4]);
assert(oriB != 0.0); // SELF_CHECK
// Flip the sign if there are odd number of swaps.
if ((swaps % 2) != 0) oriB = -oriB;
return oriB;
}
///////////////////////////////////////////////////////////////////////////////
// //
// facenormal() Calculate the normal of the face. //
// //
// The normal of the face abc can be calculated by the cross product of 2 of //
// its 3 edge vectors. A better choice of two edge vectors will reduce the //
// numerical error during the calculation. Burdakov proved that the optimal //
// basis problem is equivalent to the minimum spanning tree problem with the //
// edge length be the functional, see Burdakov, "A greedy algorithm for the //
// optimal basis problem", BIT 37:3 (1997), 591-599. If 'pivot' > 0, the two //
// short edges in abc are chosen for the calculation. //
// //
// If 'lav' is not NULL and if 'pivot' is set, the average edge length of //
// the edges of the face [a,b,c] is returned. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::facenormal(point pa, point pb, point pc, REAL *n, int pivot,
REAL* lav)
{
REAL v1[3], v2[3], v3[3], *pv1, *pv2;
REAL L1, L2, L3;
v1[0] = pb[0] - pa[0]; // edge vector v1: a->b
v1[1] = pb[1] - pa[1];
v1[2] = pb[2] - pa[2];
v2[0] = pa[0] - pc[0]; // edge vector v2: c->a
v2[1] = pa[1] - pc[1];
v2[2] = pa[2] - pc[2];
// Default, normal is calculated by: v1 x (-v2) (see Fig. fnormal).
if (pivot > 0) {
// Choose edge vectors by Burdakov's algorithm.
v3[0] = pc[0] - pb[0]; // edge vector v3: b->c
v3[1] = pc[1] - pb[1];
v3[2] = pc[2] - pb[2];
L1 = DOT(v1, v1);
L2 = DOT(v2, v2);
L3 = DOT(v3, v3);
// Sort the three edge lengths.
if (L1 < L2) {
if (L2 < L3) {
pv1 = v1; pv2 = v2; // n = v1 x (-v2).
} else {
pv1 = v3; pv2 = v1; // n = v3 x (-v1).
}
} else {
if (L1 < L3) {
pv1 = v1; pv2 = v2; // n = v1 x (-v2).
} else {
pv1 = v2; pv2 = v3; // n = v2 x (-v3).
}
}
if (lav) {
// return the average edge length.
*lav = (sqrt(L1) + sqrt(L2) + sqrt(L3)) / 3.0;
}
} else {
pv1 = v1; pv2 = v2; // n = v1 x (-v2).
}
// Calculate the face normal.
CROSS(pv1, pv2, n);
// Inverse the direction;
n[0] = -n[0];
n[1] = -n[1];
n[2] = -n[2];
}
///////////////////////////////////////////////////////////////////////////////
// //
// shortdistance() Returns the shortest distance from point p to a line //
// defined by two points e1 and e2. //
// //
// First compute the projection length l_p of the vector v1 = p - e1 along //
// the vector v2 = e2 - e1. Then Pythagoras' Theorem is used to compute the //
// shortest distance. //
// //
// This routine allows that p is collinear with the line. In this case, the //
// return value is zero. The two points e1 and e2 should not be identical. //
// //
///////////////////////////////////////////////////////////////////////////////
REAL tetgenmesh::shortdistance(REAL* p, REAL* e1, REAL* e2)
{
REAL v1[3], v2[3];
REAL len, l_p;
v1[0] = e2[0] - e1[0];
v1[1] = e2[1] - e1[1];
v1[2] = e2[2] - e1[2];
v2[0] = p[0] - e1[0];
v2[1] = p[1] - e1[1];
v2[2] = p[2] - e1[2];
len = sqrt(dot(v1, v1));
#ifdef SELF_CHECK
assert(len != 0.0);
#endif
v1[0] /= len;
v1[1] /= len;
v1[2] /= len;
l_p = dot(v1, v2);
return sqrt(dot(v2, v2) - l_p * l_p);
}
///////////////////////////////////////////////////////////////////////////////
// //
// triarea() Return the area of a triangle. //
// //
///////////////////////////////////////////////////////////////////////////////
REAL tetgenmesh::triarea(REAL* pa, REAL* pb, REAL* pc)
{
REAL A[4][4];
// Compute the coefficient matrix A (3x3).
A[0][0] = pb[0] - pa[0];
A[0][1] = pb[1] - pa[1];
A[0][2] = pb[2] - pa[2]; // vector V1 (pa->pb)
A[1][0] = pc[0] - pa[0];
A[1][1] = pc[1] - pa[1];
A[1][2] = pc[2] - pa[2]; // vector V2 (pa->pc)
cross(A[0], A[1], A[2]); // vector V3 (V1 X V2)
return 0.5 * sqrt(dot(A[2], A[2])); // The area of [a,b,c].
}
///////////////////////////////////////////////////////////////////////////////
// //
// interiorangle() Return the interior angle (0 - 2 * PI) between vectors //
// o->p1 and o->p2. //
// //
// 'n' is the normal of the plane containing face (o, p1, p2). The interior //
// angle is the total angle rotating from o->p1 around n to o->p2. Exchange //
// the position of p1 and p2 will get the complement angle of the other one. //
// i.e., interiorangle(o, p1, p2) = 2 * PI - interiorangle(o, p2, p1). Set //
// 'n' be NULL if you only want the interior angle between 0 - PI. //
// //
///////////////////////////////////////////////////////////////////////////////
REAL tetgenmesh::interiorangle(REAL* o, REAL* p1, REAL* p2, REAL* n)
{
REAL v1[3], v2[3], np[3];
REAL theta, costheta, lenlen;
REAL ori, len1, len2;
// Get the interior angle (0 - PI) between o->p1, and o->p2.
v1[0] = p1[0] - o[0];
v1[1] = p1[1] - o[1];
v1[2] = p1[2] - o[2];
v2[0] = p2[0] - o[0];
v2[1] = p2[1] - o[1];
v2[2] = p2[2] - o[2];
len1 = sqrt(dot(v1, v1));
len2 = sqrt(dot(v2, v2));
lenlen = len1 * len2;
#ifdef SELF_CHECK
assert(lenlen != 0.0);
#endif
costheta = dot(v1, v2) / lenlen;
if (costheta > 1.0) {
costheta = 1.0; // Roundoff.
} else if (costheta < -1.0) {
costheta = -1.0; // Roundoff.
}
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;
}
///////////////////////////////////////////////////////////////////////////////
// //
// projpt2edge() Return the projection point from a point to an edge. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::projpt2edge(REAL* p, REAL* e1, REAL* e2, REAL* prj)
{
REAL v1[3], v2[3];
REAL len, l_p;
v1[0] = e2[0] - e1[0];
v1[1] = e2[1] - e1[1];
v1[2] = e2[2] - e1[2];
v2[0] = p[0] - e1[0];
v2[1] = p[1] - e1[1];
v2[2] = p[2] - e1[2];
len = sqrt(dot(v1, v1));
#ifdef SELF_CHECK
assert(len != 0.0);
#endif
v1[0] /= len;
v1[1] /= len;
v1[2] /= len;
l_p = dot(v1, v2);
prj[0] = e1[0] + l_p * v1[0];
prj[1] = e1[1] + l_p * v1[1];
prj[2] = e1[2] + l_p * v1[2];
}
///////////////////////////////////////////////////////////////////////////////
// //
// projpt2face() Return the projection point from a point to a face. //
// //
///////////////////////////////////////////////////////////////////////////////
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];
}
///////////////////////////////////////////////////////////////////////////////
// //
// facedihedral() Return the dihedral angle (in radian) between two //
// adjoining faces. //
// //
// 'pa', 'pb' are the shared edge of these two faces, 'pc1', and 'pc2' are //
// apexes of these two faces. Return the angle (between 0 to 2*pi) between //
// the normal of face (pa, pb, pc1) and normal of face (pa, pb, pc2). //
// //
///////////////////////////////////////////////////////////////////////////////
REAL tetgenmesh::facedihedral(REAL* pa, REAL* pb, REAL* pc1, REAL* pc2)
{
REAL n1[3], n2[3];
REAL n1len, n2len;
REAL costheta, ori;
REAL theta;
facenormal(pa, pb, pc1, n1, 1, NULL);
facenormal(pa, pb, pc2, n2, 1, NULL);
n1len = sqrt(dot(n1, n1));
n2len = sqrt(dot(n2, n2));
costheta = dot(n1, n2) / (n1len * n2len);
// Be careful rounding error!
if (costheta > 1.0) {
costheta = 1.0;
} else if (costheta < -1.0) {
costheta = -1.0;
}
theta = acos(costheta);
ori = orient3d(pa, pb, pc1, pc2);
if (ori > 0.0) {
theta = 2 * PI - theta;
}
return theta;
}
///////////////////////////////////////////////////////////////////////////////
// //
// tetalldihedral() Get all (six) dihedral angles of a tet. //
// //
// If 'cosdd' is not NULL, it returns the cosines of the 6 dihedral angles, //
// the edge indices are given in the global array 'edge2ver'. If 'cosmaxd' //
// (or 'cosmind') is not NULL, it returns the cosine of the maximal (or //
// minimal) dihedral angle. //
// //
///////////////////////////////////////////////////////////////////////////////
bool tetgenmesh::tetalldihedral(point pa, point pb, point pc, point pd,
REAL* cosdd, REAL* cosmaxd, REAL* cosmind)
{
REAL N[4][3], vol, cosd, len;
int f1, f2, i, j;
vol = 0; // Check if the tet is valid or not.
// Get four normals of faces of the tet.
tetallnormal(pa, pb, pc, pd, N, &vol);
if (vol > 0) {
// 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 consine 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 (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;
}
///////////////////////////////////////////////////////////////////////////////
// //
// tetallnormal() Get the in-noramls of the four faces of a given tet. //
// //
// Let tet be abcd. N[4][3] returns the four normals, which are: N[0] cbd, //
// N[1] acd, N[2] bad, N[3] abc (exactly corresponding to the face indices //
// of the mesh data structure). These normals are unnormalized. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::tetallnormal(point pa, point pb, point pc, point pd,
REAL N[4][3], REAL* volume)
{
REAL 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
// 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;
}
}
}
///////////////////////////////////////////////////////////////////////////////
// //
// tetaspectratio() Calculate the aspect ratio of the tetrahedron. //
// //
// The aspect ratio of a tet is R/h, where R is the circumradius and h is //
// the shortest height of the tet. //
// //
///////////////////////////////////////////////////////////////////////////////
REAL tetgenmesh::tetaspectratio(point pa, point pb, point pc, point pd)
{
REAL vda[3], vdb[3], vdc[3];
REAL N[4][3], A[4][4], rhs[4], D;
REAL H[4], volume, radius2, minheightinv;
int indx[4];
int i, j;
// Set the matrix A = [vda, vdb, vdc]^T.
for (i = 0; i < 3; i++) A[0][i] = vda[i] = pa[i] - pd[i];
for (i = 0; i < 3; i++) A[1][i] = vdb[i] = pb[i] - pd[i];
for (i = 0; i < 3; i++) A[2][i] = vdc[i] = pc[i] - pd[i];
// Lu-decompose the matrix A.
lu_decmp(A, 3, indx, &D, 0);
// Get the volume of abcd.
volume = (A[indx[0]][0] * A[indx[1]][1] * A[indx[2]][2]) / 6.0;
// Check if it is zero.
if (volume == 0.0) return 1.0e+200; // A degenerate tet.
// if (volume < 0.0) volume = -volume;
// Check the radiu-edge ratio of the tet.
rhs[0] = 0.5 * dot(vda, vda);
rhs[1] = 0.5 * dot(vdb, vdb);
rhs[2] = 0.5 * dot(vdc, vdc);
lu_solve(A, 3, indx, rhs, 0);
// Get the circumcenter.
// for (i = 0; i < 3; i++) circumcent[i] = pd[i] + rhs[i];
// Get the square of the circumradius.
radius2 = dot(rhs, rhs);
// 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 < 3; i++) {
if (H[i] > minheightinv) minheightinv = H[i];
}
return sqrt(radius2) * minheightinv;
}
///////////////////////////////////////////////////////////////////////////////
// //
// circumsphere() Calculate the smallest circumsphere (center and radius) //
// of the given three or four points. //
// //
// The circumsphere of four points (a tetrahedron) is unique if they are not //
// degenerate. If 'pd = NULL', the smallest circumsphere of three points is //
// the diametral sphere of the triangle if they are not degenerate. //
// //
// Return TRUE if the input points are not degenerate and the circumcenter //
// and circumradius are returned in 'cent' and 'radius' respectively if they //
// are not NULLs. Otherwise, return FALSE indicated the points are degenrate.//
// //
///////////////////////////////////////////////////////////////////////////////
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;
}
///////////////////////////////////////////////////////////////////////////////
// //
// planelineint() Calculate the intersection of a line and a plane. //
// //
// The equation of a plane (points P are on the plane with normal N and P3 //
// on the plane) can be written as: N dot (P - P3) = 0. The equation of the //
// line (points P on the line passing through P1 and P2) can be written as: //
// P = P1 + u (P2 - P1). The intersection of these two occurs when: //
// N dot (P1 + u (P2 - P1)) = N dot P3. //
// Solving for u gives: //
// N dot (P3 - P1) //
// u = ------------------. //
// N dot (P2 - P1) //
// If the denominator is 0 then N (the normal to the plane) is perpendicular //
// to the line. Thus the line is either parallel to the plane and there are //
// no solutions or the line is on the plane in which case there are an infi- //
// nite number of solutions. //
// //
// The plane is given by three points pa, pb, and pc, e1 and e2 defines the //
// line. If u is non-zero, The intersection point (if exists) returns in ip. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::planelineint(REAL* pa, REAL* pb, REAL* pc, REAL* e1, REAL* e2,
REAL* ip, REAL* u)
{
REAL n[3], det, det1;
// Calculate N.
facenormal(pa, pb, pc, n, 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;
}
}
///////////////////////////////////////////////////////////////////////////////
// //
// tetprismvol() Calculate the volume of a tetrahedral prism in 4D. //
// //
// A tetrahedral prism is a convex uniform polychoron (four dimensional poly-//
// tope). It has 6 polyhedral cells: 2 tetrahedra connected by 4 triangular //
// prisms. It has 14 faces: 8 triangular and 6 square. It has 16 edges and 8 //
// vertices. (Wikipedia). //
// //
// Let 'p0', ..., 'p3' be four affinely independent points in R^3. They form //
// the lower tetrahedral facet of the prism. The top tetrahedral facet is //
// formed by four vertices, 'p4', ..., 'p7' in R^4, which is obtained by //
// lifting each vertex of the lower facet into R^4 by a weight (height). A //
// canonical choice of the weights is the square of Euclidean norm of of the //
// points (vectors). //
// //
// //
// The return value is (4!) 24 times of the volume of the tetrahedral prism. //
// //
///////////////////////////////////////////////////////////////////////////////
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]);
}
//// ////
//// ////
//// geom_cxx /////////////////////////////////////////////////////////////////
//// flip_cxx /////////////////////////////////////////////////////////////////
//// ////
//// ////
///////////////////////////////////////////////////////////////////////////////
// //
// flippush() Push a face (possibly will be flipped) into flipstack. //
// //
// The face is marked. The flag is used to check the validity of the face on //
// its popup. Some other flips may change it already. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::flippush(badface*& fstack, triface* flipface)
{
badface *newflipface;
if (!facemarked(*flipface)) {
newflipface = (badface *) flippool->alloc();
newflipface->tt = *flipface;
markface(newflipface->tt);
// Push this face into stack.
newflipface->nextitem = fstack;
fstack = newflipface;
}
}
///////////////////////////////////////////////////////////////////////////////
// //
// flip23() Perform a 2-to-3 flip (face-to-edge flip). //
// //
// 'fliptets' is an array of tetrahedra. On input it contains two tets //
// [a,b,c,d] and [b,a,c,e]. It returns three new tets: [e,d,a,b], [e,d,b,c], //
// [e,d,c,a]. The face [a,b,c] is removed, and the edge [d,e] is created. //
// //
// If 'hullflag' > 0, hull tets may be involved in this flip, i.e., one of //
// the five vertices may be 'dummypoint'. There are two canonical cases: //
// (1) d is 'dummypoint', then all three new tets are hull tets. If e is //
// 'dummypoint', we reconfigure e to d, i.e., turn it up-side down. //
// (2) c is 'dummypoint', then two new tets: [e,d,b,c] and [e,d,c,a], are //
// hull tets. If a or b is 'dummypoint', we reconfigure it to c, i.e., //
// rotate the three input tets counterclockwisely (right-hand rule) //
// until a or b is in c's position. //
// //
// If 'flipflag > 0', faces on the convex hull of the five vertices might //
// need to be flipped, e.g., for incremental DT construction or mesh quality //
// improvement. They will be queued in 'flipstack'. //
// //
// If 'flipflag = 1', it is in the process of incrmental flip DT algorithm, //
// and we assume that 'd' must be the newly inserted vertex. In such case, //
// only the link faces at 'd', i.e., three faces [a,b,e], [b,c,e], and [c,a, //
// e] needs to be queued, see [Edelsbrunner & Shah'1996] and [M\"ucke'1998]. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::flip23(triface* fliptets, int hullflag, int flipflag,
int chkencflag)
{
triface topcastets[3], botcastets[3];
triface newface, casface;
face checksh;
face checkseg;
badface *bface; // used by chkencflag
point pa, pb, pc, pd, pe;
REAL volneg[2], volpos[3], vol_diff; // volumes of involved tet-prisms.
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]);
if (b->verbose > 3) {
printf(" flip 2-to-3: (%d, %d, %d, %d, %d)\n", pointmark(pa),
pointmark(pb), pointmark(pc), pointmark(pd), pointmark(pe));
}
flip23count++;
// Get the outer boundary faces.
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);
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]));
if (hullflag > 0) {
// Check if d is dummytet.
if (pd != dummypoint) {
setvertices(fliptets[0], pe, pd, pa, pb); // [e,d,a,b] *
setvertices(fliptets[1], pe, pd, pb, pc); // [e,d,b,c] *
// Check if c is dummypoint.
if (pc != dummypoint) {
setvertices(fliptets[2], pe, pd, pc, pa); // [e,d,c,a] *
} else {
setvertices(fliptets[2], pd, pe, pa, pc); // [d,e,a,c]
esymself(fliptets[2]); // [e,d,c,a] *
}
// The hullsize does not change.
} else {
// d is dummypoint.
setvertices(fliptets[0], pa, pb, pe, pd); // [a,b,e,d]
setvertices(fliptets[1], pb, pc, pe, pd); // [b,c,e,d]
setvertices(fliptets[2], pc, pa, pe, pd); // [c,a,e,d]
// Adjust the faces to [e,d,a,b], [e,d,b,c], [e,d,c,a] *
for (i = 0; i < 3; i++) {
eprevesymself(fliptets[i]);
enextself(fliptets[i]);
}
// We deleted one hull tet, and created three hull tets.
hullsize += 2;
}
} else {
setvertices(fliptets[0], pe, pd, pa, pb); // [e,d,a,b] *
setvertices(fliptets[1], pe, pd, pb, pc); // [e,d,b,c] *
setvertices(fliptets[2], pe, pd, pc, pa); // [e,d,c,a] *
}
if (calc_tetprism_vol) {
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];
tetprism_vol_sum += vol_diff; // Update the total sum.
} // if (check_tetprism_vol_diff)
// Bond three new tets together.
for (i = 0; i < 3; i++) {
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++) {
enextesym(fliptets[i], newface);
eprevself(newface); // At edges [b,a], [c,b], [a,c].
bond(newface, topcastets[i]);
}
// Bond bottom outer boundary faces (at [b,a,c,e]).
for (i = 0; i < 3; i++) {
eprevesym(fliptets[i], newface);
enextself(newface); // At edges [a,b], [b,c], [c,a].
bond(newface, botcastets[i]);
}
// Bond 15 subsegments if there are.
if (checksubsegflag) {
// The middle three: [a,b], [b,c], [c,a].
for (i = 0; i < 3; i++) {
tsspivot1(topcastets[i], checkseg);
if (checkseg.sh != NULL) {
enextesym(fliptets[i], newface);
eprevself(newface); // At edges [b,a], [c,b], [a,c].
tssbond1(newface, checkseg);
sstbond1(checkseg, newface);
if (chkencflag & 1) {
// Skip it if it has already queued.
if (!smarktest2ed(checkseg)) {
bface = (badface *) badsubsegs->alloc();
bface->ss = checkseg;
smarktest2(bface->ss); // Only queue it once.
bface->forg = sorg(checkseg); // An alive badface.
}
}
}
}
// The top three: [d,a], [d,b], [d,c]. Two tets per edge.
for (i = 0; i < 3; i++) {
eprev(topcastets[i], casface);
tsspivot1(casface, checkseg);
if (checkseg.sh != NULL) {
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 (chkencflag & 1) {
// Skip it if it has already queued.
if (!smarktest2ed(checkseg)) {
bface = (badface *) badsubsegs->alloc();
bface->ss = checkseg;
smarktest2(bface->ss); // Only queue it once.
bface->forg = sorg(checkseg); // An alive badface.
}
}
}
}
// The bot three: [a,e], [b,e], [c,e]. Two tets per edge.
for (i = 0; i < 3; i++) {
enext(botcastets[i], casface);
tsspivot1(casface, checkseg);
if (checkseg.sh != NULL) {
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 (chkencflag & 1) {
// Skip it if it has already queued.
if (!smarktest2ed(checkseg)) {
bface = (badface *) badsubsegs->alloc();
bface->ss = checkseg;
smarktest2(bface->ss); // Only queue it once.
bface->forg = sorg(checkseg); // An alive badface.
}
}
}
}
}
// Bond 6 subfaces if there are.
if (checksubfaceflag) {
for (i = 0; i < 3; i++) {
tspivot(topcastets[i], checksh);
if (checksh.sh != NULL) {
enextesym(fliptets[i], newface);
eprevself(newface); // At edge [b,a], [c,b], [a,c].
sesymself(checksh);
tsbond(newface, checksh);
if (chkencflag & 2) {
if (!smarktest2ed(checksh)) {
bface = (badface *) badsubfacs->alloc();
bface->ss = checksh;
smarktest2(bface->ss); // Only queue it once.
bface->forg = sorg(checksh); // An alive badface
}
}
}
}
for (i = 0; i < 3; i++) {
tspivot(botcastets[i], checksh);
if (checksh.sh != NULL) {
eprevesym(fliptets[i], newface);
enextself(newface); // At edge [a,b], [b,c], [c,a]
sesymself(checksh);
tsbond(newface, checksh);
if (chkencflag & 2) {
if (!smarktest2ed(checksh)) {
bface = (badface *) badsubfacs->alloc();
bface->ss = checksh;
smarktest2(bface->ss); // Only queue it once.
bface->forg = sorg(checksh); // An alive badface
}
}
}
}
}
if (chkencflag & 4) {
// Put three new tets into check list.
for (i = 0; i < 3; i++) {
if (!marktest2ed(fliptets[i])) {
bface = (badface *) badtetrahedrons->alloc();
bface->tt = fliptets[i];
marktest2(bface->tt);
bface->forg = org(fliptets[i]);
}
}
}
// Update the point-to-tet map.
setpoint2tet(pa, encode(fliptets[0]));
setpoint2tet(pb, encode(fliptets[0]));
setpoint2tet(pc, encode(fliptets[1]));
setpoint2tet(pd, encode(fliptets[0]));
setpoint2tet(pe, encode(fliptets[0]));
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 (flipflag > 0) {
// Queue faces which may be locally non-Delaunay.
//pd = dest(fliptets[0]); // 'd' may be a new vertex.
for (i = 0; i < 3; i++) {
eprevesym(fliptets[i], newface);
//flippush(flipstack, &newface, pd);
flippush(flipstack, &newface);
}
if (flipflag > 1) {
//pe = org(fliptets[0]);
for (i = 0; i < 3; i++) {
enextesym(fliptets[i], newface);
//flippush(flipstack, &newface, pe);
flippush(flipstack, &newface);
}
}
}
recenttet = fliptets[0];
}
///////////////////////////////////////////////////////////////////////////////
// //
// flip32() Perform a 3-to-2 flip (edge-to-face flip). //
// //
// 'fliptets' is an array of three tetrahedra. On input, it contains three //
// tets: [e,d,a,b], [e,d,b,c], and [e,d,c,a]. It returns tw tets: [a,b,c,d], //
// and [b,a,c,e]. The edge [e,d] is replaced by the face [a,b,c]. //
// //
// If 'hullflag' > 0, hull tets may be involved in this flip, i.e., one of //
// the five vertices may be 'dummypoint'. There are two canonical cases: //
// (1) d is 'dummypoint', then [a,b,c,d] is hull tet. If e is 'dummypoint',//
// we reconfigure e to d, i.e., turnover it. //
// (2) c is 'dummypoint' then both [a,b,c,d] and [b,a,c,e] are hull tets. //
// If a or b is 'dummypoint', we reconfigure it to c, i.e., rotate the //
// three old tets counterclockwisely (right-hand rule) until a or b //
// is in c's position. //
// //
// If 'flipflag > 0', faces on the convex hull of the five vertices might //
// need to be flipped, e.g., for incremental DT construction or mesh quality //
// improvement. They will be queued in 'flipstack'. //
// //
// If 'flipflag = 1', it is in the process of incrmental flip DT algorithm, //
// and we assume that 'a' must be the newly inserted vertex. In such case, //
// only the link faces at 'a', i.e., two faces [c,b,d] and [b,c,e] needs to //
// be queued, refer to [Edelsbrunner & Shah'1996] and [M\"ucke'1998]. //
// //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::flip32(triface* fliptets, int hullflag, int flipflag,
int chkencflag)
{
triface topcastets[3], botcastets[3];
triface newface, casface;
face checksh;
face checkseg;
badface *bface; // used by chkencflag
point pa, pb, pc, pd, pe;
REAL volneg[3], volpos[2], vol_diff; // volumes of involved tet-prisms.
int dummyflag = 0; // Rangle = {-1, 0, 1, 2}
int i;
// For 2-to-2 flip (subfaces).
face flipshs[3], flipfaces[2];
point rempt;
int spivot = -1, scount = 0;
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]);
if (b->verbose > 3) {
printf(" flip 3-to-2: (%d, %d, %d, %d, %d)\n", pointmark(pa),
pointmark(pb), pointmark(pc), pointmark(pd), pointmark(pe));
}
flip32count++;
// Get the outer boundary faces.
for (i = 0; i < 3; i++) {
enextesym(fliptets[i], casface);
eprevself(casface);
fsym(casface, topcastets[i]);
}
for (i = 0; i < 3; i++) {
eprevesym(fliptets[i], casface);
enextself(casface);
fsym(casface, botcastets[i]);
}
if (checksubfaceflag) {
// Check if there are interior subfaces at the edge [e,d].
spivot = -1;
scount = 0;
for (i = 0; i < 3; i++) {
tspivot(fliptets[i], flipshs[i]);
if (flipshs[i].sh != NULL) {
if (b->verbose > 3) {
printf(" Found an interior subface (%d, %d, %d).\n",
pointmark(sorg(flipshs[i])), pointmark(sdest(flipshs[i])),
pointmark(sapex(flipshs[i])));
}
stdissolve(flipshs[i]); // Disconnect the sub-tet bond.
scount++;
} else {
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;
}
}
// 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 changle.
}
} else {
setvertices(fliptets[0], pa, pb, pc, pd);
setvertices(fliptets[1], pb, pa, pc, pe);
}
if (calc_tetprism_vol) {
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];
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 segments to new (flipped) tets.
for (i = 0; i < 3; i++) {
tsspivot1(topcastets[i], checkseg);
if (checkseg.sh != NULL) {
tssbond1(fliptets[0], checkseg);
sstbond1(checkseg, fliptets[0]);
if (chkencflag & 1) {
// Skip it if it has already queued.
if (!smarktest2ed(checkseg)) {
bface = (badface *) badsubsegs->alloc();
bface->ss = checkseg;
smarktest2(bface->ss); // Only queue it once.
bface->forg = sorg(checkseg); // An alive badface.
}
}
}
enextself(fliptets[0]);
}
// The three top edges.
for (i = 0; i < 3; i++) {
esym(fliptets[0], newface);
eprevself(newface); // edge b->d, c->d, a->d.
enext(topcastets[i], casface);
tsspivot1(casface, checkseg);
if (checkseg.sh != NULL) {
tssbond1(newface, checkseg);
sstbond1(checkseg, newface);
if (chkencflag & 1) {
// Skip it if it has already queued.
if (!smarktest2ed(checkseg)) {
bface = (badface *) badsubsegs->alloc();
bface->ss = checkseg;
smarktest2(bface->ss); // Only queue it once.
bface->forg = sorg(checkseg); // An alive badface.
}
}
}
enextself(fliptets[0]);
}
// Process the bottom tet bace.
for (i = 0; i < 3; i++) {
tsspivot1(botcastets[i], checkseg);
if (checkseg.sh != NULL) {
tssbond1(fliptets[1], checkseg);
sstbond1(checkseg, fliptets[1]);
if (chkencflag & 1) {
// Skip it if it has already queued.
if (!smarktest2ed(checkseg)) {
bface = (badface *) badsubsegs->alloc();
bface->ss = checkseg;
smarktest2(bface->ss); // Only queue it once.
bface->forg = sorg(checkseg); // An alive badface.
}
}
}
eprevself(fliptets[1]);
}
// The three bot edges.
for (i = 0; i < 3; i++) {
esym(fliptets[1], newface);
enextself(newface); // edge b<-e, c<-e, a<-e.
eprev(botcastets[i], casface);
tsspivot1(casface, checkseg);
if (checkseg.sh != NULL) {
tssbond1(newface, checkseg);
sstbond1(checkseg, newface);
if (chkencflag & 1) {
// Skip it if it has already queued.
if (!smarktest2ed(checkseg)) {
bface = (badface *) badsubsegs->alloc();
bface->ss = checkseg;
smarktest2(bface->ss); // Only queue it once.
bface->forg = sorg(checkseg); // An alive badface.
}
}
}
eprevself(fliptets[1]);
}
}
if (checksubfaceflag) {
// Bond the top three casing subfaces.
for (i = 0; i < 3; i++) {
tspivot(topcastets[i], checksh);
if (checksh.sh != NULL) {
esym(fliptets[0], newface); // At edge [b,a], [c,b], [a,c]
sesymself(checksh);
tsbond(newface, checksh);
if (chkencflag & 2) {
if (!smarktest2ed(checksh)) {
bface = (badface *) badsubfacs->alloc();
bface->ss = checksh;
smarktest2(bface->ss); // Only queue it once.
bface->forg = sorg(checksh); // An alive badface
}
}
}
enextself(fliptets[0]);
}
// Bond the bottom three casing subfaces.
for (i = 0; i < 3; i++) {
tspivot(botcastets[i], checksh);
if (checksh.sh != NULL) {
esym(fliptets[1], newface); // // At edge [a,b], [b,c], [c,a]
sesymself(checksh);
tsbond(newface, checksh);
if (chkencflag & 2) {
if (!smarktest2ed(checksh)) {
bface = (badface *) badsubfacs->alloc();
bface->ss = checksh;
smarktest2(bface->ss); // Only queue it once.
bface->forg = sorg(checksh); // An alive badface
}
}
}
eprevself(fliptets[1]);
}
}
if (checksubfaceflag) {
if (scount > 0) {
assert(spivot != -1); // spivot = i, in {0,1,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, 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 {
// Found two subfaces are duplicated at the same tet face.
// Due to the same reason explained below.
assert(sapex(checksh) == sapex(flipfaces[0]));
sspivot(checksh, checkseg);
assert(checkseg.sh == NULL);
// Delete the two duplicated subfaces.
rempt = sapex(checksh);
if (b->verbose > 2) {
printf(" Remove vertex %d from surface.\n", pointmark(rempt));
}
// Make sure we do not delete a Steiner points in segment.
assert(pointtype(rempt) == FREEFACETVERTEX);
setpointtype(rempt, FREEVOLVERTEX);
// Re-use flipshs.
//spivot(checksh, flipshs[0]);
flipshs[0] = checksh;
spivotself(flipshs[0]);
if (flipshs[0].sh == flipfaces[0].sh) {
sesym(checksh, flipshs[0]);
spivotself(flipshs[0]);
}
assert(flipshs[0].sh != flipfaces[0].sh);
//spivot(flipfaces[0], flipshs[1]);
flipshs[1] = flipfaces[0];
spivotself(flipshs[1]);
if (flipshs[1].sh == checksh.sh) {
sesym(flipfaces[0], flipshs[1]);
spivotself(flipshs[1]);
}
assert(flipshs[1].sh != checksh.sh);
// Bond the two subfaces together.
sbond(flipshs[0], flipshs[1]);
// Detach 'checksh' from the adjacent tets.
tsdissolve(topcastets[0]);
fsymself(topcastets[0]);
tsdissolve(topcastets[0]);
// Delete the two duplicated subfaces.
shellfacedealloc(subfaces, checksh.sh);
shellfacedealloc(subfaces, flipfaces[0].sh);
}
// // Push topcastets[0] into queue for checking new sliver.
// assert(oppo(topcastets[0]) != dummypoint);
// flippush(&(topcastets[0]), oppo(topcastets[0]));
// 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 {
// Found two subfaces are duplicated at the same tet face.
assert(sapex(checksh) == sapex(flipfaces[1]));
// This happens in case when a Steiner point is not exactly coplanar
// or collinear with the subface or subedge where it was added.
// See figs illustrated in 2011-11-09.
sspivot(checksh, checkseg);
assert(checkseg.sh == NULL);
// Since the edge [p,q] is not a segment, both subfaces must be
// removed. The effect is that the Steiner point is removed from
// the surface triangulation.
// Delete the two duplicated subfaces.
rempt = sapex(checksh);
if (b->verbose > 2) {
printf(" Remove vertex %d from surface.\n", pointmark(rempt));
}
// Make sure we do not delete a Steiner points in segment.
assert(pointtype(rempt) == FREEFACETVERTEX);
setpointtype(rempt, FREEVOLVERTEX);
// Re-use flipshs.
//spivot(checksh, flipshs[0]);
flipshs[0] = checksh;
spivotself(flipshs[0]);
if (flipshs[0].sh == flipfaces[1].sh) {
sesym(checksh, flipshs[0]);
spivotself(flipshs[0]);
}
assert(flipshs[0].sh != flipfaces[1].sh);
//spivot(flipfaces[1], flipshs[1]);
flipshs[1] = flipfaces[1];
spivotself(flipshs[1]);
if (flipshs[1].sh == checksh.sh) {
sesym(flipfaces[1], flipshs[1]);
spivotself(flipshs[1]);
}
assert(flipshs[1].sh != checksh.sh);
// Bond the two subfaces together.
sbond(flipshs[0], flipshs[1]);
// Detach 'checksh' from the adjacent tets.
tsdissolve(botcastets[0]);
fsymself(botcastets[0]);
tsdissolve(botcastets[0]);
// Delete the two duplicated subfaces.
shellfacedealloc(subfaces, checksh.sh);
shellfacedealloc(subfaces, flipfaces[1].sh);
}
// // Push botcastets[0] into queue for checking new sliver.
// assert(oppo(botcastets[0]) != dummypoint);
// flippush(&(botcastets[0]), oppo(botcastets[0]));
}
}
if (chkencflag & 4) {
// Put two new tets into check list.
for (i = 0; i < 2; i++) {
if (!marktest2ed(fliptets[i])) {
bface = (badface *) badtetrahedrons->alloc();
bface->tt = fliptets[i];
marktest2(bface->tt);
bface->forg = org(fliptets[i]);
}
}
}
setpoint2tet(pa, encode(fliptets[0]));
setpoint2tet(pb, encode(fliptets[0]));
setpoint2tet(pc, encode(fliptets[0]));
setpoint2tet(pd, encode(fliptets[0]));
setpoint2tet(pe, encode(fliptets[1]));
if (hullflag > 0) {
if (dummyflag != 0) {
// 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 (flipflag > 0) {
// Queue faces which may be locally non-Delaunay.
// pa = org(fliptets[0]); // 'a' may be a new vertex.
enextesym(fliptets[0], newface);
flippush(flipstack, &newface);
eprevesym(fliptets[1], newface);
flippush(flipstack, &newface);
if (flipflag > 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];
}
///////////////////////////////////////////////////////////////////////////////
// //
// flip41() Perform a 4-to-1 flip (Remove a vertex). //
// //
// 'fliptets' is an array of four tetrahedra in the star of the removing //
// vertex 'p'. Let the four vertices in the star of p be a, b, c, and d. The //
// four tets in 'fliptets' are: [p,d,a,b], [p,d,b,c], [p,d,c,a], and [a,b,c, //
// p]. On return, 'fliptets[0]' is the new tet [a,b,c,d]. //
// //
// If 'hullflag' is set (> 0), one of the four vertices may be 'duumypoint'. //
// The 'hullsize' may be changed. //
// //
// If 'checksubface' flag is set (>0), it is possible that there are three //
// interior subfaces connecting at p. If so, a 3-to-1 flip is performed to //
// to remove p from the surface triangulation. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::flip41(triface* fliptets, int hullflag, int flipflag,
int chkencflag)
{
triface topcastets[3], botcastet;
triface newface, neightet;
face flipshs[4];
face checksh;
face checkseg;
point pa, pb, pc, pd, pp;
badface *bface; // used by chkencflag
REAL volneg[4], volpos[1], vol_diff; // volumes of involved tet-prisms.
int dummyflag = 0; // in {0, 1, 2, 3, 4}
int spivot = -1, scount = 0;
int i;
pa = org(fliptets[3]);
pb = dest(fliptets[3]);
pc = apex(fliptets[3]);
pd = dest(fliptets[0]);
pp = org(fliptets[0]); // The removing vertex.
if (b->verbose > 3) {
printf(" flip 4-to-1: (%d, %d, %d, %d, %d)\n", pointmark(pa),
pointmark(pb), pointmark(pc), pointmark(pd), pointmark(pp));
}
// flip41count++;
// 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'.
spivot = -1;
scount = 0;
for (i = 0; i < 3; i++) {
fnext(fliptets[3], newface); // [a,b,p,d],[b,c,p,d],[c,a,p,d].
tspivot(newface, flipshs[i]);
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]}.
assert(scount == 1); // spivot >= 0
// Go to the tet containing the three subfaces.
fsym(topcastets[spivot], neightet);
// Get the three subfaces connecting at p.
for (i = 0; i < 3; i++) {
esym(neightet, newface);
tspivot(newface, flipshs[i]);
assert(flipshs[i].sh != NULL);
eprevself(neightet);
}
} else {
spivot = 3; // The new subface is [a,b,c].
}
}
} // if (checksubfaceflag)
// Re-use fliptets[0] for [a,b,c,d].
fliptets[0].ver = 11;
setelemmarker(fliptets[0].tet, 0); // Clean all flags.
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);
}
// Mark the point pp as unused.
setpointtype(pp, UNUSEDVERTEX);
unuverts++;
// Create the new tet [a,b,c,d].
if (hullflag > 0) {
// One of the four vertices may be 'dummypoint'.
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);
dummyflag = 0;
}
if (dummyflag > 0) {
// We delete 3 hull tets, and create 1 hull tet.
hullsize -= 2;
}
} else {
setvertices(fliptets[0], pa, pb, pc, pd);
}
if (calc_tetprism_vol) {
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 {
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];
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) {
// Bond 6 segments (at edges of [a,b,c,d]) if there there are.
for (i = 0; i < 3; i++) {
eprev(topcastets[i], newface); // At edges [d,a],[d,b],[d,c].
tsspivot1(newface, checkseg);
if (checkseg.sh != NULL) {
esym(fliptets[0], newface);
enextself(newface); // At edges [a,d], [b,d], [c,d].
tssbond1(newface, checkseg);
sstbond1(checkseg, newface);
if (chkencflag & 1) {
// Skip it if it has already queued.
if (!smarktest2ed(checkseg)) {
bface = (badface *) badsubsegs->alloc();
bface->ss = checkseg;
smarktest2(bface->ss); // Only queue it once.
bface->forg = sorg(checkseg); // An alive badface.
}
}
}
enextself(fliptets[0]);
}
for (i = 0; i < 3; i++) {
tsspivot1(topcastets[i], checkseg); // At edges [a,b],[b,c],[c,a].
if (checkseg.sh != NULL) {
tssbond1(fliptets[0], checkseg);
sstbond1(checkseg, fliptets[0]);
if (chkencflag & 1) {
// Skip it if it has already queued.
if (!smarktest2ed(checkseg)) {
bface = (badface *) badsubsegs->alloc();
bface->ss = checkseg;
smarktest2(bface->ss); // Only queue it once.
bface->forg = sorg(checkseg); // An alive badface.
}
}
}
enextself(fliptets[0]);
}
}
if (checksubfaceflag) {
// Bond 4 subfaces (at faces of [a,b,c,d]) if there are.
for (i = 0; i < 3; i++) {
tspivot(topcastets[i], checksh); // At faces [a,b,d],[b,c,d],[c,a,d]
if (checksh.sh != NULL) {
esym(fliptets[0], newface); // At faces [b,a,d],[c,b,d],[a,c,d]
sesymself(checksh);
tsbond(newface, checksh);
if (chkencflag & 2) {
if (!smarktest2ed(checksh)) {
bface = (badface *) badsubfacs->alloc();
bface->ss = checksh;
smarktest2(bface->ss); // Only queue it once.
bface->forg = sorg(checksh); // An alive badface
}
}
}
enextself(fliptets[0]);
}
tspivot(botcastet, checksh); // At face [b,a,c]
if (checksh.sh != NULL) {
sesymself(checksh);
tsbond(fliptets[0], checksh);
if (chkencflag & 2) {
if (!smarktest2ed(checksh)) {
bface = (badface *) badsubfacs->alloc();
bface->ss = checksh;
smarktest2(bface->ss); // Only queue it once.
bface->forg = sorg(checksh); // An alive badface
}
}
}
}
if (checksubfaceflag) {
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 (chkencflag & 4) {
// Put the new tet into check list.
if (!marktest2ed(fliptets[0])) {
bface = (badface *) badtetrahedrons->alloc();
bface->tt = fliptets[0];
marktest2(bface->tt);
bface->forg = org(fliptets[0]);
}
}
// Update the point-to-tet map.
setpoint2tet(pa, encode(fliptets[0]));
setpoint2tet(pb, encode(fliptets[0]));
setpoint2tet(pc, encode(fliptets[0]));
setpoint2tet(pd, encode(fliptets[0]));
if (flipflag > 0) {
// Queue faces which may be locally non-Delaunay.
for (i = 0; i < 3; i++) {
esym(fliptets[0], newface);
flippush(flipstack, &newface);
enextself(fliptets[0]);
}
flippush(flipstack, &(fliptets[0]));
}
recenttet = fliptets[0];
}
///////////////////////////////////////////////////////////////////////////////
// //
// flipnm() Try to flip an edge through a sequence of elementary flips. //
// //
// 'abtets' is an array of 'n' tets in the star of edge [a,b].These tets are //
// ordered in a counterclockwise cycle with respect to the vector a->b, i.e.,//
// use the right-hand rule. //
// //
// 'level' (>= 0) indicates the current link level. If 'level > 0', we are //
// flipping a link edge of an edge [a',b'], and 'abedgepivot' indicates //
// which link edge, i.e., [c',b'] or [a',c'], is [a,b] These two parameters //
// allow us to determine the new tets after a 3-to-2 flip, i.e., tets that //
// do not inside the reduced star of edge [a',b']. //
// //
// If the flag 'fc->unflip' is set, this routine un-does the flips performed //
// in flipnm([a,b]) so that the mesh is returned to its original state //
// before doing the flipnm([a,b]) operation. //
// //
// The return value is an integer nn, where nn <= n. If nn is 2, then the //
// edge is flipped. The first and the second tets in 'abtets' are new tets. //
// Otherwise, nn > 2, the edge is not flipped, and nn is the number of tets //
// in the current star of [a,b]. //
// //
// ASSUMPTIONS: //
// - Neither a nor b is 'dummypoint'. //
// - [a,b] must not be a segment. //
// //
// //
///////////////////////////////////////////////////////////////////////////////
int tetgenmesh::flipnm(triface* abtets, int n, int level, int abedgepivot,
flipconstraints* fc)
{
triface fliptets[3], spintet, flipedge;
triface *tmpabtets, *parytet;
face checksh;
face checkseg, *paryseg;
point pa, pb, pc, pd, pe, pf;
point tmppts[3];
REAL abovept[3];
REAL ori, ori1, ori2;
int reducflag, rejflag;
int hullflag;
int reflexlinkedgecount;
int edgepivot;
int n1, nn;
int i, j;
pa = org(abtets[0]);
pb = dest(abtets[0]);
if (b->verbose > 2) {
printf(" flipnm(%d): (%d, %d) - n(%d), e(%d).\n", level, pointmark(pa),
pointmark(pb), n, abedgepivot);
}
if (n > 3) {
// Try to reduce the size of the Star(ab) by flipping a face in it.
reflexlinkedgecount = 0;
for (i = 0; i < n; i++) {
// Let the face of 'abtets[i]' be [a,b,c].
if (checksubfaceflag) {
// Do not flip this face if it is a constraining face.
tspivot(abtets[i], checksh);
if (checksh.sh != NULL) {
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)) {
// [a,b,c] is a hull face, it is not flipable.
continue;
}
if (checkinverttetflag) {
// The mesh contains inverted (or degenerated) elements.
// Only do check if both elements are valid.
if (pc != dummypoint) {
ori = orient3d(pa, pb, pc, pd);
if (ori < 0) {
ori = orient3d(pb, pa, pc, pe);
}
if (ori >= 0) {
continue; // An invalid tet.
}
} else {
continue;
}
} // if (checkinverttetflag)
reducflag = 0; // Not reducible.
hullflag = (pc == dummypoint); // pc may be dummypoint.
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 immedately by a 3-to-2 flip.
reducflag = 1;
}
}
}
}
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]);
assert(pf != dummypoint);
ori = orient3d(pd, pe, pf, pa);
if (ori < 0) {
ori = orient3d(pe, pd, pf, pb);
if (ori < 0) {
// Found a 4-to-4 flip: [a,b] => [e,d]
reducflag = 1;
ori = 0; // Signal as a 4-to-4 flip (like a co-palanar case).
}
}
}
} // if (hullflag)
if (reducflag) {
// [a,b,c] could be removed by a 2-to-3 flip.
rejflag = 0;
if (fc != NULL) {
// 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, 0, 0);
// Shrink the array 'abtets', maintain the original order.
// Two tets 'abtets[i-1] ([a,b,e,c])' and 'abtets[i] ([a,b,c,d])'
// 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_|
//
eprevself(fliptets[0]);
esymself(fliptets[0]);
enextself(fliptets[0]); // [a,b,e,d]
// Increase the counter of this new tet (it is in Star(ab)).
increaseelemcounter(fliptets[0]); //marktest(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 remebers the vertex 'c' (in 'abtets[n-1].tet'), and
// (ii) it remebers the position [i] where this flip took place.
// These informations let us to either undo this flip or recover
// the original edge link (for collecting new created tets).
//abtets[n - 1] = fliptets[1]; // [e,d,b,c] is remebered.
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].
}
}
// Star(ab) is reduced. Try to flip the edge [a,b].
nn = flipnm(abtets, n - 1, level, abedgepivot, fc);
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 degenrated
// tet. It must be removed.
// Remeber that 'abtets[i-1]' is [a,b,e,d]. We can use it to
// find another two tets [e,d,b,c] and [e,d,c,a].
fliptets[0] = abtets[(i-1 + (n-1)) % (n-1)]; // [a,b,e,d]
eprevself(fliptets[0]);
esymself(fliptets[0]);
enextself(fliptets[0]); // [e,d,a,b]
fnext(fliptets[0], fliptets[1]); // [1] is [e,d,b,c]
fnext(fliptets[1], fliptets[2]); // [2] is [e,d,c,a]
assert(apex(fliptets[0]) == oppo(fliptets[2])); // SELF_CHECK
// Restore the two original tets in Star(ab).
flip32(fliptets, hullflag, 0, 0);
// Marktest the two restored tets in Star(ab).
for (j = 0; j < 2; j++) {
increaseelemcounter(fliptets[j]); //marktest(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 (upflip || (ori == 0))
} // if (nn > 2)
if (nn == 2) { //if ((nn == 2) || !fullsearch) {
// The edge has been flipped.
return nn;
}
if (!fc->unflip) {
// The flips are not reversed. The current Star(ab) can not be
// further reduced. Return its size (# of tets).
return nn;
}
// unflip is set.
// Continue the search for flips.
} else {
if (b->verbose > 2) {
printf(" -- Reject a 2-to-3 flip at star face (%d, %d, %d)",
pointmark(pa), pointmark(pb), pointmark(pc));
printf(", link (%d)\n", level);
}
if (fc != NULL) {
fc->rejf23count++;
}
} // if (rejflag)
} // if (reducflag)
} // i
// 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))) {
// Record the largest level.
if ((level + 1) > maxfliplinklevel) {
maxfliplinklevel = level + 1;
}
if (fc != NULL) {
// Increase the link level counter.
if ((level + 1) > fc->maxflippedlinklevelcount) {
fc->maxflippedlinklevelcount = level + 1;
}
}
// Try to reduce the Star(ab) by flipping a reflex edge in Link(ab).
for (i = 0; i < n; i++) {
// Do not flip this face [a,b,c] if there are two Stars involved.
if ((elemcounter(abtets[i]) > 1) ||
(elemcounter(abtets[(i - 1 + n) % n]) > 1)) {
continue;
}
pc = apex(abtets[i]);
if (pc == dummypoint) {
continue; // [a,b,dummypoint] is a hull edge.
}
pd = apex(abtets[(i + 1) % n]);
pe = apex(abtets[(i - 1 + n) % n]);
if ((pd == dummypoint) || (pe == dummypoint)) {
continue; // [a,b,c] is a hull face.
}
if (checkinverttetflag) {
// The mesh contains inverted (or degenerated) elements.
// Only do check if both elements are valid.
// assert(pc != dummypoint);
ori = orient3d(pa, pb, pc, pd);
if (ori < 0) {
ori = orient3d(pb, pa, pc, pe);
}
if (ori >= 0) {
continue; // An invalid tet.
}
} // if (checkinverttetflag)
edgepivot = 0; // No edge is selected yet.
// Test if [b,c] is locally convex or flat.
ori = orient3d(pb, pc, pd, pe);
if (ori <= 0) {
// Select the edge [c,b].
enext(abtets[i], flipedge); // [b,c,a,d]
edgepivot = 1;
}
if (!edgepivot) {
// Test if [c,a] is locally convex or flat.
ori = orient3d(pc, pa, pd, pe);
if (ori <= 0) {
// Select the edge [a,c].
eprev(abtets[i], flipedge); // [c,a,b,d].
edgepivot = 2;
}
}
if (!edgepivot) continue;
// An edge is selected.
if (checksubsegflag) {
// Do not flip it if it is a segment.
tsspivot1(flipedge, checkseg);
if (checkseg.sh != NULL) {
if (b->verbose > 2) {
printf(" -- Can't flip a link(%d) segment (%d, %d).\n",
level, pointmark(org(flipedge)), pointmark(dest(flipedge)));
}
if (fc != NULL) {
fc->encsegcount++;
if (fc->collectencsegflag) {
if (!sinfected(checkseg)) {
// Queue this segment in list.
sinfect(checkseg);
caveencseglist->newindex((void **) &paryseg);
*paryseg = checkseg;
}
}
}
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)); //if (marktested(spintet)) j++;
fnextself(spintet);
if (spintet.tet == flipedge.tet) break;
}
assert(n1 >= 3);
if (j > 2) {
// The Star(flipedge) overlaps other Stars.
continue; // Do not flip this edge.
}
// Only two tets can be marktested.
assert(j == 2);
flipstarcount++;
// Record the maximum star size.
if (n1 > maxflipstarsize) {
maxflipstarsize = n1;
}
if ((b->flipstarsize > 0) && (n1 > b->flipstarsize)) {
// The star size exceeds the given limit (-LL__).
skpflipstarcount++;
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;
}
// SELF_CHECK BEGIN
// These two tets are inside both of the Stars.
assert(elemcounter(tmpabtets[0]) == 2);
assert(elemcounter(tmpabtets[1]) == 2);
// Marktest the tets in Star(flipedge) but not in Star(ab).
for (j = 2; j < n1; j++) {
assert(elemcounter(tmpabtets[j]) == 1);
//marktest(tmpabtets[j]);
}
// Try to flip the selected edge away.
nn = flipnm(tmpabtets, n1, level + 1, edgepivot, fc);
if (nn == 2) {
// The edge is flipped. Star(ab) is reduced.
// Shrink the array 'abtets', maintain the original order.
if (edgepivot == 1) {
// 'tmpabtets[0]' is [d,a,e,b] => contains [a,b].
spintet = tmpabtets[0]; // [d,a,e,b]
enextself(spintet);
esymself(spintet);
enextself(spintet); // [a,b,e,d]
} else {
// 'tmpabtets[1]' is [b,d,e,a] => contains [a,b].
spintet = tmpabtets[1]; // [b,d,e,a]
eprevself(spintet);
esymself(spintet);
eprevself(spintet); // [a,b,e,d]
} // edgepivot == 2
//assert(!marktested(spintet)); // It's a new tet.
assert(elemcounter(spintet) == 0);
//marktest(spintet); // It is in Star(ab).
increaseelemcounter(spintet);
// Put the new tet at [i-1]-th entry.
abtets[(i - 1 + n) % n] = spintet;
for (j = i; j < n - 1; j++) {
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 is not flipped.
if (fc->unflip) {
// Recover the flipped edge ([c,b] or [a,c]).
assert(nn == (n - 1));
// The sequence of flips are saved in 'tmpabtets'.
// abtets[(i-1) % (n-1)] is [a,b,e,d], i.e., the tet created by
// the flipping of edge [c,b] or [a,c].It must still exist in
// Star(ab). It is the start tet to recover the flipped edge.
if (edgepivot == 1) {
// The flip edge is [c,b].
tmpabtets[0] = abtets[((i-1)+(n-1))%(n-1)]; // [a,b,e,d]
eprevself(tmpabtets[0]);
esymself(tmpabtets[0]);
eprevself(tmpabtets[0]); // [d,a,e,b]
fsym(tmpabtets[0], tmpabtets[1]); // [a,d,e,c]
} else {
// The flip edge is [a,c].
tmpabtets[1] = abtets[((i-1)+(n-1))%(n-1)]; // [a,b,e,d]
enextself(tmpabtets[1]);
esymself(tmpabtets[1]);
enextself(tmpabtets[1]); // [b,d,e,a]
fsym(tmpabtets[1], tmpabtets[0]); // [d,b,e,c]
} // if (edgepivot == 2)
// Recover the flipped edge ([c,b] or [a,c]).
flipnm_post(tmpabtets, n1, 2, edgepivot, fc);
// Insert the two recovered tets into Star(ab).
for (j = n - 2; j >= i; j--) {
abtets[j + 1] = abtets[j]; // Downshift
}
if (edgepivot == 1) {
// tmpabtets[0] is [c,b,d,a] ==> contains [a,b]
// tmpabtets[1] is [c,b,a,e] ==> contains [a,b]
// tmpabtets[2] is [c,b,e,d]
fliptets[0] = tmpabtets[1];
enextself(fliptets[0]);
esymself(fliptets[0]); // [a,b,e,c]
fliptets[1] = tmpabtets[0];
esymself(fliptets[1]);
eprevself(fliptets[1]); // [a,b,c,d]
} else {
// tmpabtets[0] is [a,c,d,b] ==> contains [a,b]
// tmpabtets[1] is [a,c,b,e] ==> contains [a,b]
// tmpabtets[2] is [a,c,e,d]
fliptets[0] = tmpabtets[1];
eprevself(fliptets[0]);
esymself(fliptets[0]); // [a,b,e,c]
fliptets[1] = tmpabtets[0];
esymself(fliptets[1]);
enextself(fliptets[1]); // [a,b,c,d]
} // edgepivot == 2
for (j = 0; j < 2; j++) {
assert(elemcounter(fliptets[j]) == 0); // SELF_CHECK
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 (nn == 2) { //if ((nn == 2) || !fullsearch) {
// The edge has been flipped.
return nn;
}
if (!fc->unflip) {
// The flips are not reversed. The current Star(ab) can not be
// further reduced. Return its size (# of tets).
return nn;
}
// unflip is set.
// Continue the search for flips.
} else {
// The seclected edge is not flipped.
if (fc->unflip) {
// The memory should already be freed.
assert(nn == n1);
} else {
// Release the memory used in this attempted flip.
flipnm_post(tmpabtets, n1, nn, edgepivot, fc);
}
// Decrease the star counters of tets in Star(flipedge).
for (j = 0; j < nn; j++) {
assert(elemcounter(tmpabtets[j]) > 0); // SELF_CHECK
decreaseelemcounter(tmpabtets[j]);
}
// Release the allocated spaces.
delete [] tmpabtets;
}
} // i
} else {
if (b->verbose > 2) {
printf(" -- Maximal link level (%d) reached at edge (%d, %d).\n",
level, pointmark(org(abtets[0])), pointmark(dest(abtets[0])));
}
if (fc != NULL) {
fc->misfliplinklevelcount++;
}
} // if (level...)
} // if (reflexlinkedgecount > 0)
} else {
// Check if a 3-to-2 flip is possible.
pc = apex(abtets[0]);
pd = apex(abtets[1]);
pe = apex(abtets[2]);
// Check if one of them is dummypoint. If so, we rearrange the vertices
// c, d, and e into p0, p1, and p2, such that p2 is the dummypoint.
hullflag = 0;
if (pc == dummypoint) {
hullflag = 1;
tmppts[0] = pd;
tmppts[1] = pe;
tmppts[2] = pc;
} else if (pd == dummypoint) {
hullflag = 1;
tmppts[0] = pe;
tmppts[1] = pc;
tmppts[2] = pd;
} else if (pe == dummypoint) {
hullflag = 1;
tmppts[0] = pc;
tmppts[1] = pd;
tmppts[2] = pe;
} else {
tmppts[0] = pc;
tmppts[1] = pd;
tmppts[2] = pe;
}
reducflag = 0;
rejflag = 0;
if (checkinverttetflag) {
// Only do flip if no tet is inverted (or degenerated).
if (hullflag == 0) {
ori = orient3d(pa, pb, pc, pd);
if (ori < 0) {
ori = orient3d(pa, pb, pd, pe);
if (ori < 0) {
ori = orient3d(pa, pb, pe, pc);
}
}
} else {
ori = orient3d(pa, pb, tmppts[0], tmppts[1]);
}
if (ori >= 0) {
if (b->verbose > 2) {
printf(" -- Hit a non-valid tet (%d, %d) - (%d, %d, %d)",
pointmark(pa), pointmark(pb), pointmark(pc), pointmark(pd),
pointmark(pe));
printf(" at link(%d)\n", level);
}
return 3;
}
} // if (checkinverttetflag)
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 (b->verbose > 2) {
printf(" -- Hit a chrismastree (%d, %d) - (%d, %d, %d)",
pointmark(pa), pointmark(pb), pointmark(pc), pointmark(pd),
pointmark(pe));
printf(" at link(%d)\n", level);
}
if (fc != NULL) {
fc->chrismastreecount++;
}
}
} else {
// [a,b] is a hull edge. Moreover, the tet [a,b,p0,p1] is a hull tet
// ([a,b,p0] and [a,b,p1] are two hull faces).
// This can happen when it is in the middle of a 4-to-4 flip.
// Note that [a,b] may even be a non-convex hull edge.
if (!nonconvex) {
// [a,b], [a,b,p0] and [a,b,p1] are on the convex hull.
ori = orient3d(pa, pb, tmppts[0], tmppts[1]);
if (ori == 0) {
// They four vertices are coplanar. A 2-to-2 flip is possible if
// [a,b] and [p0,p1] are intersecting each other.
// NOTE: The following test is not robust, should be replaced in
// the future. 2011-12-01.
calculateabovepoint4(pa, pb, tmppts[0], tmppts[1]);
for (j = 0; j < 3; j++) {
abovept[j] = dummypoint[j];
}
// Make sure that no inverted face will be created, i.e., [p1,p0,
// abvpt,pa] and [p0,p1,abvpt,pb] must be valid tets.
ori1 = orient3d(tmppts[0], tmppts[1], abovept, pa);
ori2 = orient3d(tmppts[0], tmppts[1], abovept, pb);
if (ori1 * ori2 < 0) {
reducflag = 1; // Flipable.
}
if (!reducflag) {
if (b->verbose > 2) {
printf(" -- Hit a degenerate chrismastree (%d, %d)",
pointmark(pa), pointmark(pb));
printf(" - (%d, %d, -1) at link(%d)\n",
pointmark(tmppts[0]), pointmark(tmppts[1]), level);
}
if (fc != NULL) {
fc->chrismastreecount++;
}
}
} else {
if (b->verbose > 2) {
printf(" -- Hit a convex hull edge (%d, %d) at link(%d).\n",
pointmark(pa), pointmark(pb), level);
}
if (fc != NULL) {
fc->convexhulledgecount++;
}
}
} else { // if (nonconvex)
// [a,b,p0] and [a,b,p1] must be two subfaces.
// Since [a,b] is not a segment. A 3-to-2 flip (including a 2-to-2
// flip) is possible.
// Here we only do flip if there are exactly three tets containing
// the edge [p0,p1]. In this case, the other two tets at [p0,p1]
// (not [a,b,p0,p1]) must be valid. Since they already exist.
for (j = 0; j < 3; j++) {
if (apex(abtets[j]) == dummypoint) {
flipedge = abtets[(j + 1) % 3]; // [a,b,p0,p1].
break;
}
}
// assert(j < 3);
eprevself(flipedge);
esymself(flipedge);
enextself(flipedge); // [p0,p1,a,b].
assert(apex(flipedge) == pa);
spintet = flipedge;
j = 0;
while (1) {
j++;
fnextself(spintet);
if (spintet.tet == flipedge.tet) break;
}
if (j == 3) {
reducflag = 1;
} else {
if (b->verbose > 2) {
printf(" -- Hit a hull edge (%d, %d) at link(%d).\n",
pointmark(pa), pointmark(pb), level);
}
//if (fc != NULL) {
// fc->convexhulledgecount++;
//}
}
}
} // 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;
for (j = 0; j < 3; j++) {
tspivot(abtets[j], checksh);
if (checksh.sh != NULL) {
nn++; // Found a subface.
}
}
assert(nn < 3);
if (nn == 1) {
// Found only 1 subface containing this edge. This can happen in
// the boundary recovery phase. The neighbor subface is not yet
// recovered. This edge should not be flipped at this moment.
rejflag = 1;
}
}
if (!rejflag && (fc != NULL)) {
//rejflag = checkflipeligibility(2, tmppts[0], tmppts[1], tmppts[2],
// pa, pb, level, abedgepivot, fc);
// Here we must permute 'a' and 'b'. Since in the check... function,
// we assume the following point sequence, 'a,b,c,d,e', where
// the face [a,b,c] will be flipped and the edge [e,d] will be
// created. The two new tets are [a,b,c,d] and [b,a,c,e].
rejflag = checkflipeligibility(2, tmppts[0], tmppts[1], tmppts[2],
pb, pa, level, abedgepivot, fc);
}
if (!rejflag) {
// Do flip: [a,b] => [c,d,e]
flip32(abtets, hullflag, 0, 0);
sucflipstarcount++;
if (fc->remove_ndelaunay_edge) {
if (level == 0) {
// It is the desired removing edge.
if (tetprism_vol_sum >= fc->bak_tetprism_vol) {
if (b->verbose > 2) {
printf(" -- Reject to flip (%d, %d) at link(%d)\n",
pointmark(pa), pointmark(pb), level);
printf(" due to an increased volume (%.17g).\n",
tetprism_vol_sum - fc->bak_tetprism_vol);
}
// flip back: [c,d,e] => [a,b].
flip23(abtets, hullflag, 0, 0);
// Increase the element counter -- They are in cavity.
for (j = 0; j < 3; j++) {
increaseelemcounter(abtets[j]);
}
return 3;
}
} // if (level == 0)
}
if (fc->collectnewtets) {
// Collect new tets.
if (level == 0) {
// Push the two new tets into stack.
for (j = 0; j < 2; j++) {
cavetetlist->newindex((void **) &parytet);
*parytet = abtets[j];
}
} else {
// Only one of the new tets is collected. The other one is inside
// the reduced edge star. 'abedgepivot' is either '1' or '2'.
cavetetlist->newindex((void **) &parytet);
if (abedgepivot == 1) { // [c,b]
*parytet = abtets[1];
} else {
assert(abedgepivot == 2); // [a,c]
*parytet = abtets[0];
}
}
} // if (fc->collectnewtets)
return 2;
} else {
if (b->verbose > 2) {
printf(" -- Reject a 3-to-2 flip (%d, %d) at link(%d).\n",
pointmark(pa), pointmark(pb), level);
}
if (fc != NULL) {
fc->rejf32count++;
}
} // if (rejflag)
} // if (reducflag)
} // if (n == 3)
// The current (reduced) Star size.
return n;
}
///////////////////////////////////////////////////////////////////////////////
// //
// flipnm_post() Post process a n-to-m flip. //
// //
// IMPORTANT: This routine only works when there is no other flip operation //
// is done after flipnm([a,b]) which attempts to remove an edge [a,b]. //
// //
// 'abtets' is an array of 'n' (>= 3) tets which are in the original star of //
// [a,b] before flipnm([a,b]). 'nn' (< n) is the value returned by flipnm. //
// If 'nn == 2', the edge [a,b] has been flipped. 'abtets[0]' and 'abtets[1]'//
// are [c,d,e,b] and [d,c,e,a], i.e., a 2-to-3 flip can recover the edge [a, //
// b] and its initial Star([a,b]). If 'nn >= 3' edge [a,b] still exists in //
// current mesh and 'nn' is the current number of tets in Star([a,b]). //
// //
// Each 'abtets[i]', where nn <= i < n, saves either a 2-to-3 flip or a //
// flipnm([p1,p2]) operation ([p1,p2] != [a,b]) which created the tet //
// 'abtets[t-1]', where '0 <= t <= i'. These information can be used to //
// undo the flips performed in flipnm([a,b]) or to collect new tets created //
// by the flipnm([a,b]) operation. //
// //
// Default, this routine only walks through the flips and frees the spaces //
// allocated during the flipnm([a,b]) operation. //
// //
// If the flag 'fc->unflip' is set, this routine un-does the flips performed //
// in flipnm([a,b]) so that the mesh is returned to its original state //
// before doing the flipnm([a,b]) operation. //
// //
// //
///////////////////////////////////////////////////////////////////////////////
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, 0, 0);
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++;
} else { // nn > 2.
// The edge [a,b] exists.
}
// Walk through the performed flips.
for (i = nn; i < n; i++) {
// At the beginning of each step 'i', the size of the Star([a,b]) is 'i'.
// At the end of this step, the size of the Star([a,b]) is 'i+1'.
// The sizes of the Link([a,b]) are the same.
fliptype = ((abtets[i].ver >> 4) & 3); // 0, 1, or 2.
if (fliptype == 1) {
// It was a 2-to-3 flip: [a,b,c]->[e,d].
t = (abtets[i].ver >> 6);
assert(t <= i);
if (fc->unflip) {
if (b->verbose > 2) {
printf(" Recover a 2-to-3 flip at f[%d].\n", t);
}
// 'abtets[(t-1)%i]' is the tet [a,b,e,d] in current Star(ab), i.e.,
// it is created by a 2-to-3 flip [a,b,c] => [e,d].
fliptets[0] = abtets[((t - 1) + i) % i]; // [a,b,e,d]
eprevself(fliptets[0]);
esymself(fliptets[0]);
enextself(fliptets[0]); // [e,d,a,b]
fnext(fliptets[0], fliptets[1]); // [e,d,b,c]
fnext(fliptets[1], fliptets[2]); // [e,d,c,a]
// Do a 3-to-2 flip: [e,d] => [a,b,c].
// NOTE: hull tets may be invloved.
flip32(fliptets, 1, 0, 0);
// Expand the array 'abtets', maintain the original order.
// The new array length is (i+1).
for (j = i - 1; j >= t; j--) {
abtets[j + 1] = abtets[j]; // Downshift
}
// The tet abtets[(t-1)%i] is deleted. Insert the two new tets
// 'fliptets[0]' [a,b,c,d] and 'fliptets[1]' [b,a,c,e] into
// the (t-1)-th and t-th entries, respectively.
esym(fliptets[1], abtets[((t-1) + (i+1)) % (i+1)]); // [a,b,e,c]
abtets[t] = fliptets[0]; // [a,b,c,d]
if (fc->collectnewtets) {
// Pop up two (flipped) tets from the stack.
cavetetlist->objects -= 2;
}
}
} else if (fliptype == 2) {
tmpabtets = (triface *) (abtets[i].tet);
n1 = ((abtets[i].ver >> 19) & 8191); // \sum_{i=0^12}{2^i} = 8191
edgepivot = (abtets[i].ver & 3);
t = ((abtets[i].ver >> 6) & 8191);
assert(t <= i);
if (fc->unflip) {
if (b->verbose > 2) {
printf(" Recover a %d-to-m flip at e[%d] of f[%d].\n", n1,
edgepivot, t);
}
// Recover the flipped edge ([c,b] or [a,c]).
// abtets[(t - 1 + i) % i] is [a,b,e,d], i.e., the tet created by
// the flipping of edge [c,b] or [a,c]. It must still exist in
// Star(ab). Use it to recover the flipped edge.
if (edgepivot == 1) {
// The flip edge is [c,b].
tmpabtets[0] = abtets[(t - 1 + i) % i]; // [a,b,e,d]
eprevself(tmpabtets[0]);
esymself(tmpabtets[0]);
eprevself(tmpabtets[0]); // [d,a,e,b]
fsym(tmpabtets[0], tmpabtets[1]); // [a,d,e,c]
} else {
// The flip edge is [a,c].
tmpabtets[1] = abtets[(t - 1 + i) % i]; // [a,b,e,d]
enextself(tmpabtets[1]);
esymself(tmpabtets[1]);
enextself(tmpabtets[1]); // [b,d,e,a]
fsym(tmpabtets[1], tmpabtets[0]); // [d,b,e,c]
} // if (edgepivot == 2)
// Do a n1-to-m1 flip to recover the flipped edge ([c,b] or [a,c]).
flipnm_post(tmpabtets, n1, 2, edgepivot, fc);
// Insert the two recovered tets into the original Star(ab).
for (j = i - 1; j >= t; j--) {
abtets[j + 1] = abtets[j]; // Downshift
}
if (edgepivot == 1) {
// tmpabtets[0] is [c,b,d,a] ==> contains [a,b]
// tmpabtets[1] is [c,b,a,e] ==> contains [a,b]
// tmpabtets[2] is [c,b,e,d]
fliptets[0] = tmpabtets[1];
enextself(fliptets[0]);
esymself(fliptets[0]); // [a,b,e,c]
fliptets[1] = tmpabtets[0];
esymself(fliptets[1]);
eprevself(fliptets[1]); // [a,b,c,d]
} else {
// tmpabtets[0] is [a,c,d,b] ==> contains [a,b]
// tmpabtets[1] is [a,c,b,e] ==> contains [a,b]
// tmpabtets[2] is [a,c,e,d]
fliptets[0] = tmpabtets[1];
eprevself(fliptets[0]);
esymself(fliptets[0]); // [a,b,e,c]
fliptets[1] = tmpabtets[0];
esymself(fliptets[1]);
enextself(fliptets[1]); // [a,b,c,d]
} // edgepivot == 2
// Insert the two recovered tets into Star(ab).
abtets[((t-1) + (i+1)) % (i+1)] = fliptets[0];
abtets[t] = fliptets[1];
}
else {
// Only free the spaces.
flipnm_post(tmpabtets, n1, 2, edgepivot, fc);
} // if (!unflip)
if (b->verbose > 2) {
printf(" Release %d spaces at f[%d].\n", n1, i);
}
delete [] tmpabtets;
} else {
assert(fliptype == 0); // Not a saved flip.
assert(0); // Should be not possible.
}
} // i
return 1;
}
///////////////////////////////////////////////////////////////////////////////
// //
// lawsonflip3d() A three-dimensional Lawson's flip algorithm. //
// //
// The basic idea of Lawson's algorithm is to flip every face of the triang- //
// ulation which is not locally Delaunay until no such face exists, then the //
// triangulation is a DT. However, in 3D, it is common that a face which is //
// not locally Delaunay and is not flippable. Hence, Laowson's algorithm may //
// get stuck. It is still an open problem, whether there exists a flip algo- //
// rithm which has a guarantee to create a DT in 3D. //
// //
// If only one vertex is added into a DT, then Lawson's flip algorithm is //
// guaranteed to transform it into a new DT [Joe'91]. Moreover, an arbitrary //
// order of flips is sufficient [Edelsbrunner & Shah'96]. //
// //
// In practice, it is desired to remove not locally Delaunay faces by flips //
// as many as possible. For this purpose, a second queue is used to store //
// the not locally Delaunay faces which are not flippable, and try them at a //
// later time. //
// //
// If 'newpt' (p) is not NULL, it is a new vertex just inserted into the //
// tetrahedralization T. //
// //
// 'flipflag' indicates the property of the tetrahedralization 'T' which //
// does not include 'p' yet. //
// //
// If 'peelsliverflag' is set, the purpose of calling Lawson's flip is to //
// remove "hull slivers". This flag only works with a non-convex mesh, i.e., //
// the mesh must contains boundaries (segments and subfaces). //
// //
// 'chkencflag' indicates whether segments, subfaces, and tets should be //
// checked (for encroaching and quality) after flips. //
// //
///////////////////////////////////////////////////////////////////////////////
long tetgenmesh::lawsonflip3d(point newpt, int flipflag, int peelsliverflag,
int chkencflag, int flipedgeflag)
{
badface *popface, *bface;
triface fliptets[5], baktets[2];
triface fliptet, neightet, *parytet;
face checksh, *parysh;
face checkseg, *paryseg;
point *ppt, pd, pe, pf;
long flipcount;
REAL sign, ori;
int convflag;
int n, i;
// For removing hull slivers.
face neighsh;
point p1, p2;
point pa, pb, pc, rempt;
REAL ang;
long tetpeelcount;
int remflag;
flipconstraints fc;
if (b->verbose > 2) {
printf(" Lawson flip %ld faces.\n", flippool->items);
}
flipcount = flip23count + flip32count + flip44count;
tetpeelcount = opt_sliver_peels;
if (flipedgeflag) {
fc.remove_ndelaunay_edge = 1;
fc.unflip = 1; // Unflip if the edge is not flipped.
fc.collectnewtets = 1;
assert(cavetetlist->objects == 0l);
assert(calc_tetprism_vol == 1); // Swith on.
} else {
assert(unflipqueue->objects == 0); // The second queue must be empty.
}
while (1) {
while (flipstack != (badface *) NULL) {
// Pop a face from the stack.
popface = flipstack;
flipstack = flipstack->nextitem; // The next top item in stack.
fliptet = popface->tt;
flippool->dealloc((void *) popface);
// Skip it if it is a dead tet (destroyed by previous flips).
if (isdeadtet(fliptet)) continue;
// Skip it if it is not the same tet as we saved.
if (!facemarked(fliptet)) continue;
unmarkface(fliptet);
// FOR DEBUG
if (flipflag == 1) {
assert(oppo(fliptet) == newpt);
}
if (ishulltet(fliptet)) {
// It is a hull tet.
if (((flipflag == 4) || peelsliverflag) && !b->convex) {
fliptet.ver = epivot[fliptet.ver & 3];
if (oppo(fliptet) == dummypoint) {
// It's a hull face (oppo(fliptet) == dummypoint).
// Check if there exists a "hull sliver".
fsymself(fliptet);
tspivot(fliptet, checksh);
assert(checksh.sh != NULL);
for (i = 0; i < 3; i++) {
sspivot(checksh, checkseg);
if (checkseg.sh == NULL) {
spivot(checksh, neighsh);
assert(neighsh.sh != NULL);
if (sorg(checksh) != sdest(neighsh)) {
sesymself(neighsh);
}
stpivot(neighsh, neightet);
if (neightet.tet == fliptet.tet) {
// Found a hull sliver 'neightet' [d,e,a,b], where [d,e,a]
// and [e,d,b] are two hull faces. Normally, a 3-to-2 flip
// (including a 2-to-2 flip on hull subfaces) can remove
// this hull sliver.
// A special case is the existence of a hull tet [b,a,d,-1]
// or [a,b,e,-1]. It was creared by a previous hull tet
// removal. Moreover, 'd' or 'e' might be Steiner points
// on segments [a,b]. In this case, eithe [a,d],[b,d] or
// [a,e],[b,e] are subsegments. If so, a 4-to-1 flip
// (including a 3-to-1, and maybe a 2-to-1 flip) should be
// applied to remove an exterior vertex.
// See figures (2011-11-13 and 15) for illustraions.
// First check if the face [b,a,d] is a hull face.
eprev(neightet, fliptets[0]);
esymself(fliptets[0]); // [d,a,b,e]
enextself(fliptets[0]); // [a,b,d,e]
fsymself(fliptets[0]); // [b,a,d,#]
if (oppo(fliptets[0]) != dummypoint) {
// Second check if the face [a,b,e] is a hull face.
enext(neightet, fliptets[0]);
esymself(fliptets[0]); // [a,e,b,d]
eprevself(fliptets[0]); // [b,a,e,d]
fsymself(fliptets[0]); // [b,a,e,#]
}
if (oppo(fliptets[0]) != dummypoint) {
// Make sure we do not create an "inverted triangle" in the
// boundary, i.e., in exactly planar case, d and e must
// lie in the different sides of the edge [a,b].
// If the dihedral angle formed by [a,b,e] and [a,b,d] is
// larger than 90 degree, we can remove [a,b,e,d].
fliptets[0] = neightet; // [e,d,a,b]
eprevself(fliptets[0]);
esymself(fliptets[0]);
enextself(fliptets[0]); // [a,b,e,d].
pa = org(fliptets[0]);
pb = dest(fliptets[0]);
p1 = apex(fliptets[0]); // pe
p2 = oppo(fliptets[0]); // pd
ang = facedihedral(pa, pb, p1, p2);
ang *= 2.0;
if (ang > PI) {
if (b->verbose > 2) {
printf(" Remove a hull sliver (%d, %d, %d, %d).\n",
pointmark(org(fliptet)), pointmark(dest(fliptet)),
pointmark(apex(fliptet)), pointmark(oppo(fliptet)));
}
// Remove the ill tet from bounday.
fliptets[0] = neightet; // [e,d,a,b]
fnext(fliptets[0], fliptets[1]); // [e,d,b,c]
fnext(fliptets[1], fliptets[2]); // [e,d,c,a]
// FOR DEBUG
fnext(fliptets[2], fliptets[3]);
assert(fliptets[3].tet == neightet.tet);
assert(oppo(fliptets[1]) == dummypoint);
// Do a 3-to-2 flip to remove the ill tet. Two hull tets
// are removed toether. Two hull subfaces are flipped.
flip32(fliptets, 1, flipflag, 0);
// Update counters.
flip32count--;
flip22count--;
opt_sliver_peels++;
}
} else {
// There exists a thrid hull tet at vertex.
rempt = apex(fliptets[0]);
if (pointmark(rempt) >
(in->numberofpoints - (in->firstnumber ? 0 : 1))) {
if (pointtype(rempt) == FREESEGVERTEX) {
st_segref_count--;
} else if (pointtype(rempt) == FREEFACETVERTEX) {
st_facref_count--;
} else {
assert(0); // Impossible.
}
if (b->verbose > 2) {
printf(" Remove an exterior Steiner vertex %d.\n",
pointmark(rempt));
}
if (removevertexbyflips(rempt)) {
// exsteinercount++;
} else {
assert(0); // Not possible.
}
} else {
//if (b->verbose > 2) {
// printf(" Remove an exterior input vertex %d.\n",
// pointmark(rempt));
//}
// Comment: We do not remove an input point.
}
}
break;
}
} // if (checkseg.sh == NULL)
senextself(checksh);
} // i
} else {
// It's a hull edge.
assert(apex(fliptet) == dummypoint);
if (!peelsliverflag) {
// The hull edge may be not locally Delaunay. Put interior
// faces at this edge into 'flipstack' for flipping.
neightet = fliptet; // [a,b,c,d] ('c' is dummypoint).
fnextself(neightet); // [a,b,d,#1] ([a,b,d] is a hull face).
while (1) {
fnextself(neightet); // [a,b,#1,#2]
if (oppo(neightet) != dummypoint) {
// It is an interior face.
flippush(flipstack, &neightet);
} else {
// We assume the interior of the domain is connected.
// Hence we can hit hull faces only twice.
break;
}
} // while (1)
} // if (!peelsliverflag)
}
} // if ((flipflag == 4) || peelsliverflag)
// Do not flip a hull face/edge UNLESS it is in the process of
// incrementally creating a DT in which the convex hull may be
// enlarged by the flips (when p lies outside of it).
if (flipflag != 1) {
continue;
}
} // if (ishulltet(fliptet))
if (peelsliverflag) {
continue; // Only check hull tets.
}
// Let 'fliptet' be [a,b,c,d], the face [a,b,c] is the flip face.
// Get its opposite tet [b,a,c,e].
fsym(fliptet, neightet);
if (ishulltet(neightet)) {
// It is a hull tet.
if (flipflag == 1) {
// Check if the new point is visible by the hull face.
ppt = (point *) neightet.tet;
ori = orient3d(ppt[4], ppt[5], ppt[6], newpt); orient3dcount++;
if (ori < 0) {
// Visible. Perform a 2-to-3 flip on the flip face.
fliptets[0] = fliptet; // [a,b,c,d], d = newpt.
fliptets[1] = neightet; // [b,a,c,e], c = dummypoint.
flip23(fliptets, 1, flipflag, chkencflag); // flip a hull tet.
//recenttet = fliptets[0];
} else if (ori == 0) {
// Handle degenerate case ori == 0.
if (oppo(neightet) == newpt) {
// Two hull tets have the same base face.
if (b->verbose > 2) {
printf(" Close an open face (%d, %d, %d)\n",
pointmark(org(fliptet)), pointmark(dest(fliptet)),
pointmark(apex(fliptet)));
}
// The following code connect adjacent tets at corresponding
// sides of the two hull tets. It is hard to understand.
// See an example in 2011-11-11.
// First infect the two hull tets (they will be deleted).
infect(fliptet);
infect(neightet);
// Connect the actual adjacent tets.
for (i = 0; i < 3; i++) {
fnext(fliptet, fliptets[0]);
fnext(neightet, fliptets[1]);
if (!infected(fliptets[0])) {
assert(!infected(fliptets[1]));
bond(fliptets[0], fliptets[1]);
// Update the point-to-tet map.
pa = org(fliptet);
pb = dest(fliptet);
setpoint2tet(pa, encode(fliptets[0]));
setpoint2tet(pb, encode(fliptets[0]));
// Remeber a recent tet for point location.
recenttet = fliptets[0];
// apex(fliptets[0]) is the new point. The opposite face may
// be not locally Delaunay. Put it in flip stack.
assert(apex(fliptets[0]) == newpt); // SELF_CHECK
esymself(fliptets[0]);
flippush(flipstack, &(fliptets[0]));
assert(apex(fliptets[1]) == newpt); // SELF_CHECK
esymself(fliptets[1]);
flippush(flipstack, &(fliptets[1]));
}
enextself(fliptet);
eprevself(neightet);
}
// Delete the two tets.
tetrahedrondealloc(fliptet.tet);
tetrahedrondealloc(neightet.tet);
// Update the hull size.
hullsize -= 2;
}
}
} // if (flipflag == 1)
continue; // Do not flip a hull face.
} // if (ishulltet(neightet))
if (ishulltet(fliptet)) {
continue; // Do not flip a hull tet.
}
if ((flipflag == 3) || (flipflag == 4)) {
if (checksubfaceflag) {
// Do not flip a subface.
tspivot(fliptet, checksh);
if (checksh.sh != NULL) {
if (chkencflag & 2) {
// Mesh refinement.
// Put this subface into list.
if (!smarktest2ed(checksh)) {
bface = (badface *) badsubfacs->alloc();
bface->ss = checksh;
smarktest2(checksh); // Only queue it once.
bface->forg = sorg(checksh); // An alive badface.
}
}
continue;
}
}
} // if ((flipflag == 3) || (flipflag == 4))
ppt = (point *) fliptet.tet;
pe = oppo(neightet);
sign = insphere_s(ppt[4], ppt[5], ppt[6], ppt[7], pe);
if (sign < 0) {
if (b->verbose > 3) {
printf(" A non-Delaunay face (%d, %d, %d) - %d, %d\n",
pointmark(org(fliptet)), pointmark(dest(fliptet)),
pointmark(apex(fliptet)), pointmark(oppo(fliptet)),
pointmark(pe));
}
// Try to flip this face.
pd = oppo(fliptet);
// Check the convexity of its three edges.
convflag = 1;
for (i = 0; i < 3; i++) {
p1 = org(fliptet);
p2 = dest(fliptet);
ori = orient3d(p1, p2, pd, pe); orient3dcount++;
if (ori < 0) {
// A locally non-convex edge.
convflag = -1;
break;
} else if (ori == 0) {
// A locally flat edge.
convflag = 0;
break;
}
enextself(fliptet);
}
if (convflag > 0) {
// A 2-to-3 flip is found.
fliptets[0] = fliptet; // abcd, d may be the new vertex.
fliptets[1] = neightet; // bace.
if ((flipflag == 1) || (flipflag == 2)) { // CDT boundary recovery.
if (checksubfaceflag) {
// Check if a subface will be flipped.
tspivot(fliptets[0], checksh);
if (checksh.sh != NULL) {
assert(flipflag < 3); // 1 or 2.
// It is updateing a conforming DT or a CDT.
if (b->verbose > 3) {
printf(" Queue a flipped subface (%d, %d, %d).\n",
pointmark(sorg(checksh)), pointmark(sdest(checksh)),
pointmark(sapex(checksh)));
}
for (i = 0; i < 2; i++) {
tsdissolve(fliptets[i]); // Disconnect the tet->sub bond.
}
stdissolve(checksh); // Disconnect the sub->tet bond.
// Add the missing subface into list.
subfacstack->newindex((void **) &parysh);
*parysh = checksh;
} // if (checksh.sh != NULL)
}
} // if ((flipflag == 1) || (flipflag == 2))
flip23(fliptets, 0, flipflag, chkencflag);
//recenttet = fliptets[0]; // for point location.
} else {
// The edge ('fliptet') is non-convex or flat.
if ((flipflag == 3) || (flipflag == 4)) {
// Do not flip a subsegment.
tsspivot1(fliptet, checkseg);
if (checkseg.sh != NULL) {
if (b->verbose > 3) {
printf(" Found a non-Delaunay segment (%d, %d).\n",
pointmark(sorg(checkseg)), pointmark(sdest(checkseg)));
}
// Comment: this should be only possible when a new Steiner
// point is inserted on a segment nearby.
if (chkencflag & 1) {
// Put this segment into list.
if (!smarktest2ed(checkseg)) {
bface = (badface *) badsubsegs->alloc();
bface->ss = checkseg;
smarktest2(checkseg); // Only queue it once.
bface->forg = sorg(checkseg); // An alive badface.
}
}
continue;
}
}
// A 3-to-2 or 4-to-4 may be possible.
esym(fliptet, fliptets[0]); // [b,a,d,c]
// assert(apex(fliptets[0]) == pd);
n = 0;
do {
fnext(fliptets[n], fliptets[n + 1]);
n++;
} while ((fliptets[n].tet != fliptet.tet) && (n < 5));
if (n == 3) {
// Found a 3-to-2 flip.
if ((flipflag == 1) || (flipflag == 2)) { // CDT boundary recovery.
if (checksubsegflag) {
// Check if the flip edge is subsegment.
tsspivot1(fliptets[0], checkseg);
if (checkseg.sh != NULL) {
if (!sinfected(checkseg)) {
// This subsegment will be flipped. Queue it.
if (b->verbose > 3) {
printf(" Queue a flipped segment (%d, %d).\n",
pointmark(sorg(checkseg)), pointmark(sdest(checkseg)));
}
sinfect(checkseg); // Only save it once.
subsegstack->newindex((void **) &paryseg);
*paryseg = checkseg;
}
// Clean tet-to-seg pointers.
for (i = 0; i < 3; i++) {
tssdissolve1(fliptets[i]);
}
// Clean the seg-to-tet pointer.
sstdissolve1(checkseg);
}
}
if (checksubfaceflag) {
// Check if there are subfaces to be flipped.
for (i = 0; i < 3; i++) {
tspivot(fliptets[i], checksh);
if (checksh.sh != NULL) {//if (flipshs[i].sh != NULL) {
if (b->verbose > 2) {
printf(" Queue a flipped subface (%d, %d, %d).\n",
pointmark(sorg(checksh)), pointmark(sdest(checksh)),
pointmark(sapex(checksh)));
}
tsdissolve(fliptets[i]); // Disconnect the tet->sub bond.
stdissolve(checksh); // Disconnect the sub->tet bond.
// Add the missing subface into list.
subfacstack->newindex((void **) &parysh);
*parysh = checksh;
}
}
}
} // if ((flipflag == 1) || (flipflag == 2))
// Now flip the edge.
flip32(fliptets, 0, flipflag, chkencflag);
//recenttet = fliptets[0]; // for point location.
} else {
// There are more than 3 tets shared at this edge.
if ((n == 4) && (convflag < 0)) {
// Check if a 4-to-4 flip is possible.
pf = apex(fliptets[3]);
if (pf == dummypoint) {
// It is a non-convex hull edge shared by four tets (two hull
// tets and two interior tets).
// Let the two interior tets be [a,b,c,d] and [b,a,c,e] where
// [a,b] be the hull edge, [a,b,c] be the interior face.
// [a,b,d] and [a,b,e] are two hull faces.
// A 4-to-4 flip is possible if the two new tets [e,d,b,c]
// and [e,d,c,a] are valid tets.
// Current status:
// 'fliptets[0]' is [a,b,e,c]
// 'fliptets[1]' is [a,b,c,d]
// 'fliptets[2]' is [a,b,d,f] (hull tet)
// 'fliptets[3]' is [a,b,f,e] (hull tet)
pa = org(fliptets[1]);
pb = dest(fliptets[1]);
pc = apex(fliptets[1]);
p1 = oppo(fliptets[1]); // pd
p2 = apex(fliptets[0]); // pe
ori = orient3d(p2, p1, pb, pc);
if (ori < 0) {
ori = orient3d(p2, p1, pc, pa);
if (ori < 0) {
convflag = -2; // A 4-to-4 flip is possible.
}
}
}
} // if ((n == 4) && (convflag < 0))
if ((n == 4) && ((convflag == 0) || (convflag == -2))) {
// Found a 4-to-4 flip.
if (b->verbose > 3) {
printf(" A 4-to-4 flip (%d, %d) - (%d, %d).\n",
pointmark(org(fliptet)), pointmark(dest(fliptet)),
pointmark(pd), pointmark(pe));
}
if ((flipflag == 1) || (flipflag == 2)) { // CDT boundary recovery
if (checksubsegflag) {
// Check if the flip edge is subsegment.
tsspivot1(fliptets[0], checkseg);
if (checkseg.sh != NULL) {
if (!sinfected(checkseg)) {
// This subsegment will be flipped. Queue it.
if (b->verbose > 3) {
printf(" Queue a flipped segment (%d, %d).\n",
pointmark(sorg(checkseg)), pointmark(sdest(checkseg)));
}
sinfect(checkseg); // Only save it once.
subsegstack->newindex((void **) &paryseg);
*paryseg = checkseg;
}
// Clean the tet-to-seg pointers.
for (i = 0; i < 4; i++) {
tssdissolve1(fliptets[i]);
}
// Clean the seg-to-tet pointer.
sstdissolve1(checkseg);
}
}
if (checksubfaceflag) {
// Check if there are subfaces to be flipped.
for (i = 0; i < 4; i++) {
tspivot(fliptets[i], checksh);
if (checksh.sh != NULL) {
if (b->verbose > 3) {
printf(" Queue a flipped subface (%d,%d,%d).\n",
pointmark(sorg(checksh)), pointmark(sdest(checksh)),
pointmark(sapex(checksh)));
}
tsdissolve(fliptets[i]); // Disconnect the tet->sub bond.
stdissolve(checksh); // Disconnect the sub->tet bond.
// Add the missing subface into list.
subfacstack->newindex((void **) &parysh);
*parysh = checksh;
}
}
}
} // if ((flipflag == 1) || (flipflag == 2))
// First do a 2-to-3 flip.
// Comment: This flip temporarily creates either a degenerated
// tet (convflag == 0) or an inverted tet (convflag < 0).
// It is removed by the followed 3-to-2 flip.
fliptets[0] = fliptet; // tet abcd, d is the new vertex.
baktets[0] = fliptets[2];
baktets[1] = fliptets[3];
// The flip may involve hull tets.
flip23(fliptets, 1, flipflag, chkencflag);
// Then do a 3-to-2 flip.
enextesymself(fliptets[0]); // fliptets[0] is edab.
eprevself(fliptets[0]); // tet badc, d is the new vertex.
fliptets[1] = baktets[0];
fliptets[2] = baktets[1];
flip32(fliptets, 1, flipflag, chkencflag);
flip23count--;
flip32count--;
flip44count++;
//recenttet = fliptets[0]; // for point location.
} else {
// This edge is shared by more than 4 tets.
if (b->verbose > 2) {
printf(" An unflippable non-Delaunay edge (%d,%d).\n",
pointmark(org(fliptet)), pointmark(dest(fliptet)));
}
remflag = 0;
if (flipedgeflag == 2) {
// Try to flip this edge by my edge flip algorithm.
// Remember the the objective value (volume of all tetprisms).
fc.bak_tetprism_vol = tetprism_vol_sum;
if (removeedgebyflips(&fliptet, &fc) == 2) {
if (b->verbose > 2) {
printf(" Decreased quantity: %.17g.\n",
fc.bak_tetprism_vol - tetprism_vol_sum);
}
// Queue new faces in flipstack.
for (i = 0; i < cavetetlist->objects; i++) {
parytet = (triface *) fastlookup(cavetetlist, i);
if (!isdeadtet(*parytet)) { // Skip a dead tet.
for (parytet->ver = 0; parytet->ver < 4; parytet->ver++) {
// Avoid queue a face twice.
fsym(*parytet, neightet);
if (!facemarked(neightet)) {
//flippush(flipstack, parytet);
bface = (badface *) flippool->alloc();
bface->tt = *parytet;
markface(bface->tt);
bface->forg = org(bface->tt); // An alive badface.
bface->fdest = dest(bface->tt);
bface->fapex = apex(bface->tt);
// bface->foppo = oppo(bface->tt);
// Push this face into stack.
bface->nextitem = flipstack;
flipstack = bface;
}
} // parytet->ver
}
} // i
cavetetlist->restart();
remflag = 1;
}
}
if (!remflag) {
// Found an unflippable non-Delaunay edge.
if (flipedgeflag > 0) { // if (flipflag > 1) {
// Save this face (of the edge) in a second queue.
unflipqueue->newindex((void **) &bface);
bface->tt = fliptet;
bface->forg = org(fliptet);
bface->fdest = dest(fliptet);
bface->fapex = apex(fliptet); // FOR DEBUG.
}
}
}
} // if (n > 3)
} // if (convflag <= 0)
} // if (sign < 0)
} // while (flipstack != NULL)
break;
} // while (1)
if (b->verbose > 2) {
printf(" Total %ld flips", flip23count + flip32count + flip44count
- flipcount);
if ((flipflag == 4) || peelsliverflag) {
printf(", %ld sliver peels", opt_sliver_peels - tetpeelcount);
}
printf("\n");
}
return flip23count + flip32count + flip44count - flipcount;
}
///////////////////////////////////////////////////////////////////////////////
// //
// insertvertex() Insert a point into current tetrahedralization. //
// //
// This routine implements the famous Bowyer-Watson (B-W) algorithm to add a //
// new point p into current tetrahedralization, denoted as T. The baisc idea //
// of B-W algorithm is: first finds a "cavity", denoted as C inside T, where //
// C is a simplicial polyhedron formed by a union of tetrahedra in T. If all //
// boundary faces (triangles) of C are visible by p, i.e., C is star-shaped, //
// then T can be re-tetrahedralized by first deleting all old tetrahedra in //
// C, then replacing new tetrahedra formed by boundary faces of C and p. The //
// result is that p becomesis a vertex of T. //
// //
// //
///////////////////////////////////////////////////////////////////////////////
int tetgenmesh::insertvertex(point insertpt, triface *searchtet, face *splitsh,
face *splitseg, insertvertexflags *ivf)
{
arraypool *swaplist; // for updating cavity.
triface *cavetet, spintet, neightet, neineitet, *parytet;
triface oldtet, newtet, newneitet;
face checksh, *parysh, neighsh, spinsh;
face checkseg, *paryseg;
point *pts, pa, pb, pc, *parypt;
badface *bface;
enum locateresult loc;
REAL sign, ori;
REAL rd, cent[3];
REAL attrib, volume;
long cutcount, cutshcount, tetcount = 0;
long bakhullsize;
bool enqflag;
int i, j, k, s;
int rejptflag, encptflag; // for protecting balls.
int bgmloc;
#ifdef WITH_RUNTIME_COUNTERS
clock_t tstart, tend;
#endif
if (b->verbose > 2) {
printf(" Insert point %d\n", pointmark(insertpt));
}
// Locate the point.
loc = OUTSIDE; // Set a default value.
if (searchtet->tet != NULL) {
loc = (enum locateresult) ivf->iloc;
}
if (loc == OUTSIDE) {
#ifdef WITH_RUNTIME_COUNTERS
tstart = clock();
#endif
tetcount = ptloc_count; // Count the number of walked tets.
if (searchtet->tet == NULL) {
if (!b->weighted) {
if (b->btree) {
btree_search(insertpt, searchtet);
} else if (b->hilbertcurve) { // -U
*searchtet = recenttet;
} else { // -u0
randomsample(insertpt, searchtet);
}
} else {
// There may exist dangling vertex.
*searchtet = recenttet;
}
}
// Locate the point. Use 'randflag' if the mesh is non-convex.
loc = locate(insertpt, searchtet, ivf->chkencflag, checksubfaceflag);
if (b->verbose > 3) {
printf(" Walk distance (# tets): %ld\n", ptloc_count-tetcount);
}
if (ptloc_max_count < (ptloc_count - tetcount)) {
ptloc_max_count = (ptloc_count - tetcount);
}
#ifdef WITH_RUNTIME_COUNTERS
tend = clock();
t_ptloc += (tend - tstart);
#endif
}
if (b->verbose > 3) {
printf(" Located tet (%d, %d, %d, %d).\n",
pointmark(org(*searchtet)), pointmark(dest(*searchtet)),
pointmark(apex(*searchtet)), pointmark(oppo(*searchtet)));
}
#ifdef WITH_RUNTIME_COUNTERS
tstart = clock();
#endif
if (b->weighted) {
if (loc != OUTSIDE) {
// Check if this vertex is regular.
pts = (point *) searchtet->tet;
assert(pts[7] != dummypoint);
sign = orient4d_s(pts[4], pts[5], pts[6], pts[7], insertpt,
pts[4][3], pts[5][3], pts[6][3], pts[7][3],
insertpt[3]);
if (sign > 0) {
// This new vertex does not lie below the lower hull. Skip it.
if (b->verbose > 2) {
printf(" The point is above the lower hull, skipped.\n");
}
return OUTSIDE;
}
}
}
// Create the initial cavity C(p) which contains all tetrahedra directly
// intersect with p.
// If 'bowywat > 2' and p lies on a segment or subface, also create the
// initial sub-cavity sC(p) which contains all subfaces (and segment)
// which directly intersect with p.
// If 'bowywat > 2', the initial C(p) is validated, i.e., all boundary
// faces of C(p) should be visible by p.
// Remember the current hullsize. It is used to restore the hullsize
// if the new point is rejected for insertion.
bakhullsize = hullsize;
if (loc == OUTSIDE) {
if (b->verbose > 3) {
printf(" Outside hull.\n");
}
// The current hull will be enlarged.
// Add four adjacent boundary tets into list.
for (i = 0; i < 4; i++) {
decode(searchtet->tet[i], neightet);
neightet.ver = epivot[neightet.ver & 3];
cavetetlist->newindex((void **) &parytet);
*parytet = neightet;
}
if ((point) searchtet->tet[7] == dummypoint) hullsize--;
// tetrahedrondealloc(searchtet->tet);
infect(*searchtet);
caveoldtetlist->newindex((void **) &parytet);
*parytet = *searchtet;
flip14count++;
} else if (loc == INTETRAHEDRON) {
if (b->verbose > 3) {
printf(" Inside tet.\n");
}
// Add four adjacent boundary tets into list.
for (i = 0; i < 4; i++) {
decode(searchtet->tet[i], neightet);
neightet.ver = epivot[neightet.ver & 3];
cavetetlist->newindex((void **) &parytet);
*parytet = neightet;
}
// tetrahedrondealloc(searchtet->tet);
infect(*searchtet);
caveoldtetlist->newindex((void **) &parytet);
*parytet = *searchtet;
flip14count++;
} else if (loc == ONFACE) {
if (b->verbose > 3) {
printf(" On face.\n");
}
// Add six adjacent boundary tets into list.
j = (searchtet->ver & 3); // The current face number.
for (i = 1; i < 4; i++) {
decode(searchtet->tet[(j + i) % 4], neightet);
neightet.ver = epivot[neightet.ver & 3];
cavetetlist->newindex((void **) &parytet);
*parytet = neightet;
}
decode(searchtet->tet[j], spintet);
j = (spintet.ver & 3); // The current face number.
for (i = 1; i < 4; i++) {
decode(spintet.tet[(j + i) % 4], neightet);
neightet.ver = epivot[neightet.ver & 3];
cavetetlist->newindex((void **) &parytet);
*parytet = neightet;
}
if ((point) spintet.tet[7] == dummypoint) hullsize--;
if ((point) searchtet->tet[7] == dummypoint) hullsize--;
// tetrahedrondealloc(spintet.tet);
infect(spintet);
caveoldtetlist->newindex((void **) &parytet);
*parytet = spintet;
// tetrahedrondealloc(searchtet->tet);
infect(*searchtet);
caveoldtetlist->newindex((void **) &parytet);
*parytet = *searchtet;
flip26count++;
if (ivf->splitbdflag) { //if (bowywat > 2) {
if (splitsh != NULL) {
// Create the initial sub-cavity sC(p).
smarktest(*splitsh);
caveshlist->newindex((void **) &parysh);
*parysh = *splitsh;
}
} // if (splitbdflag)
} else if (loc == ONEDGE) {
if (b->verbose > 3) {
printf(" On edge.\n");
}
// Add all adjacent boundary tets into list.
spintet = *searchtet;
while (1) {
enextesym(spintet, neightet);
fsymself(neightet);
neightet.ver = epivot[neightet.ver & 3];
cavetetlist->newindex((void **) &parytet);
*parytet = neightet;
eprevesym(spintet, neightet);
fsymself(neightet);
neightet.ver = epivot[neightet.ver & 3];
cavetetlist->newindex((void **) &parytet);
*parytet = neightet;
if ((point) spintet.tet[7] == dummypoint) hullsize--;
// tetrahedrondealloc(spintet.tet);
infect(spintet);
caveoldtetlist->newindex((void **) &parytet);
*parytet = spintet;
fnextself(spintet);
if (spintet.tet == searchtet->tet) break;
} // while (1)
flipn2ncount++;
if (ivf->splitbdflag) { //if (bowywat > 2) {
// Create the initial sub-cavity sC(p).
if (splitseg != NULL) {
smarktest(*splitseg);
splitseg->shver = 0;
spivot(*splitseg, *splitsh);
}
if (splitsh != NULL) {
if (splitsh->sh != NULL) {
// Collect all subfaces share at this edge.
pa = sorg(*splitsh);
neighsh = *splitsh;
while (1) {
// Adjust the origin of its edge to be 'pa'.
if (sorg(neighsh) != pa) {
sesymself(neighsh);
}
// Add this face into list (in B-W cavity).
smarktest(neighsh);
caveshlist->newindex((void **) &parysh);
*parysh = neighsh;
// Add this face into face-at-splitedge list.
cavesegshlist->newindex((void **) &parysh);
*parysh = neighsh;
// Go to the next face at the edge.
spivotself(neighsh);
// Stop if all faces at the edge have been visited.
if (neighsh.sh == splitsh->sh) break;
if (neighsh.sh == NULL) break;
} // while (1)
} // if (not a dangling segment)
}
} // if (splitbdflag)
} else if (loc == INSTAR) {
if (b->verbose > 3) {
printf(" Inside star.\n");
}
// We assume that all tets in the star are given in 'caveoldtetlist',
// and they are all infected.
assert(caveoldtetlist->objects > 0);
// 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 & 3];
cavetetlist->newindex((void **) &parytet);
*parytet = neightet;
}
}
}
} else if (loc == ONVERTEX) {
pa = org(*searchtet);
if (b->verbose > 3) {
printf(" On vertex %d.\n", pointmark(pa));
}
if (insertpt != pa) {
// Remember it is a duplicated point.
setpointtype(insertpt, DUPLICATEDVERTEX);
// Set a pointer to the point it duplicates.
setpoint2ppt(insertpt, pa);
}
// The point already exist. Do nothing and return.
return (int) loc;
} else if (loc == ENCSUBFACE) {
if (b->verbose > 3) {
printf(" Encroached.\n");
}
// The vertex lies outside of the region boundary.
// Treated it as outside
loc = OUTSIDE;
return (int) loc;
} else {
assert(0); // Unknown type.
}
if (ivf->assignmeshsize) {
// Assign mesh size for the new point.
if (bgm != NULL) {
// Interpolate the mesh size from the background mesh.
pa = org(*searchtet);
bgm->decode(point2bgmtet(pa), neightet); // neightet is in 'bgm'!
bgmloc = bgm->scoutpoint(insertpt, &neightet, 0); // randflag = 0
if (bgmloc != (int) OUTSIDE) {
insertpt[pointmtrindex] = // posflag = 1
bgm->getpointmeshsize(insertpt, &neightet, bgmloc, 1);
setpoint2bgmtet(insertpt, bgm->encode(neightet));
}
} else {
insertpt[pointmtrindex] = // posflag = 1
getpointmeshsize(insertpt, searchtet, (int) loc, 1);
}
} // if (assignmeshsize)
if (ivf->validflag) { //if (bowywat > 2) {
// Validate the initial C(p). Enlarge it at a face which is not visible
// by p. This removes (interior) slivers. Re-use 'cavebdrylist'.
tetcount = 0l;
for (i = 0; i < cavetetlist->objects; i++) {
cavetet = (triface *) fastlookup(cavetetlist, i);
// Other expansions may make this face inside C(p).
if (!infected(*cavetet)) {
pc = apex(*cavetet);
// Do valid if it is a face (not a hull edge).
if (pc != dummypoint) {
pa = org(*cavetet);
pb = dest(*cavetet);
ori = orient3d(pa, pb, pc, insertpt);
if (ori <= 0) {
// An invalid face. Enlarge the cavity.
//if (oppo(*cavetet) != dummypoint) {
if (b->verbose > 3) {
printf(" Enlarge cavity at (%d, %d, %d)\n",
pointmark(pa), pointmark(pb), pointmark(pc));
}
// Add the other three faces into list.
j = (cavetet->ver & 3); // The current face number.
for (k = 1; k < 4; k++) {
decode(cavetet->tet[(j + k) % 4], neightet);
neightet.ver = epivot[neightet.ver & 3];
cavetetlist->newindex((void **) &parytet);
*parytet = neightet;
}
if ((point) cavetet->tet[7] == dummypoint) hullsize--;
infect(*cavetet);
caveoldtetlist->newindex((void **) &parytet);
*parytet = *cavetet;
tetcount++;
//} else {
// printf("Error in insertvertex %d: ", pointmark(insertpt));
// printf("Invalid initial cavity at face %d.\n", i + 1);
// assert(0);
//}
} else {
// A valid face.
cavebdrylist->newindex((void **) &parytet);
*parytet = *cavetet;
}
} else {
// A hull edge is valid.
cavebdrylist->newindex((void **) &parytet);
*parytet = *cavetet;
}
} // if (!infected(*cavetet))
} // i
if (tetcount > 0l) {
// The cavity has been enlarged. Update it.
cavetetlist->restart();
for (i = 0; i < cavebdrylist->objects; i++) {
cavetet = (triface *) fastlookup(cavebdrylist, i);
if (!infected(*cavetet)) {
cavetetlist->newindex((void **) &parytet);
*parytet = *cavetet;
}
} // i
} // if (tetcount)
cavebdrylist->restart();
tetcount = 0l;
} // if (bowywat > 2)
// Update the cavity C(p) using the Bowyer-Watson approach (bowywat > 0).
for (i = 0; i < cavetetlist->objects; i++) {
// 'cavetet' is an adjacent tet at outside of the cavity.
cavetet = (triface *) fastlookup(cavetetlist, i);
// The tet may be tested and included in the (enlarged) cavity.
if (!infected(*cavetet)) {
// Check for two possible cases for this tet:
// (1) It is a cavity tet, or
// (2) it is a cavity boundary face.
// In case (1), this tet is grabbed in the cavity and three adjacent
// tets on other faces of this tet are added into 'cavetetlist'.
enqflag = false;
if (!marktested(*cavetet)) {
if (ivf->bowywat) {
// Do Delaunay (in-sphere) test.
pts = (point *) cavetet->tet;
if (pts[7] != dummypoint) {
// 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);
orient3dcount++;
if (ori < 0) {
// A visible hull face.
//if (!nonconvex) {
// Include it in the cavity. The convex hull will be enlarged.
enqflag = true; // (ori < 0.0);
//}
} else if (ori == 0.0) {
// A coplanar hull face. We need to test if this hull face is
// Delaunay or not. We test if the adjacent tet (not faked)
// of this hull face is Delaunay or not.
neightet = *cavetet;
neightet.ver = 3; // The face opposite to dummypoint.
fsym(neightet, neineitet);
if (!infected(neineitet)) {
if (!marktested(neineitet)) {
// Do Delaunay test on this tet.
pts = (point *) neineitet.tet;
assert(pts[7] != dummypoint);
if (b->weighted) {
sign = orient4d_s(pts[4],pts[5],pts[6],pts[7], insertpt,
pts[4][3], pts[5][3], pts[6][3],
pts[7][3], insertpt[3]);
} else {
sign = insphere_s(pts[4],pts[5],pts[6],pts[7], insertpt);
}
enqflag = (sign < 0.0);
} else {
// The adjacent tet has been tested (marktested), and it
// is Delaunay (not get infected). Hence the the hull
// face is Delaunay as well.
// enqflag = false;
}
} else {
// The adjacent tet is non-Delaunay. The hull face is non-
// Delaunay as well. Include it in the cavity.
enqflag = true;
} // if (!infected(neineitet))
} // if (ori == 0.0)
} else {
// A hull face (must be a subface).
assert(checksubfaceflag);
assert(ivf->validflag);
// We FIRST include it in the initial cavity if the adjacent tet
// (not faked) of this hull face is not Delaunay wrt p.
// Whether it belongs to the final cavity will be determined
// during the validation process. 'validflag'.
neightet = *cavetet;
neightet.ver = 3; // The face opposite to dummypoint.
fsym(neightet, neineitet);
if (!infected(neineitet)) {
if (!marktested(neineitet)) {
// Do Delaunay test on this tet.
pts = (point *) neineitet.tet;
assert(pts[7] != dummypoint);
if (b->weighted) {
sign = orient4d_s(pts[4],pts[5],pts[6],pts[7], insertpt,
pts[4][3], pts[5][3], pts[6][3],
pts[7][3], insertpt[3]);
} else {
sign = insphere_s(pts[4],pts[5],pts[6],pts[7], insertpt);
}
enqflag = (sign < 0.0);
} else {
// The adjacent tet has been tested (marktested), and it
// is Delaunay (not get infected). Hence the the hull
// face is Delaunay as well.
// enqflag = false;
} // if (marktested(neineitet))
} else {
// The adjacent tet is non-Delaunay. The hull face is non-
// Delaunay as well. Include it in the cavity.
enqflag = true;
} // if (infected(neineitet))
} // if (nonconvex)
} // if (pts[7] != dummypoint)
} // if (bowywat)
marktest(*cavetet); // Only test it once.
} // if (!marktested(*cavetet))
if (enqflag) {
// Found a tet in the cavity. Put other three faces in check list.
k = (cavetet->ver & 3); // The current face number
for (j = 1; j < 4; j++) {
decode(cavetet->tet[(j + k) % 4], neightet);
neightet.ver = epivot[neightet.ver & 3];
cavetetlist->newindex((void **) &parytet);
*parytet = neightet;
}
if ((point) cavetet->tet[7] == dummypoint) hullsize--;
// tetrahedrondealloc(cavetet->tet);
infect(*cavetet);
caveoldtetlist->newindex((void **) &parytet);
*parytet = *cavetet;
} else {
// Found a boundary face of the cavity. It may be a face of a hull
// tet which contains 'dummypoint'. Choose the edge in the face
// such that its endpoints are not 'dummypoint', while its apex
// may be 'dummypoint'.
//j = (cavetet->ver & 3); // j is the face number.
//cavetet->ver = epivot[j]; // [4,5,2,11]
cavebdrylist->newindex((void **) &parytet);
*parytet = *cavetet;
}
} // if (!infected(*cavetet))
} // i
if (b->verbose > 3) {
printf(" Initial cavity size: %ld tets, %ld faces.\n",
caveoldtetlist->objects, cavebdrylist->objects);
}
if (checksubsegflag) {
// Collect all segments of C(p).
for (i = 0; i < caveoldtetlist->objects; i++) {
cavetet = (triface *) fastlookup(caveoldtetlist, i);
for (j = 0; j < 6; j++) {
cavetet->ver = edge2ver[j];
tsspivot1(*cavetet, checkseg);
if (checkseg.sh != NULL) {
if (!sinfected(checkseg)) {
sinfect(checkseg);
cavetetseglist->newindex((void **) &paryseg);
*paryseg = checkseg;
}
}
}
}
// Uninfect collected segments.
for (i = 0; i < cavetetseglist->objects; i++) {
checkseg = * (face *) fastlookup(cavetetseglist, i);
suninfect(checkseg);
}
} // if (checksubsegflag)
if (checksubfaceflag) {
// Collect all subfaces of C(p).
for (i = 0; i < caveoldtetlist->objects; i++) {
cavetet = (triface *) fastlookup(caveoldtetlist, i);
oldtet = *cavetet;
for (oldtet.ver = 0; oldtet.ver < 4; oldtet.ver++) {
tspivot(oldtet, checksh);
if (checksh.sh != NULL) {
if (!sinfected(checksh)) {
sinfect(checksh);
cavetetshlist->newindex((void **) &parysh);
*parysh = checksh;
}
}
}
}
// Uninfect collected subfaces.
for (i = 0; i < cavetetshlist->objects; i++) {
checksh = * (face *) fastlookup(cavetetshlist, i);
suninfect(checksh);
}
} // if (checksubfaceflag)
if (ivf->rejflag & 1) {
// Reject insertion of this point if it encroaches upon any segment.
for (i = 0; i < cavetetseglist->objects; i++) {
checkseg = * (face *) fastlookup(cavetetseglist, i);
pa = sorg(checkseg);
pb = sdest(checkseg);
if (checkseg4encroach(pa, pb, insertpt)) {
if (b->verbose > 3) {
printf(" Found an encroached seg (%d, %d).\n",
pointmark(pa), pointmark(pb));
}
encseglist->newindex((void **) &paryseg);
*paryseg = checkseg;
}
} // i
if (encseglist->objects > 0) {
if (b->verbose > 3) {
printf(" Found %ld encroached segments. Reject it.\n",
encseglist->objects);
}
for (i = 0; i < caveoldtetlist->objects; i++) {
cavetet = (triface *) fastlookup(caveoldtetlist, i);
uninfect(*cavetet);
unmarktest(*cavetet);
}
for (i = 0; i < cavebdrylist->objects; i++) {
cavetet = (triface *) fastlookup(cavebdrylist, i);
unmarktest(*cavetet); // Unmark it.
}
// Clear working lists.
cavetetlist->restart();
cavebdrylist->restart();
caveoldtetlist->restart();
cavetetseglist->restart();
cavetetshlist->restart();
if (ivf->splitbdflag) { //if (bowywat > 2) {
if (splitseg != NULL) {
sunmarktest(*splitseg);
}
for (i = 0; i < caveshlist->objects; i++) {
parysh = (face *) fastlookup(caveshlist, i);
assert(smarktested(*parysh));
sunmarktest(*parysh);
}
caveshlist->restart();
cavesegshlist->restart();
}
// Restore the hullsize.
hullsize = bakhullsize;
return (int) ENCSEGMENT;
}
} // if (reject & 1)
if (ivf->rejflag & 2) {
// Reject insertion of this point if it encroaches upon any subface.
for (i = 0; i < cavetetshlist->objects; i++) {
checksh = * (face *) fastlookup(cavetetshlist, i);
pa = sorg(checksh);
pb = sdest(checksh);
pc = sapex(checksh);
if (checkfac4encroach(pa, pb, pc, insertpt, cent, &rd)) {
if (b->verbose > 3) {
printf(" Found an encroached subface (%d, %d, %d).\n",
pointmark(pa), pointmark(pb), pointmark(pc));
}
encshlist->newindex((void **) &bface);
bface->ss = checksh;
bface->forg = pa; // Not a dad one.
for (j = 0; j < 3; j++) bface->cent[j] = cent[j];
bface->key = rd;
}
} // i
if (encshlist->objects > 0) {
if (b->verbose > 3) {
printf(" Found %ld encroached subfaces. Reject it.\n",
caveencshlist->objects);
}
for (i = 0; i < caveoldtetlist->objects; i++) {
cavetet = (triface *) fastlookup(caveoldtetlist, i);
uninfect(*cavetet);
unmarktest(*cavetet);
}
for (i = 0; i < cavebdrylist->objects; i++) {
cavetet = (triface *) fastlookup(cavebdrylist, i);
unmarktest(*cavetet); // Unmark it.
}
cavetetlist->restart();
cavebdrylist->restart();
caveoldtetlist->restart();
cavetetseglist->restart();
cavetetshlist->restart();
if (ivf->splitbdflag) { //if (bowywat > 2) {
if (splitseg != NULL) {
sunmarktest(*splitseg);
}
for (i = 0; i < caveshlist->objects; i++) {
parysh = (face *) fastlookup(caveshlist, i);
assert(smarktested(*parysh));
sunmarktest(*parysh);
}
caveshlist->restart();
cavesegshlist->restart();
}
// Restore the hullsize.
hullsize = bakhullsize;
return (int) ENCSUBFACE;
}
} // if (reject & 2)
if (ivf->splitbdflag) { //if (bowywat > 2) { // if (bowywat == 3) {
// Update the sC(p).
// We have already 'smarktested' the subfaces which directly intersect
// with p in 'caveshlist'. From them, we 'smarktest' their neighboring
// subfaces which are included in C(p). Do not across a segment.
for (i = 0; i < caveshlist->objects; i++) {
parysh = (face *) fastlookup(caveshlist, i);
assert(smarktested(*parysh));
checksh = *parysh;
for (j = 0; j < 3; j++) {
sspivot(checksh, checkseg);
if (checkseg.sh == NULL) {
spivot(checksh, neighsh);
assert(neighsh.sh != NULL);
if (!smarktested(neighsh)) {
// Add this subface if it is inside C(p).
stpivot(neighsh, neightet);
if (infected(neightet)) {
fsymself(neightet);
if (infected(neightet)) {
smarktest(neighsh);
caveshlist->newindex((void **) &parysh);
*parysh = neighsh;
}
}
}
}
senextself(checksh);
} // j
} // i
if (b->verbose > 3) {
printf(" Initial subcavity size: %ld subfacess.\n",
caveshlist->objects);
}
}
cutcount = 0l;
if (ivf->validflag) {
//if (bowywat > 1) { // if (bowywat == 2 || bowywat == 3) {
// T is a CT. Validation is needed (fig/dump-cavity-case8).
cavetetlist->restart(); // Re-use it.
//if (splitbdflag) { //if (bowywat > 2) { // if (bowywat == 3) {
if (ivf->respectbdflag) {
// The initial cavity may include subfaces which are not on the facets
// being splitting. Find them and make them as boundary of C(p).
// Comment: We have already 'smarktested' the subfaces in sC(p).
// It is needed by 'splitbdflag'.
for (i = 0; i < cavetetshlist->objects; i++) {
parysh = (face *) fastlookup(cavetetshlist, i);
stpivot(*parysh, neightet);
if (infected(neightet)) {
fsymself(neightet);
if (infected(neightet)) {
if (!smarktested(*parysh)) {
if (b->verbose > 3) {
printf(" Found a subface (%d, %d, %d) inside cavity.\n",
pointmark(sorg(*parysh)), pointmark(sdest(*parysh)),
pointmark(sapex(*parysh)));
}
// It is possible that this face is a boundary subface.
// Check if it is a hull face.
assert(apex(neightet) != dummypoint);
if (oppo(neightet) != dummypoint) {
fsymself(neightet);
}
if (oppo(neightet) != dummypoint) {
pa = org(neightet);
pb = dest(neightet);
pc = apex(neightet);
ori = orient3d(pa, pb, pc, insertpt);
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) {
if (b->verbose > 3) {
printf(" Cut tet (%d, %d, %d, %d)\n",
pointmark(org(neightet)), pointmark(dest(neightet)),
pointmark(apex(neightet)), pointmark(oppo(neightet)));
}
uninfect(neightet);
unmarktest(neightet);
cutcount++;
neightet.ver = epivot[neightet.ver & 3];
cavebdrylist->newindex((void **) &parytet);
*parytet = neightet;
// Add three new faces to find new boundaries.
for (j = 0; j < 3; j++) {
esym(neightet, neineitet);
neineitet.ver = epivot[neineitet.ver & 3];
cavebdrylist->newindex((void **) &parytet);
*parytet = neineitet;
enextself(neightet);
}
// Update hullsize.
if (oppo(neightet) == dummypoint) hullsize++;
} // 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)) {
if (b->verbose > 3) {
printf(" Found an interior segment (%d, %d).\n",
pointmark(sorg(*paryseg)), pointmark(sdest(*paryseg)));
}
// Find an adjacent tet at this segment such that both faces
// at this segment are not visible by p.
pa = org(neightet);
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;
}
}
}
} else {
}
fnextself(spintet);
if (spintet.tet == neightet.tet) break;
}
if (j == 0) {
// Not found such a face.
assert(0); // debug this case.
}
neightet = spintet;
if (b->verbose > 3) {
printf(" Cut tet (%d, %d, %d, %d)\n",
pointmark(org(neightet)), pointmark(dest(neightet)),
pointmark(apex(neightet)), pointmark(oppo(neightet)));
}
uninfect(neightet);
unmarktest(neightet);
cutcount++;
neightet.ver = epivot[neightet.ver & 3];
cavebdrylist->newindex((void **) &parytet);
*parytet = neightet;
// Add three new faces to find new boundaries.
for (j = 0; j < 3; j++) {
esym(neightet, neineitet);
neineitet.ver = epivot[neineitet.ver & 3];
cavebdrylist->newindex((void **) &parytet);
*parytet = neineitet;
enextself(neightet);
}
// Update hullsize.
//if (oppo(neightet) == dummypoint) hullsize++;
if ((point) (neightet.tet[7]) == dummypoint) hullsize++;
}
}
} // i
} // if (bowywat > 2)
// Update the cavity by removing invisible faces until it is star-shaped.
for (i = 0; i < cavebdrylist->objects; i++) {
cavetet = (triface *) fastlookup(cavebdrylist, i);
// 'cavetet' is an exterior tet adjacent to the cavity.
assert(cavetet->ver == epivot[cavetet->ver & 3]); // SELF_CHECK
// It must be not inside the cavity (since we only cut tets).
assert(!infected(*cavetet));
// Check if its neighbor is inside C(p).
fsym(*cavetet, neightet);
if (infected(neightet)) {
if (apex(*cavetet) != dummypoint) {
// It is a cavity boundary face. Check its visibility.
if (oppo(neightet) != dummypoint) {
pa = org(*cavetet);
pb = dest(*cavetet);
pc = apex(*cavetet);
ori = orient3d(pa, pb, pc, insertpt); orient3dcount++;
enqflag = (ori > 0);
// Comment: if ori == 0 (coplanar case), we also cut the tet.
} else {
// 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 {
if (b->verbose > 3) {
printf(" Cut tet (%d, %d, %d, %d)\n",
pointmark(org(neightet)), pointmark(dest(neightet)),
pointmark(apex(neightet)), pointmark(oppo(neightet)));
}
uninfect(neightet);
unmarktest(neightet);
cutcount++;
// Add three new faces to find new boundaries.
for (j = 0; j < 3; j++) {
esym(neightet, neineitet);
neineitet.ver = epivot[neineitet.ver & 3];
cavebdrylist->newindex((void **) &parytet);
*parytet = neineitet;
enextself(neightet);
}
// Update the hullsize.
if (oppo(neightet) == dummypoint) hullsize++;
// 'cavetet' is not on the cavity boundary anymore.
unmarktest(*cavetet);
}
} else {
// 'cavetet' is not on the cavity boundary anymore.
unmarktest(*cavetet);
}
} // i
if (cutcount > 0) {
// The cavity has been updated.
// Update the cavity boundary faces.
cavebdrylist->restart();
for (i = 0; i < cavetetlist->objects; i++) {
cavetet = (triface *) fastlookup(cavetetlist, i);
// 'cavetet' was an exterior tet adjacent to the cavity.
assert(cavetet->ver == epivot[cavetet->ver & 3]); // SELF_CHECK
assert(!infected(*cavetet));
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) {
printf("Invalid cavity of Steiner point %d.\n", pointmark(insertpt));
assert(0);
}
if (ivf->splitbdflag) { //if (bowywat > 2) { // if (bowywat == 3) {
cutshcount = 0;
// Update the sub-cavity sC(p).
for (i = 0; i < caveshlist->objects; i++) {
parysh = (face *) fastlookup(caveshlist, i);
if (smarktested(*parysh)) {
enqflag = false;
stpivot(*parysh, neightet);
if (infected(neightet)) {
fsymself(neightet);
if (infected(neightet)) {
enqflag = true;
}
}
if (!enqflag) {
if (b->verbose > 3) {
printf(" Cut subface (%d, %d, %d).\n",
pointmark(sorg(*parysh)), pointmark(sdest(*parysh)),
pointmark(sapex(*parysh)));
}
sunmarktest(*parysh);
// Use the last entry of this array to fill this entry.
j = caveshlist->objects - 1;
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) {
// The to-be split subface should be in sC(p).
if (!smarktested(*splitsh)) i++;
}
} else if (loc == ONEDGE) {
if (splitseg != NULL) {
// The to-be split segment should be in sC(p).
if (!smarktested(*splitseg)) i++;
}
if (splitsh != 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 (b->verbose > 3) {
printf(" Found %d invalid items. Reject it.\n", i);
}
for (i = 0; i < caveoldtetlist->objects; i++) {
cavetet = (triface *) fastlookup(caveoldtetlist, i);
uninfect(*cavetet);
unmarktest(*cavetet);
}
for (i = 0; i < cavebdrylist->objects; i++) {
cavetet = (triface *) fastlookup(cavebdrylist, i);
unmarktest(*cavetet); // Unmark it.
}
cavetetlist->restart();
cavebdrylist->restart();
caveoldtetlist->restart();
cavetetseglist->restart();
cavetetshlist->restart();
if (ivf->splitbdflag) { //if (bowywat > 2) {
if (splitseg != NULL) {
sunmarktest(*splitseg);
}
for (i = 0; i < caveshlist->objects; i++) {
parysh = (face *) fastlookup(caveshlist, i);
assert(smarktested(*parysh));
sunmarktest(*parysh);
}
caveshlist->restart();
cavesegshlist->restart();
}
// Restore the hullsize.
hullsize = bakhullsize;
return (int) BADELEMENT;
}
} // if (cutshcount > 0)
} // if (bowywat > 2)
} // if (cutcount > 0)
} // if (validflag) // if (bowywat > 1)
if (b->verbose > 3) {
printf(" Final cavity: %ld tets, %ld faces.",
caveoldtetlist->objects, cavebdrylist->objects);
if (cutcount > 0l) {
printf(" Updated %ld times.", cutcount);
}
printf("\n");
}
if (ivf->refineflag) {
// The new point is inserted by Delaunay refinement, i.e., it is the
// circumcenter of a tetrahedron, or a subface, or a segment.
// Do not insert this point if the tetrahedron, or subface, or segment
// is not inside the final cavity.
rejptflag = 0;
if (ivf->refineflag == 1) {
// The new point is the circumcenter of a tetrahedron.
assert(!isdeadtet(ivf->refinetet));
if (!infected(ivf->refinetet)) {
rejrefinetetcount++;
rejptflag = 1;
}
} else if (ivf->refineflag == 2) {
// The new point is the circumcenter of a subface.
assert(ivf->refinesh.sh != NULL);
if (!smarktested(ivf->refinesh)) {
rejrefineshcount++;
rejptflag = 1;
}
}
if (rejptflag) {
if (b->verbose > 2) {
printf(" Point %d does not refine its element. Rejected.\n",
pointmark(insertpt));
}
// Restore the original status.
for (i = 0; i < caveoldtetlist->objects; i++) {
cavetet = (triface *) fastlookup(caveoldtetlist, i);
uninfect(*cavetet);
unmarktest(*cavetet);
}
for (i = 0; i < cavebdrylist->objects; i++) {
cavetet = (triface *) fastlookup(cavebdrylist, i);
unmarktest(*cavetet); // Unmark it.
}
// Clear working lists.
cavetetlist->restart();
cavebdrylist->restart();
caveoldtetlist->restart();
cavetetshlist->restart();
cavetetseglist->restart();
cavetetvertlist->restart();
if (ivf->splitbdflag) { //if (bowywat > 2) { // if (bowywat == 3) {
if (splitseg != NULL) {
sunmarktest(*splitseg);
}
for (i = 0; i < caveshlist->objects; i++) {
parysh = (face *) fastlookup(caveshlist, i);
assert(smarktested(*parysh));
sunmarktest(*parysh);
}
caveshlist->restart();
cavesegshlist->restart();
}
// Restore the hullsize.
hullsize = bakhullsize;
loc = BADELEMENT;
return (int) loc;
} // if (rejptflag)
} // if (ivf->refineflag)
rejptflag = (ivf->rejflag & 4);
encptflag = 0;
if (b->weighted || b->plc || rejptflag) {
// Collect all vertices of C(p).
for (i = 0; i < caveoldtetlist->objects; i++) {
cavetet = (triface *) fastlookup(caveoldtetlist, i);
assert(infected(*cavetet));
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
if (b->verbose > 3) {
printf(" %ld cavity vertices.\n", cavetetvertlist->objects);
}
// Uninfect all collected (cavity) vertices.
for (i = 0; i < cavetetvertlist->objects; i++) {
parypt = (point *) fastlookup(cavetetvertlist, i);
puninfect(*parypt);
}
if (b->plc || rejptflag) {
// Check if p is too close to an existing vertex.
pts = NULL;
for (i = 0; i < cavetetvertlist->objects; i++) {
parypt = (point *) fastlookup(cavetetvertlist, i);
rd = distance(*parypt, insertpt);
// Is the point very close to an existing point?
if (rd < b->minedgelength) {
pts = parypt;
break;
}
if (rejptflag) {
// Is the point encroaches upon an existing point?
if (rd < (*parypt)[pointmtrindex]) {
// The point lies inside the protection ball.
if (b->verbose > 2) {
printf(" Point %d lies in protball of %d. Rejected.\n",
pointmark(insertpt), pointmark(*parypt));
}
pts = parypt;
encptflag = 1;
break;
}
}
} // i
if (pts != NULL) {
// p is too close to *pts.
if (ivf->iloc != (int) INSTAR) {
if (pointmark(insertpt) <= in->numberofpoints) {
// It's an input point.
if (!b->quiet) {
printf("Warning: Point %d is replaced by point %d.\n",
pointmark(insertpt), pointmark(*pts));
}
// Count the number of duplicated points.
dupverts++;
} else { // It's a Steiner point.
if (b->verbose) {
if (!rejptflag) {
printf("Warning: Reject a Steiner point %d (close to %d).\n",
pointmark(insertpt), pointmark(*pts));
}
}
}
// Remember it is a duplicated point.
setpointtype(insertpt, DUPLICATEDVERTEX);
// Set a pointer to the point it duplicates.
setpoint2ppt(insertpt, *pts);
// Restore the original status.
for (i = 0; i < caveoldtetlist->objects; i++) {
cavetet = (triface *) fastlookup(caveoldtetlist, i);
uninfect(*cavetet);
unmarktest(*cavetet);
}
for (i = 0; i < cavebdrylist->objects; i++) {
cavetet = (triface *) fastlookup(cavebdrylist, i);
unmarktest(*cavetet); // Unmark it.
}
// Clear working lists.
cavetetlist->restart();
cavebdrylist->restart();
caveoldtetlist->restart();
cavetetshlist->restart();
cavetetseglist->restart();
cavetetvertlist->restart();
if (ivf->splitbdflag) { //if (bowywat > 2) { // if (bowywat == 3) {
if (splitseg != NULL) {
sunmarktest(*splitseg);
}
for (i = 0; i < caveshlist->objects; i++) {
parysh = (face *) fastlookup(caveshlist, i);
assert(smarktested(*parysh));
sunmarktest(*parysh);
}
caveshlist->restart();
cavesegshlist->restart();
}
// Restore the hullsize.
hullsize = bakhullsize;
if (!encptflag) {
loc = NEARVERTEX;
} else {
loc = ENCVERTEX;
}
return (int) loc;
} else { // (iloc == (int) INSTAR)
// The cavity is guaranteed to be valid by the caller of this
// function. We still insert this vertex.
if (b->verbose) {
printf("Warning: The Steiner point %d is very close to %d.\n",
pointmark(insertpt), pointmark(*pts));
}
}
} // if (pts != NULL)
}
}
// The new point will be inserted.
totaldeadtets += caveoldtetlist->objects;
totalbowatcavsize += cavebdrylist->objects;
if (maxbowatcavsize < cavebdrylist->objects) {
maxbowatcavsize = cavebdrylist->objects;
}
// Before re-mesh C(p). Process the segments and subfaces which are on the
// boundary of C(p). Make sure that each such segment or subface is
// connecting to a tet outside C(p). So we can re-connect them to the
// new tets inside the C(p) later.
if (checksubsegflag) {
for (i = 0; i < cavetetseglist->objects; i++) {
paryseg = (face *) fastlookup(cavetetseglist, i);
// Operate on it if it is not the splitting segment, i.e., in sC(p).
if (!smarktested(*paryseg)) {
// Check if the segment is inside the cavity.
// 'j' counts the num of adjacent tets of this seg.
// 'k' counts the num of adjacent tets which are 'sinfected'.
j = k = 0;
sstpivot1(*paryseg, neightet);
spintet = neightet;
while (1) {
j++;
if (!infected(spintet)) {
neineitet = spintet; // An outer tet. Remember it.
} else {
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) {//if (bowywat < 3) { // if (bowywat == 2) {
checkseg = *paryseg;
if (b->verbose > 3) {
printf(" Queueing a missing seg (%d, %d)\n",
pointmark(sorg(checkseg)), pointmark(sdest(checkseg)));
}
sinfect(checkseg); // Flag it as an interior segment.
caveencseglist->newindex((void **) &paryseg);
*paryseg = checkseg;
} else {
assert(0); // Not possible.
}
}
} else {
// assert(smarktested(*paryseg));
// Flag it as an interior segment. Do not queue it, since it will
// be deleted after the segment splitting.
sinfect(*paryseg);
}
} // i
if (b->verbose > 3) {
printf(" %ld (%ld) cavity (interior) segments.\n",
cavetetseglist->objects, caveencseglist->objects);
}
} // if (checksubsegflag)
if (checksubfaceflag) {
for (i = 0; i < cavetetshlist->objects; i++) {
parysh = (face *) fastlookup(cavetetshlist, i);
// Operate on it if it is not inside the sub-cavity sC(p).
if (!smarktested(*parysh)) {
// Check if this subface is inside the cavity.
k = 0;
for (j = 0; j < 2; j++) {
stpivot(*parysh, neightet);
if (!infected(neightet)) {
checksh = *parysh; // Remeber 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) { //if (bowywat < 3) { // if (bowywat == 2) {
checksh = *parysh;
if (b->verbose > 3) {
printf(" Queueing a missing subface (%d, %d, %d)\n",
pointmark(sorg(checksh)), pointmark(sdest(checksh)),
pointmark(sapex(checksh)));
}
sinfect(checksh); // Flag it.
caveencshlist->newindex((void **) &parysh);
*parysh = checksh;
} else {
assert(0); // Not possible.
}
}
} 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 (b->verbose > 3) {
printf(" %ld (%ld) cavity (interior) subfaces.\n",
cavetetshlist->objects, caveencshlist->objects);
}
} // if (checksubfaceflag) {
// Create new tetrahedra to fill the cavity.
for (i = 0; i < cavebdrylist->objects; i++) {
cavetet = (triface *) fastlookup(cavebdrylist, i);
neightet = *cavetet;
assert(!infected(neightet));
unmarktest(neightet); // Unmark it.
// Get the oldtet (inside the cavity).
fsym(neightet, oldtet);
if (apex(neightet) != dummypoint) {
// Create a new tet in the cavity (see Fig. bowyerwatson 1 or 3).
maketetrahedron(&newtet);
setorg(newtet, dest(neightet));
setdest(newtet, org(neightet));
setapex(newtet, apex(neightet));
setoppo(newtet, insertpt);
// The new tet inherits attribtes from the old tet.
for (j = 0; j < in->numberoftetrahedronattributes; j++) {
attrib = elemattribute(oldtet.tet, j);
setelemattribute(newtet.tet, j, attrib);
}
if (b->varvolume) {
volume = volumebound(oldtet.tet);
setvolumebound(newtet.tet, volume);
}
} else {
// Create a new hull tet (see Fig. bowyerwatson 2).
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);
}
// 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));
if (ivf->lawson > 1) { // if (lawson == 2 || lawson == 3) {
// Re-use this list to save new interior cavity faces.
cavetetlist->restart();
}
// Connect adjacent new tetrahedra together.
for (i = 0; i < cavebdrylist->objects; i++) {
cavetet = (triface *) fastlookup(cavebdrylist, i);
// cavtet is an oldtet, get the newtet at this face.
oldtet = *cavetet;
fsym(oldtet, neightet);
fsym(neightet, newtet);
// Comment: oldtet and newtet must be at the same directed edge.
// Connect the three other faces of this newtet.
for (j = 0; j < 3; j++) {
esym(newtet, neightet); // Go to the face.
if (neightet.tet[neightet.ver & 3] == NULL) {
// Find the adjacent face of this newtet.
spintet = oldtet;
while (1) {
fnextself(spintet);
if (!infected(spintet)) break;
}
fsym(spintet, newneitet);
esymself(newneitet);
assert(newneitet.tet[newneitet.ver & 3] == NULL); // FOR DEBUG
bond(neightet, newneitet);
if (ivf->lawson > 1) {
// We are updateing a CDT. Queue the internal face.
// See also fig/dump-cavity-case13, -case21.
cavetetlist->newindex((void **) &parytet);
*parytet = neightet;
}
}
setpoint2tet(org(newtet), encode(newtet));
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 (splitsh != NULL) {
// Split a subface or a segment.
sinsertvertex(insertpt, splitsh, splitseg, ivf->sloc, ivf->sbowywat);
}
if (checksubfaceflag) {
if (ivf->splitbdflag) { //if (bowywat > 2) { // if (bowywat == 3) {
// Recover new subfaces in C(p).
for (i = 0; i < caveshbdlist->objects; i++) {
// Get an old subface at edge [a, b].
parysh = (face *) fastlookup(caveshbdlist, i);
spivot(*parysh, checksh); // The new subface [a, b, p].
// Do not recover a deleted new face (degenerated).
if (checksh.sh[3] != NULL) {
// Note that the old subface still connects to adjacent old tets
// of C(p), which still connect to the tets outside C(p).
stpivot(*parysh, neightet);
assert(infected(neightet));
// Find the adjacent tet containing the edge [a,b] outside C(p).
spintet = neightet;
while (1) {
fnextself(spintet);
if (!infected(spintet)) break;
assert(spintet.tet != neightet.tet);
}
// The adjacent tet connects to a new tet in C(p).
fsym(spintet, neightet);
assert(!infected(neightet));
// Find the tet containing the face [a, b, p].
spintet = neightet;
while (1) {
fnextself(spintet);
if (apex(spintet) == insertpt) break;
assert(spintet.tet != neightet.tet);
}
// Adjust the edge direction in spintet and checksh.
if (sorg(checksh) != org(spintet)) {
sesymself(checksh);
assert(sorg(checksh) == org(spintet));
}
assert(sdest(checksh) == dest(spintet));
// Connect the subface to two adjacent tets.
tsbond(spintet, checksh);
fsymself(spintet);
sesymself(checksh);
tsbond(spintet, checksh);
} // if (checksh.sh[3] != NULL)
}
// There should be no missing interior subfaces in C(p).
assert(caveencshlist->objects == 0l);
} else {
// bowywat = 1 or bowywat = 2.
// The Boundary reocvery 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) {
if (b->verbose > 3) {
printf(" Queue new subface (%d, %d, %d).\n",
pointmark(sorg(checksh)), pointmark(sdest(checksh)),
pointmark(sapex(checksh)));
}
//sdissolve(checksh); // It has not been connected yet.
subfacstack->newindex((void **) &parysh);
*parysh = checksh;
}
}
// Put all interior subfaces into stack for recovery.
for (i = 0; i < caveencshlist->objects; i++) {
parysh = (face *) fastlookup(caveencshlist, i);
assert(sinfected(*parysh));
// Some subfaces inside C(p) might be split in sinsertvertex().
// Only queue those faces which are not split.
if (!smarktested(*parysh)) {
if (b->verbose > 3) {
printf(" Queue a missing subface (%d, %d, %d) x%lx.\n",
pointmark(sorg(*parysh)), pointmark(sdest(*parysh)),
pointmark(sapex(*parysh)), (uintptr_t) parysh->sh);
}
checksh = *parysh;
suninfect(checksh);
stdissolve(checksh); // Detach connections to old tets.
subfacstack->newindex((void **) &parysh);
*parysh = checksh;
}
}
}
} // if (checksubfaceflag)
if (checksubsegflag) {
if (ivf->splitbdflag) { //if (bowywat > 2) { // if (bowywat == 3) {
if (splitseg != NULL) {
// Recover the two new subsegments in C(p).
for (i = 0; i < cavesegshlist->objects; i++) {
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.
pa = sorg(checkseg);
pb = sdest(checkseg);
point2tetorg(pa, neightet);
finddirection(&neightet, pb, 1);
assert(dest(neightet) == pb);
}
assert(!infected(neightet));
sstbond1(checkseg, neightet);
spintet = neightet;
while (1) {
tssbond1(spintet, checkseg);
fnextself(spintet);
if (spintet.tet == neightet.tet) break;
}
}
} // if (splitseg != NULL)
// There should be no interior segment in C(p).
assert(caveencseglist->objects == 0l);
} else {
// bowywat == 1 or bowywat == 2;
// The Boundary Recovery Phase.
// Queue missing segments in C(p) for recovery.
if (splitseg != NULL) {
// Queue two new subsegments in C(p) for recovery.
for (i = 0; i < cavesegshlist->objects; i++) {
paryseg = (face *) fastlookup(cavesegshlist, i);
if (b->verbose > 3) {
printf(" Queue new subseg (%d, %d)\n",
pointmark(sorg(*paryseg)), pointmark(sdest(*paryseg)));
}
checkseg = *paryseg;
//sstdissolve1(checkseg); // It has not been connected yet.
s = randomnation(subsegstack->objects + 1);
subsegstack->newindex((void **) &paryseg);
*paryseg = * (face *) fastlookup(subsegstack, s);
paryseg = (face *) fastlookup(subsegstack, s);
*paryseg = checkseg;
}
} // if (splitseg != NULL)
for (i = 0; i < caveencseglist->objects; i++) {
paryseg = (face *) fastlookup(caveencseglist, i);
assert(sinfected(*paryseg));
if (!smarktested(*paryseg)) { // It may be split.
if (b->verbose > 3) {
printf(" Queue a missing segment (%d, %d).\n",
pointmark(sorg(*paryseg)), pointmark(sdest(*paryseg)));
}
checkseg = *paryseg;
suninfect(checkseg);
sstdissolve1(checkseg); // Detach connections to old tets.
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->plc || b->weighted) {
// Some vertices may be completed inside the cavity. They must be
// detected and added to recovering list.
if (b->plc) {
tetcount = subvertstack->objects; // Re-use tetcount;
}
// Since every "live" vertex must contain a pointer to a non-dead
// tetrahedron, we can check for each vertex this pointer.
for (i = 0; i < cavetetvertlist->objects; i++) {
pts = (point *) fastlookup(cavetetvertlist, i);
decode(point2tet(*pts), *searchtet);
assert(searchtet->tet != NULL); // No tet has been deleted yet.
if (infected(*searchtet)) {
if (b->weighted) {
if (b->verbose > 2) {
printf(" Point #%d is removed from the hull.\n",
pointmark(*pts));
}
setpointtype(*pts, UNUSEDVERTEX);
} else {
if (b->verbose > 3) {
printf(" Queue a dangling vertex %d.\n", pointmark(*pts));
}
subvertstack->newindex((void **) &parypt);
*parypt = *pts;
}
}
}
if (b->plc) {
if (subvertstack->objects > tetcount) {
// There are missing vertices after inserting the new point.
printf("DBG: Insert %d. Found %ld interior vertices.\n",
pointmark(insertpt), subvertstack->objects);
assert(0); // NEED TO DEBUG.
}
}
}
if (ivf->chkencflag & 1) {
// 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)) {
// Skip it if it has already queued.
if (!smarktest2ed(*paryseg)) {
bface = (badface *) badsubsegs->alloc();
bface->ss = *paryseg;
smarktest2(bface->ss); // Only queue it once.
bface->forg = sorg(*paryseg); // An alive badface.
}
}
}
if (splitseg != NULL) {
// Queue the two new subsegments inside C(p).
for (i = 0; i < cavesegshlist->objects; i++) {
paryseg = (face *) fastlookup(cavesegshlist, i);
bface = (badface *) badsubsegs->alloc();
bface->ss = *paryseg;
smarktest2(bface->ss); // Only queue it once.
bface->forg = sorg(*paryseg); // An alive badface.
}
}
} // 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)) {
// Skip it if it has already queued.
if (!smarktest2ed(*parysh)) {
bface = (badface *) badsubfacs->alloc();
bface->ss = *parysh;
smarktest2(bface->ss); // Only queue it once.
bface->forg = sorg(*parysh); // An alive badface.
//bface->fdest = sdest(*parysh);
//bface->fapex = sapex(*parysh);
}
}
}
// Queue all new subfaces inside C(p).
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) {
//assert(!smarktest2ed(checksh));
bface = (badface *) badsubfacs->alloc();
bface->ss = checksh;
smarktest2(bface->ss); // Only queue it once.
bface->forg = sorg(checksh); // An alive badface.
}
}
} // if (chkencflag & 2)
if (ivf->chkencflag & 4) {
// Queue all new tetrahedra in C(p).
for (i = 0; i < cavebdrylist->objects; i++) {
cavetet = (triface *) fastlookup(cavebdrylist, i);
//assert(!marktest2ed(*cavetet));
bface = (badface *) badtetrahedrons->alloc();
bface->tt = *cavetet;
marktest2(bface->tt);
bface->forg = org(*cavetet);
}
}
// C(p) is re-meshed successfully.
// Deleted the old tets in C(p).
for (i = 0; i < caveoldtetlist->objects; i++) {
searchtet = (triface *) fastlookup(caveoldtetlist, i);
tetrahedrondealloc(searchtet->tet);
}
if (splitsh != NULL) {
// Delete the old subfaces in sC(p).
for (i = 0; i < caveshlist->objects; i++) {
parysh = (face *) fastlookup(caveshlist, i);
if (checksubfaceflag) {//if (bowywat == 2) {
// It is possible that this subface still connects to adjacent
// tets which are not in C(p). If so, clear connections in the
// adjacent tets at this subface.
stpivot(*parysh, neightet);
if (neightet.tet != NULL) {
if (neightet.tet[4] != NULL) {
// Found an adjacent tet. It must be not in C(p).
assert(!infected(neightet));
tsdissolve(neightet);
fsymself(neightet);
assert(!infected(neightet));
tsdissolve(neightet);
}
}
}
shellfacedealloc(subfaces, parysh->sh);
}
if (splitseg != NULL) {
// Delete the old segment in sC(p).
shellfacedealloc(subsegs, splitseg->sh);
}
}
if (ivf->lawson) {
for (i = 0; i < cavebdrylist->objects; i++) {
searchtet = (triface *) fastlookup(cavebdrylist, i);
//flippush(flipstack, searchtet, insertpt);
flippush(flipstack, searchtet);
}
if (ivf->lawson > 1) {
for (i = 0; i < cavetetlist->objects; i++) {
searchtet = (triface *) fastlookup(cavetetlist, i);
//flippush(flipstack, searchtet, oppo(*searchtet));
flippush(flipstack, searchtet);
}
}
}
// The vertex should already have a type.
assert(pointtype(insertpt) != UNUSEDVERTEX);
#ifdef WITH_RUNTIME_COUNTERS
tend = clock();
t_ptinsert += (tend - tstart);
#endif
if (b->btree) {
btree_insert(insertpt);
}
// Clean the working lists.
caveoldtetlist->restart();
cavebdrylist->restart();
cavetetlist->restart();
if (checksubsegflag) {
cavetetseglist->restart();
caveencseglist->restart();
}
if (checksubfaceflag) {
cavetetshlist->restart();
caveencshlist->restart();
}
if (b->plc || b->weighted) {
cavetetvertlist->restart();
}
if (splitsh != NULL) {
caveshlist->restart();
caveshbdlist->restart();
cavesegshlist->restart();
}
return (int) loc;
}
//// ////
//// ////
//// flip_cxx /////////////////////////////////////////////////////////////////
//// delaunay_cxx /////////////////////////////////////////////////////////////
//// ////
//// ////
///////////////////////////////////////////////////////////////////////////////
// //
// transfernodes() Read the vertices from the input (tetgenio). //
// //
// Initializing 'this->points'. Transferring all points from 'in->pointlist'//
// into it. All points are indexed (start from in->firstnumber). Each point //
// is initialized be UNUSEDVERTEX. The bounding box (xmin, xmax, ymin, ymax,//
// zmin, zmax) and the diameter (longest) of the point set are calculated. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::transfernodes()
{
point pointloop;
REAL x, y, z, w;
int coordindex;
int attribindex;
int mtrindex;
int i, j;
if (b->psc) {
assert(in->pointparamlist != NULL);
}
// 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++];
if (b->weighted) { // -w option
if (in->numberofpointattributes > 0) {
// The first point attribute is weight.
w = in->pointattributelist[in->numberofpointattributes * i];
} else {
// No given weight available.
w = 0;
}
if (b->weighted_param == 0) {
pointloop[3] = x * x + y * y + z * z - w; // Weighted DT.
} else { // -w1 option
pointloop[3] = w; // Regular tetrahedralization.
}
} else {
pointloop[3] = 0;
}
// Read the point attributes.
for (j = 0; j < in->numberofpointattributes; j++) {
pointloop[4 + j] = in->pointattributelist[attribindex++];
}
// Read the point metric tensor.
for (j = 0; j < in->numberofpointmtrs; j++) {
pointloop[pointmtrindex + j] = in->pointmtrlist[mtrindex++];
}
// Determine the smallest and largests x, y and z coordinates.
if (i == 0) {
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(3);
}
// Two identical points are distinguished by 'lengthlimit'.
if (b->minedgelength == 0.0) {
b->minedgelength = longest * b->epsilon;
}
}
///////////////////////////////////////////////////////////////////////////////
// //
// btree_sort() Sort vertices using a binary space partition (bsp) tree. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::btree_sort(point* vertexarray, int arraysize, int axis,
REAL bxmin, REAL bxmax, REAL bymin, REAL bymax,
REAL bzmin, REAL bzmax, int depth)
{
point *leftarray, *rightarray;
point **pptary, swapvert;
REAL split;
bool lflag, rflag;
int i, j, k; // *iptr,
if (b->verbose > 3) {
printf(" Depth %d, %d verts. Bbox (%g, %g, %g),(%g, %g, %g). %s-axis\n",
depth, arraysize, bxmin, bymin, bzmin, bxmax, bymax, bzmax,
axis == 0 ? "x" : (axis == 1 ? "y" : "z"));
}
if (depth > max_btree_depth) {
max_btree_depth = depth;
}
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);
}
i = 0;
j = arraysize - 1;
// Partition the vertices into left- and right-arraies.
do {
for (; i < arraysize; i++) {
if (vertexarray[i][axis] >= split) {
break;
}
}
for (; j >= 0; j--) {
if (vertexarray[j][axis] < split) {
break;
}
}
// Is the partition finished?
if (i == (j + 1)) {
break;
}
// Swap i-th and j-th vertices.
swapvert = vertexarray[i];
vertexarray[i] = vertexarray[j];
vertexarray[j] = swapvert;
// Continue patitioning the array;
} while (true);
if (b->verbose > 3) {
printf(" leftsize = %d, rightsize = %d\n", i, arraysize - i);
}
lflag = rflag = false;
// Do not continue the partition if one of the array sizes is 0.
// if (depth < max_tree_depth) {
if (!((i == 0) || (i == arraysize))) {
if (i > b->max_btreenode_size) {
// Recursively partition the left array (length = i).
if (axis == 0) { // x
btree_sort(vertexarray, i, (axis + 1) % 3, bxmin, split, bymin,
bymax, bzmin, bzmax, depth + 1);
} else if (axis == 1) { // y
btree_sort(vertexarray, i, (axis + 1) % 3, bxmin, bxmax, bymin,
split, bzmin, bzmax, depth + 1);
} else { // z
btree_sort(vertexarray, i, (axis + 1) % 3, bxmin, bxmax, bymin,
bymax, bzmin, split, depth + 1);
}
} else {
lflag = true;
}
if ((arraysize - i) > b->max_btreenode_size) {
// Recursively partition the right array (length = arraysize - i).
if (axis == 0) { // x
btree_sort(&(vertexarray[i]), arraysize - i, (axis + 1) % 3, split,
bxmax, bymin, bymax, bzmin, bzmax, depth + 1);
} else if (axis == 1) { // y
btree_sort(&(vertexarray[i]), arraysize - i, (axis + 1) % 3, bxmin,
bxmax, split, bymax, bzmin, bzmax, depth + 1);
} else { // z
btree_sort(&(vertexarray[i]), arraysize - i, (axis + 1) % 3, bxmin,
bxmax, bymin, bymax, split, bzmax, depth + 1);
}
} else {
rflag = true;
}
} else {
// Both left and right are done.
lflag = rflag = true;
}
if (lflag && (i > 0)) {
// Remember the maximal length of the partitions.
if (i > max_btreenode_size) {
max_btreenode_size = i;
}
// Allocate space for the left array (use the first entry to save
// the length of this array).
leftarray = new point[i + 1];
leftarray[0] = (point) i; // The array lenth.
//iptr = (int *) &(leftarray[0]);
//*iptr = i; // Save the array lenth in the first entry.
// Put all points in this array.
for (k = 0; k < i; k++) {
leftarray[k + 1] = vertexarray[k];
setpoint2ppt(leftarray[k + 1], (point) leftarray);
}
// Save this array in list.
btreenode_list->newindex((void **) &pptary);
*pptary = leftarray;
}
// Get the length of the right array.
j = arraysize - i;
if (rflag && (j > 0)) {
if (j > max_btreenode_size) {
max_btreenode_size = j;
}
// Allocate space for the right array (use the first entry to save
// the length of this array).
rightarray = new point[j + 1];
rightarray[0] = (point) j; // The array lenth.
//iptr = (int *) &(rightarray[0]);
//*iptr = j; // Save the array length in the first entry.
// Put all points in this array.
for (k = 0; k < j; k++) {
rightarray[k + 1] = vertexarray[i + k];
setpoint2ppt(rightarray[k + 1], (point) rightarray);
}
// Save this array in list.
btreenode_list->newindex((void **) &pptary);
*pptary = rightarray;
}
}
///////////////////////////////////////////////////////////////////////////////
// //
// btree_insert() Add a vertex into a tree node. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::btree_insert(point insertpt)
{
point *ptary;
long arylen; // The array lenhgth is saved in ptary[0].
// Get the tree node (save in this point).
ptary = (point *) point2ppt(insertpt);
// Get the current array length.
arylen = (long) ptary[0];
// Insert the point into the node.
ptary[arylen + 1] = insertpt;
// Increase the array length by 1.
ptary[0] = (point) (arylen + 1);
}
///////////////////////////////////////////////////////////////////////////////
// //
// btree_search() Search a near point for an inserting point. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::btree_search(point insertpt, triface* searchtet)
{
point *ptary;
point nearpt, candpt;
REAL dist2, mindist2;
int ptsamples, ptidx;
long arylen;
int i;
// Get the tree node (save in this point).
ptary = (point *) point2ppt(insertpt);
// Get the current array length.
arylen = (long) ptary[0];
if (arylen == 0) {
searchtet->tet = NULL;
return;
}
if (1) {
if (arylen < 5) { //if (arylen < 10) {
ptsamples = arylen;
} else {
ptsamples = 5; // Take at least 10 samples.
// The number of random samples taken is proportional to the third root
// of the number of points in the cell.
while (ptsamples * ptsamples * ptsamples < arylen) {
ptsamples++;
}
}
// Select "good" candidate using k random samples, taking the closest one.
mindist2 = 1.79769E+308; // The largest double value (8 byte).
nearpt = NULL;
for (i = 0; i < ptsamples; i++) {
ptidx = randomnation((unsigned long) arylen);
candpt = ptary[ptidx + 1];
dist2 = (candpt[0] - insertpt[0]) * (candpt[0] - insertpt[0])
+ (candpt[1] - insertpt[1]) * (candpt[1] - insertpt[1])
+ (candpt[2] - insertpt[2]) * (candpt[2] - insertpt[2]);
if (dist2 < mindist2) {
mindist2 = dist2;
nearpt = candpt;
}
}
} else {
// TEST, randomly select a point.
ptidx = randomnation((unsigned long) arylen);
nearpt = ptary[ptidx + 1];
}
if (b->verbose > 2) {
printf(" Get point %d (cell size %ld).\n", pointmark(nearpt), arylen);
}
decode(point2tet(nearpt), *searchtet);
}
///////////////////////////////////////////////////////////////////////////////
// //
// ordervertices() Order the vertices for incremental inserting. //
// //
// We assume the vertices have been sorted by a binary tree. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::ordervertices(point* vertexarray, int arraysize)
{
point **ipptary, **jpptary, *swappptary;
point *ptary;
long arylen;
int index, i, j;
// First pick one vertex from each tree node.
for (i = 0; i < (int) btreenode_list->objects; i++) {
ipptary = (point **) fastlookup(btreenode_list, i);
ptary = *ipptary;
vertexarray[i] = ptary[1]; // Skip the first entry.
}
index = i;
// Then put all other points in the array node by node.
for (i = (int) btreenode_list->objects - 1; i >= 0; i--) {
// Randomly pick a tree node.
j = randomnation(i + 1);
// Save the i-th node.
ipptary = (point **) fastlookup(btreenode_list, i);
// Get the j-th node.
jpptary = (point **) fastlookup(btreenode_list, j);
// Order the points in the node.
ptary = *jpptary;
arylen = (long) ptary[0];
for (j = 2; j <= arylen; j++) { // Skip the first point.
vertexarray[index] = ptary[j];
index++;
}
// Clear this tree node.
ptary[0] = (point) 0;
// Swap i-th node to j-th node.
swappptary = *ipptary;
*ipptary = *jpptary; // [i] <= [j]
*jpptary = swappptary; // [j] <= [i]
}
// Make sure we've done correctly.
assert(index == arraysize);
}
///////////////////////////////////////////////////////////////////////////////
// //
// randomnation() Generate a random number between 0 and 'choices' - 1. //
// //
///////////////////////////////////////////////////////////////////////////////
unsigned long tetgenmesh::randomnation(unsigned int choices)
{
unsigned long newrandom;
if (choices >= 714025l) {
newrandom = (randomseed * 1366l + 150889l) % 714025l;
randomseed = (newrandom * 1366l + 150889l) % 714025l;
newrandom = newrandom * (choices / 714025l) + randomseed;
if (newrandom >= choices) {
return newrandom - choices;
} else {
return newrandom;
}
} else {
randomseed = (randomseed * 1366l + 150889l) % 714025l;
return randomseed % choices;
}
}
///////////////////////////////////////////////////////////////////////////////
// //
// randomsample() Randomly sample the tetrahedra for point loation. //
// //
// This routine implements Muecke's Jump-and-walk point location algorithm. //
// It improves the simple walk-through by "jumping" to a good starting point //
// via random sampling. Searching begins from one of handles: the input //
// 'searchtet', a recently encountered tetrahedron 'recenttet', or from one //
// chosen from a random sample. The choice is made by determining which one //
// 's origin is closest to the point we are searcing for. Having chosen the //
// starting tetrahedron, the simple Walk-through algorithm is executed. //
// //
///////////////////////////////////////////////////////////////////////////////
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 (searchtet->tet == NULL) {
// A null tet. Choose the recenttet as the starting tet.
*searchtet = recenttet;
// Recenttet should not be dead.
assert(recenttet.tet[4] != NULL);
}
// 'searchtet' should be a valid tetrahedron. Choose the base face
// whose vertices must not be 'dummypoint'.
searchtet->ver = 3;
// Record the distance from its origin to the searching point.
torg = org(*searchtet);
searchdist = (searchpt[0] - torg[0]) * (searchpt[0] - torg[0]) +
(searchpt[1] - torg[1]) * (searchpt[1] - torg[1]) +
(searchpt[2] - torg[2]) * (searchpt[2] - torg[2]);
if (b->verbose > 3) {
printf(" Dist %g from tet (%d, %d, %d, %d).\n", searchdist,
pointmark(torg), pointmark(dest(*searchtet)),
pointmark(apex(*searchtet)), pointmark(oppo(*searchtet)));
}
// If a recently encountered tetrahedron has been recorded and has not
// been deallocated, test it as a good starting point.
if (recenttet.tet != searchtet->tet) {
recenttet.ver = 3;
torg = org(recenttet);
dist = (searchpt[0] - torg[0]) * (searchpt[0] - torg[0]) +
(searchpt[1] - torg[1]) * (searchpt[1] - torg[1]) +
(searchpt[2] - torg[2]) * (searchpt[2] - torg[2]);
if (dist < searchdist) {
*searchtet = recenttet;
searchdist = dist;
if (b->verbose > 3) {
printf(" Dist %g from recent tet (%d, %d, %d, %d).\n",
searchdist, pointmark(torg), pointmark(dest(*searchtet)),
pointmark(apex(*searchtet)), pointmark(oppo(*searchtet)));
}
}
}
// 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;
if (b->verbose > 3) {
printf(" Dist %g from tet (%d, %d, %d, %d).\n", searchdist,
pointmark(torg), pointmark(dest(*searchtet)),
pointmark(apex(*searchtet)), pointmark(oppo(*searchtet)));
}
}
} else {
// A dead tet. Re-sample it.
if (i != tetblocks - 1) j--;
}
}
sampleblock = (void **) *sampleblock;
}
}
///////////////////////////////////////////////////////////////////////////////
// //
// locate() Find a tetrahedron containing a given point. //
// //
// This routine implements the simple Walk-through point location algorithm. //
// Begins its search from 'searchtet', assume there is a line segment L from //
// a vertex of 'searchtet' to the query point 'searchpt', and simply walk //
// towards 'searchpt' by traversing all faces intersected by L. //
// //
// On completion, 'searchtet' is a tetrahedron that contains 'searchpt'. The //
// returned value indicates one of the following cases: //
// - ONVERTEX, the search point lies on the origin of 'searchtet'. //
// - ONEDGE, the search point lies on an edge of 'searchtet'. //
// - ONFACE, the search point lies on a face of 'searchtet'. //
// - INTET, the search point lies in the interior of 'searchtet'. //
// - OUTSIDE, the search point lies outside the mesh. 'searchtet' is a //
// hull tetrahedron whose base face is visible by the search point. //
// //
// WARNING: This routine is designed for convex triangulations, and will not //
// generally work after the holes and concavities have been carved. //
// //
// If 'randflag' is set (> 0). Randomly choose a tetrahedron when there are //
// multiple choices in the path. //
// //
///////////////////////////////////////////////////////////////////////////////
enum tetgenmesh::locateresult
tetgenmesh::locate(point searchpt, triface* searchtet, int chkencflag,
int randflag)
{
triface neightet;
face checksh;
point torg, tdest, tapex, toppo;
enum {ORGMOVE, DESTMOVE, APEXMOVE} nextmove;
REAL ori, oriorg, oridest, oriapex;
enum locateresult loc;
int s; // i;
if (searchtet->tet == NULL) {
// A null tet. Choose the recenttet as the starting tet.
*searchtet = recenttet;
// Recenttet should not be dead.
assert(recenttet.tet[4] != NULL);
}
// 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);
assert(!ishulltet(*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); orient3dcount++;
if (ori < 0.0) break;
}
if (searchtet->ver == 4) {
// Either 'searchtet' is a very flat tet, or the 'searchpt' lies in
// infinity, or both of them. Return OUTSIDE.
assert(0); // return OUTSIDE;
}
loc = OUTSIDE; // Set a default return value.
// Walk through tetrahedra to locate the point.
while (true) {
ptloc_count++; // Algorithimic count.
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 serarchtet's base face. There are three other faces in
// searchtet (all connecting to toppo), which one is the exit?
oriorg = orient3d(tdest, tapex, toppo, searchpt);
oridest = orient3d(tapex, torg, toppo, searchpt);
oriapex = orient3d(torg, tdest, toppo, searchpt);
orient3dcount+=3;
// Now decide which face to move. It is possible there are more than one
// faces are viable moves. Use the opposite points of thier neighbors
// to discriminate, i.e., we choose the face whose opposite point has
// the shortest distance to searchpt.
if (oriorg < 0) {
if (oridest < 0) {
if (oriapex < 0) {
if (0) { //if (!randflag) {
} else {
// Randomly choose a direction.
s = randomnation(3); // 's' is in {0,1,2}.
if (s == 0) {
nextmove = ORGMOVE;
} else if (s == 1) {
nextmove = DESTMOVE;
} else {
nextmove = APEXMOVE;
}
} // if (randflag)
} else {
// Two faces, opposite to origin and destination, are viable.
if (0) { // if (!randflag) {
} else {
// Randomly choose a direction.
s = randomnation(2); // 's' is in {0,1}.
if (s == 0) {
nextmove = ORGMOVE;
} else {
nextmove = DESTMOVE;
}
} // if (randflag)
}
} else {
if (oriapex < 0) {
// Two faces, opposite to origin and apex, are viable.
if (0) { // if (!randflag) {
} else {
// Randomly choose a direction.
s = randomnation(2); // 's' is in {0,1}.
if (s == 0) {
nextmove = ORGMOVE;
} else {
nextmove = APEXMOVE;
}
} // if (randflag)
} else {
// Only the face opposite to origin is viable.
nextmove = ORGMOVE;
}
}
} else {
if (oridest < 0) {
if (oriapex < 0) {
// Two faces, opposite to destination and apex, are viable.
if (0) { // if (!randflag) {
} else {
// Randomly choose a direction.
s = randomnation(2); // 's' is in {0,1}.
if (s == 0) {
nextmove = DESTMOVE;
} else {
nextmove = APEXMOVE;
}
} // if (randflag)
} else {
// Only the face opposite to destination is viable.
nextmove = DESTMOVE;
}
} else {
if (oriapex < 0) {
// Only the face opposite to apex is viable.
nextmove = APEXMOVE;
} else {
// The point we seek must be on the boundary of or inside this
// tetrahedron. Check for boundary cases.
if (oriorg == 0) {
// Go to the face opposite to origin.
//enextfnextself(*searchtet);
enextesymself(*searchtet);
if (oridest == 0) {
//enextself(*searchtet); // edge apex->oppo
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) {
//enext2self(*searchtet);
enextself(*searchtet); // edge dest->oppo
loc = ONEDGE; // return ONEDGE;
break;
}
loc = ONFACE; // return ONFACE;
break;
}
if (oridest == 0) {
// Go to the face opposite to destination.
//enext2fnextself(*searchtet);
eprevesymself(*searchtet);
if (oriapex == 0) {
//enextself(*searchtet);
eprevself(*searchtet); // edge oppo->org
loc = ONEDGE; // return ONEDGE;
break;
}
loc = ONFACE; // return ONFACE;
break;
}
if (oriapex == 0) {
// Go to the face opposite to apex
//fnextself(*searchtet);
esymself(*searchtet);
loc = ONFACE; // return ONFACE;
break;
}
loc = 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.
tspivot(*searchtet, checksh);
if (checksh.sh != NULL) {
loc = ENCSUBFACE;
break;
}
}
// Move to the adjacent tetrahedron (maybe a hull tetrahedron).
fsymself(*searchtet);
if (oppo(*searchtet) == dummypoint) {
loc = OUTSIDE; // return OUTSIDE;
break;
}
// Retreat the three vertices of the base face.
torg = org(*searchtet);
tdest = dest(*searchtet);
tapex = apex(*searchtet);
} // while (true)
return loc;
}
///////////////////////////////////////////////////////////////////////////////
// //
// initialdelaunay() Create an initial Delaunay tetrahedralization. //
// //
// The tetrahedralization contains only one tetrahedron abcd, and four hull //
// tetrahedra. The points pa, pb, pc, and pd must be linearly independent. //
// //
///////////////////////////////////////////////////////////////////////////////
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);
}
if (b->btree) {
btree_insert(pa);
btree_insert(pb);
btree_insert(pc);
btree_insert(pd);
}
setpoint2tet(pa, encode(firsttet));
setpoint2tet(pb, encode(firsttet));
setpoint2tet(pc, encode(firsttet));
setpoint2tet(pd, encode(firsttet));
// Remember the first tetrahedron.
recenttet = firsttet;
}
///////////////////////////////////////////////////////////////////////////////
// //
// incrementaldelaunay() Create a Delaunay tetrahedralization by //
// the incremental approach. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::incrementaldelaunay(clock_t& tv)
{
triface searchtet;
point *permutarray, swapvertex;
insertvertexflags ivf;
REAL v1[3], v2[3], n[3];
REAL bboxsize, bboxsize2, bboxsize3, ori;
int randindex, loc;
int i, j;
if (!b->quiet) {
printf("Delaunizing vertices...\n");
}
if (b->max_btreenode_size > 0) { // not -u0.
b->btree = 1;
btreenode_list = new arraypool(sizeof(point*), 10);
max_btreenode_size = 0;
max_btree_depth = 0;
}
// Form a random permuation (uniformly at random) of the set of vertices.
permutarray = new point[in->numberofpoints];
points->traversalinit();
if (b->btree) { // -u option
for (i = 0; i < in->numberofpoints; i++) {
permutarray[i] = (point) points->traverse();
}
if (b->verbose) {
printf(" Sorting vertices by a bsp-tree.\n");
}
// Sort the points using a binary tree recursively.
btree_sort(permutarray, in->numberofpoints, 0, xmin, xmax, ymin, ymax,
zmin, zmax, 0);
if (b->verbose) {
printf(" Number of tree nodes: %ld.\n", btreenode_list->objects);
printf(" Maximum tree node size: %d.\n", max_btreenode_size);
printf(" Maximum tree depth: %d.\n", max_btree_depth);
}
// Order the sorted points.
ordervertices(permutarray, in->numberofpoints);
} else if (b->hilbertcurve) {
if (b->verbose) {
printf(" Sorting vertices by hilbert curve.\n");
}
// To be done...
for (i = 0; i < in->numberofpoints; i++) {
permutarray[i] = (point) points->traverse();
}
} else {
if (b->verbose) {
printf(" Permuting vertices.\n");
}
for (i = 0; i < in->numberofpoints; i++) {
randindex = randomnation(i + 1);
permutarray[i] = permutarray[randindex];
permutarray[randindex] = (point) points->traverse();
}
}
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 ((DIST(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(10);
}
}
if (i > 1) {
// Swap to move the non-indetical 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.
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(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-indetical 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 = orient3d(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(10);
}
ori = orient3d(permutarray[0], permutarray[1], permutarray[2],
permutarray[i]);
}
if (i > 3) {
// Swap to move the non-indetical vertex from index i to index 1.
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");
}
// Choose algorithm: Bowyer-Watson (default) or Incremental Flip (-l).
if (b->incrflip) {
ivf.bowywat = 0;
ivf.lawson = 1;
} else {
ivf.bowywat = 1;
ivf.lawson = 0;
}
for (i = 4; i < in->numberofpoints; i++) {
if (b->verbose > 2) printf(" #%d", i);
if (pointtype(permutarray[i]) == UNUSEDVERTEX) {
setpointtype(permutarray[i], VOLVERTEX);
}
// Auto choose the starting tet for point location.
searchtet.tet = NULL;
ivf.iloc = (int) OUTSIDE;
// Insert the vertex.
loc = insertvertex(permutarray[i], &searchtet, NULL, NULL, &ivf);
if (loc == (int) ONVERTEX) {
// The point already exists. Mark it and do nothing on it.
swapvertex = org(searchtet);
assert(swapvertex != permutarray[i]); // SELF_CHECK
if (b->object != tetgenbehavior::STL) {
if (!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++;
continue;
}
if (ivf.lawson) {
// If -l option. Perform flip to recover Delaunayness.
lawsonflip3d(permutarray[i], ivf.lawson, 0, 0, 0);
}
}
if (b->btree) {
// Bsp-tree is used only in DT construction.
point **pptary;
for (i = 0; i < (int) btreenode_list->objects; i++) {
pptary = (point **) fastlookup(btreenode_list, i);
delete [] *pptary;
}
delete btreenode_list;
b->btree = 0; // Disable it.
}
delete [] permutarray;
}
//// ////
//// ////
//// delaunay_cxx /////////////////////////////////////////////////////////////
//// surface_cxx //////////////////////////////////////////////////////////////
//// ////
//// ////
///////////////////////////////////////////////////////////////////////////////
// //
// calculateabovepoint() Calculate a point above a facet in 'dummypoint'. //
// //
///////////////////////////////////////////////////////////////////////////////
bool tetgenmesh::calculateabovepoint(arraypool *facpoints, point *ppa,
point *ppb, point *ppc)
{
point *ppt, pa, pb, pc;
REAL v1[3], v2[3], n[3];
REAL lab, len, A, area;
REAL x, y, z;
int i;
ppt = (point *) fastlookup(facpoints, 0);
pa = *ppt; // a is the first point.
pb = pc = NULL; // Avoid compiler warnings.
// Get a point b s.t. the length of [a, b] is maximal.
lab = 0;
for (i = 1; i < facpoints->objects; i++) {
ppt = (point *) fastlookup(facpoints, i);
x = (*ppt)[0] - pa[0];
y = (*ppt)[1] - pa[1];
z = (*ppt)[2] - pa[2];
len = x * x + y * y + z * z;
if (len > lab) {
lab = len;
pb = *ppt;
}
}
lab = sqrt(lab);
if (lab == 0) {
if (!b->quiet) {
printf("Warning: All points of a facet are coincident with %d.\n",
pointmark(pa));
}
return false;
}
// Get a point c s.t. the area of [a, b, c] is maximal.
v1[0] = pb[0] - pa[0];
v1[1] = pb[1] - pa[1];
v1[2] = pb[2] - pa[2];
A = 0;
for (i = 1; i < facpoints->objects; i++) {
ppt = (point *) fastlookup(facpoints, i);
v2[0] = (*ppt)[0] - pa[0];
v2[1] = (*ppt)[1] - pa[1];
v2[2] = (*ppt)[2] - pa[2];
CROSS(v1, v2, n);
area = DOT(n, n);
if (area > A) {
A = area;
pc = *ppt;
}
}
if (A == 0) {
// All points are collinear. No above point.
if (!b->quiet) {
printf("Warning: All points of a facet are collinaer with [%d, %d].\n",
pointmark(pa), pointmark(pb));
}
return false;
}
// Calculate an above point of this facet.
facenormal(pa, pb, pc, n, 1, NULL);
len = sqrt(DOT(n, n));
n[0] /= len;
n[1] /= len;
n[2] /= len;
lab /= 2.0; // Half the maximal length.
dummypoint[0] = pa[0] + lab * n[0];
dummypoint[1] = pa[1] + lab * n[1];
dummypoint[2] = pa[2] + lab * n[2];
if (ppa != NULL) {
// Return the three points.
*ppa = pa;
*ppb = pb;
*ppc = pc;
}
return true;
}
///////////////////////////////////////////////////////////////////////////////
// //
// Calculate an above point. It lies above the plane containing the subface //
// [a,b,c], and save it in dummypoint. Moreover, the vector pa->dummypoint //
// is the normal of the plane. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::calculateabovepoint4(point pa, point pb, point pc, point pd)
{
arraypool *ptarray;
point *parypt;
ptarray = new arraypool(sizeof(point), 4);
ptarray->newindex((void **) &parypt);
*parypt = pa;
ptarray->newindex((void **) &parypt);
*parypt = pb;
ptarray->newindex((void **) &parypt);
*parypt = pc;
ptarray->newindex((void **) &parypt);
*parypt = pd;
calculateabovepoint(ptarray, NULL, NULL, NULL);
delete ptarray;
}
///////////////////////////////////////////////////////////////////////////////
// //
// flipshpush() Push a facet edge into flip stack. //
// //
///////////////////////////////////////////////////////////////////////////////
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;
}
///////////////////////////////////////////////////////////////////////////////
// //
// flip22() Remove an edge by transforming 2-to-2 subfaces. //
// //
// 'flipfaces' contains two faces: abc and bad. This routine removes these 2 //
// faces and replaces them by two new faces: cdb and dca. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::flip22(face* flipfaces, int flipflag, int chkencflag)
{
face bdedges[4], outfaces[4], infaces[4], bdsegs[4];
face checkface, checkseg;
point pa, pb, pc, pd;
badface *bface;
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 (b->verbose > 3) {
printf(" flip 2-to-2: (%d, %d, %d, %d)\n", pointmark(pa),
pointmark(pb), pointmark(pc), pointmark(pd));
}
flip22count++;
// Collect the four boundary edges.
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) {
sspivot(bdedges[i], checkseg);
if (checkseg.sh != NULL) {
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 abc -> cdb.
setshvertices(flipfaces[0], pc, pd, pb);
// Transform bad -> dca.
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.
if (!smarktest2ed(bdsegs[(3 + i) % 4])) {
bface = (badface *) badsubsegs->alloc();
bface->ss = bdsegs[(3 + i) % 4];
smarktest2(bface->ss); // Only queue it once.
bface->forg = sorg(bface->ss); // An alive badface.
}
}
} else {
ssdissolve(bdedges[i]);
}
}
if (chkencflag & 2) {
// Queue the flipped subfaces for quality/encroaching checks.
for (i = 0; i < 2; i++) {
if (!smarktest2ed(flipfaces[i])) {
bface = (badface *) badsubfacs->alloc();
bface->ss = flipfaces[i];
smarktest2(bface->ss); // Only queue it once.
bface->forg = sorg(bface->ss); // An alive badface.
}
}
}
recentsh = flipfaces[0];
if (flipflag) {
// Put the boundary edges into flip stack.
for (i = 0; i < 4; i++) {
flipshpush(&(bdedges[i]));
}
}
}
///////////////////////////////////////////////////////////////////////////////
// //
// flip31() Remove a vertex by transforming 3-to-1 subfaces. //
// //
// 'flipfaces' is an array of subfaces. Its length is at least 4. On input, //
// the first three faces are: [p,a,b], [p,b,c], and [p,c,a]. This routine //
// replaces them by one face [a,b,c], it is returned in flipfaces[3]. //
// //
// NOTE: The three old subfaces are not deleted within this routine. They //
// still hold pointers to their adjacent subfaces. These informations are //
// needed by the routine 'sremovevertex()' for recovering a segment. //
// The caller of this routine must delete the old subfaces after their uses. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::flip31(face* flipfaces, int flipflag)
{
face bdedges[3], outfaces[3], infaces[3], bdsegs[3];
face checkface, checkseg;
point pa, pb, pc, delpt;
REAL area;
int i;
delpt = sorg(flipfaces[0]);
pa = sdest(flipfaces[0]);
pb = sdest(flipfaces[1]);
pc = sdest(flipfaces[2]);
if (b->verbose > 3) {
printf(" flip 3-to-1: (%d, %d, %d) - %d)\n", pointmark(pa),
pointmark(pb), pointmark(pc), pointmark(delpt));
}
// flip31count++;
// Collect all infos at the three boundary edges.
for (i = 0; i < 3; i++) {
senext(flipfaces[i], bdedges[i]);
spivot(bdedges[i], outfaces[i]);
infaces[i] = outfaces[i];
sspivot(bdedges[i], bdsegs[i]);
if (outfaces[i].sh != NULL) {
sspivot(bdedges[i], checkseg);
if (checkseg.sh != NULL) {
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], area);
}
// 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.
for (i = 0; i < 3; i++) {
flipshpush(&(bdedges[i]));
}
}
}
///////////////////////////////////////////////////////////////////////////////
// //
// lawsonflip() Flip non-locally Delaunay edges. //
// //
///////////////////////////////////////////////////////////////////////////////
long tetgenmesh::lawsonflip()
{
badface *popface;
face flipfaces[2];
face checkseg;
point pa, pb, pc, pd;
REAL sign;
long flipcount;
if (b->verbose > 2) {
printf(" Lawson flip %ld edges.\n", flippool->items);
}
flipcount = flip22count;
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.
sspivot(flipfaces[0], checkseg);
if (checkseg.sh != NULL) 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]);
sign = incircle3d(pa, pb, pc, pd);
if (sign < 0) {
// It is non-locally Delaunay. Flip it.
flip22(flipfaces, 1, 0);
}
}
if (b->verbose > 2) {
printf(" %ld edges stacked, %ld flips.\n", flippool->items,
flip22count - flipcount);
}
assert(flippool->items == 0l); // SELF_CHECK
return flip22count - flipcount;
}
///////////////////////////////////////////////////////////////////////////////
// //
// sinsertvertex() Insert a vertex into a triangulation of a facet. //
// //
// This function uses three global arrays: 'caveshlist', 'caveshbdlist', and //
// 'caveshseglist'. On return, 'caveshlist' contains old subfaces in C(p), //
// 'caveshbdlist' contains new subfaces in C(p). If the new point lies on a //
// segment, 'cavesegshlist' returns the two new subsegments. //
// //
// NOTE: the old subfaces in C(p) are not deleted. Theyare needed in case we //
// want to remove the new point immedately. //
// //
///////////////////////////////////////////////////////////////////////////////
int tetgenmesh::sinsertvertex(point insertpt, face *searchsh, face *splitseg,
int iloc, int bowywat)
{
triface adjtet;
face cavesh, neighsh, *parysh;
face newsh, casout, casin;
face aseg, bseg, aoutseg, boutseg;
face checkseg;
point pa, pb, pc;
enum locateresult loc;
REAL sign, ori, area;
int i, j;
if (b->verbose > 2) {
printf(" Insert facet point %d.\n", pointmark(insertpt));
}
if (splitseg != NULL) {
// A segment is going to be split, no point location.
spivot(*splitseg, *searchsh);
loc = ONEDGE;
} else {
loc = (enum locateresult) iloc;
if (loc == OUTSIDE) {
// Do point location in surface mesh.
if (searchsh->sh == NULL) {
*searchsh = recentsh;
}
// Start searching from 'searchsh'.
loc = slocate(insertpt, searchsh, 1, 1, 0);
}
}
if (b->verbose > 2) {
if (searchsh->sh != NULL) {
pa = sorg(*searchsh);
pb = sdest(*searchsh);
pc = sapex(*searchsh);
printf(" Located subface (%d, %d, %d).\n", pointmark(pa),
pointmark(pb), pointmark(pc));
} else {
assert(splitseg != NULL);
pa = sorg(*splitseg);
pb = sdest(*splitseg);
printf(" Located segment (%d, %d).\n", pointmark(pa),pointmark(pb));
}
}
if (bowywat < 3) { // if (bowywat == 1 || bowywat == 2) {
// Form the initial sC(p).
if (loc == ONFACE) {
if (b->verbose > 2) {
printf(" Inside face.\n");
}
// Add the face into list (in B-W cavity).
smarktest(*searchsh);
caveshlist->newindex((void **) &parysh);
*parysh = *searchsh;
} else if (loc == ONEDGE) {
if (b->verbose > 2) {
printf(" On edge.\n");
}
if (splitseg != 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);
*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) {
if (b->verbose > 2) {
printf(" On vertex.\n");
}
return (int) loc;
} else if (loc == OUTSIDE) {
// Comment: This should only happen during the surface meshing step.
// Enlarge the convex hull of the triangulation by including p.
// An above point of the facet is set in 'dummypoint' to replace
// orient2d tests by orient3d tests.
if (b->verbose > 2) {
printf(" Outside face.\n");
}
// Imagine that the current edge a->b (in 'searchsh') is horizontal in a
// plane, and a->b is directed from left to right, p lies above a->b.
// Find the right-most edge of the triangulation which is visible by p.
neighsh = *searchsh;
while (1) {
senext2self(neighsh);
spivot(neighsh, casout);
if (casout.sh == NULL) {
// A convex hull edge. Is it visible by p.
pa = sorg(neighsh);
pb = sdest(neighsh);
ori = orient3d(pa, pb, dummypoint, insertpt);
if (ori < 0) {
*searchsh = neighsh; // Visible, update 'searchsh'.
} else {
break; // 'searchsh' is the right-most visible edge.
}
} else {
if (sorg(casout) != sdest(neighsh)) sesymself(casout);
neighsh = casout;
}
}
// 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, area);
}
// 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 {
// Under this case, the sub-cavity sC(p) has already been formed in
// insertvertex(). Check it.
// FOR DEBUG ONLY.
for (i = 0; i < caveshlist->objects; i++) {
cavesh = * (face *) fastlookup(caveshlist, i);
assert(smarktested(cavesh));
}
if (splitseg != NULL) {
assert(smarktested(*splitseg));
}
}// if (bowywat < 3)
// Form the Bowyer-Watson cavity sC(p).
for (i = 0; i < caveshlist->objects; i++) {
cavesh = * (face *) fastlookup(caveshlist, i);
for (j = 0; j < 3; j++) {
sspivot(cavesh, checkseg);
if (checkseg.sh == NULL) {
spivot(cavesh, neighsh);
if (neighsh.sh != NULL) {
// The adjacent face exists.
if (!smarktested(neighsh)) {
if (bowywat) {
if (bowywat > 2) {
// It must be a boundary edge.
sign = 1;
} else {
// Check if this subface is connected to adjacent tet(s).
stpivot(neighsh, adjtet);
if (adjtet.tet == NULL) {
// Check if the subface is non-Delaunay wrt. the new pt.
pa = sorg(neighsh);
pb = sdest(neighsh);
pc = sapex(neighsh);
sign = incircle3d(pa, pb, pc, insertpt);
} else {
// It is connected to an adjacent tet. A boundary edge.
sign = 1;
}
}
if (sign < 0) {
// Add the adjacent face in list (in B-W cavity).
smarktest(neighsh);
caveshlist->newindex((void **) &parysh);
*parysh = neighsh;
}
} else {
sign = 1; // A boundary edge.
}
} else {
sign = -1; // Not a boundary edge.
}
} else {
// No adjacent face. It is a hull edge.
if (loc == OUTSIDE) {
// It is a boundary edge if it does not contain p.
if ((sorg(cavesh) == insertpt) || (sdest(cavesh) == insertpt)) {
sign = -1; // Not a boundary edge.
} else {
sign = 1; // A boundary edge.
}
} else {
sign = 1; // A boundary edge.
}
}
} else {
// Do not across a segment. It is a boundary edge.
sign = 1;
}
if (sign >= 0) {
// Add a boundary edge.
caveshbdlist->newindex((void **) &parysh);
*parysh = cavesh;
}
senextself(cavesh);
} // j
} // i
if (b->verbose > 3) {
printf(" Size of cavity: %ld faces, %ld bdry edges.\n",
caveshlist->objects, caveshbdlist->objects);
}
// Creating new subfaces.
for (i = 0; i < caveshbdlist->objects; i++) {
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));
setshelltype(newsh, shelltype(*parysh));
if (checkconstraints) {
area = areabound(*parysh);
setareabound(newsh, area);
}
// Update the point-to-subface map.
if (pointtype(pa) == FREEFACETVERTEX) {
setpoint2sh(pa, sencode(newsh));
}
if (pointtype(pb) == FREEFACETVERTEX) {
setpoint2sh(pb, sencode(newsh));
}
// Connect newsh to outer subfaces.
spivot(*parysh, casout);
if (casout.sh != NULL) {
casin = casout;
if (checkseg.sh != NULL) {
// Make sure that newsh has the right ori at this segment.
checkseg.shver = 0;
if (sorg(newsh) != sorg(checkseg)) {
sesymself(newsh);
sesymself(*parysh); // This side should also be inversed.
}
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);
}
// 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);
assert(sorg(neighsh) == pb); // SELF_CHECK
assert(sapex(neighsh) == insertpt); // SELF_CHECK
senext2self(neighsh); // Go to the open edge [p, b].
sbond(newsh, neighsh);
} else {
// There is no adjacent new face at this side.
assert(loc == OUTSIDE); // SELF_CHECK
}
}
spivot(*parysh, newsh); // The new subface [a, b, p].
senext2self(newsh); // At edge [p, a].
spivot(newsh, neighsh);
if (neighsh.sh == NULL) {
// Find the adjacent new subface at edge [p, a].
pa = sorg(*parysh);
neighsh = *parysh;
while (1) {
senext2self(neighsh);
spivotself(neighsh);
if (neighsh.sh == NULL) break;
if (!smarktested(neighsh)) break;
if (sorg(neighsh) != pa) sesymself(neighsh);
}
if (neighsh.sh != NULL) {
// Now 'neighsh' is a new subface at edge [#, a].
if (sdest(neighsh) != pa) sesymself(neighsh);
assert(sdest(neighsh) == pa); // SELF_CHECK
assert(sapex(neighsh) == insertpt); // SELF_CHECK
senextself(neighsh); // Go to the open edge [a, p].
sbond(newsh, neighsh);
} else {
// There is no adjacent new face at this side.
assert(loc == OUTSIDE); // SELF_CHECK
}
}
}
if (loc == ONEDGE) {
// An edge is being split. We distinguish two cases:
// (1) the edge is not on the boundary of the cavity;
// (2) the edge is on the boundary of the cavity.
// In case (2), the edge is either a segment or a hull edge. There are
// degenerated new faces in the cavity. They must be removed.
for (i = 0; i < cavesegshlist->objects; i++) {
// Get the saved old subface.
parysh = (face *) fastlookup(cavesegshlist, i);
// Get a possible new degenerated subface.
spivot(*parysh, cavesh);
if (sapex(cavesh) == insertpt) {
// Found a degenerated new subface, i.e., case (2).
if (cavesegshlist->objects > 1) {
// There are more than one subface share at this edge.
j = (i + 1) % (int) cavesegshlist->objects;
parysh = (face *) fastlookup(cavesegshlist, j);
spivot(*parysh, neighsh);
// Adjust cavesh and neighsh both at edge a->b, and has p as apex.
if (sorg(neighsh) != sorg(cavesh)) {
sesymself(neighsh);
assert(sorg(neighsh) == sorg(cavesh)); // SELF_CHECK
}
assert(sapex(neighsh) == insertpt); // SELF_CHECK
// Connect adjacent faces at two other edges of cavesh and neighsh.
// As a result, the two degenrated new faces are squessed 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]. Squeese 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) {
if (bowywat < 3) {
smarktest(*splitseg); // Mark it as being processed.
}
aseg = *splitseg;
pa = sorg(*splitseg);
pb = sdest(*splitseg);
if (b->verbose > 2) {
printf(" Split seg (%d, %d) by %d.\n", pointmark(pa),
pointmark(pb), pointmark(insertpt));
}
// Insert the new point p.
makeshellface(subsegs, &aseg);
makeshellface(subsegs, &bseg);
setshvertices(aseg, pa, insertpt, NULL);
setshvertices(bseg, insertpt, pb, NULL);
setshellmark(aseg, shellmark(*splitseg));
setshellmark(bseg, shellmark(*splitseg));
setshelltype(aseg, shelltype(*splitseg));
setshelltype(bseg, shelltype(*splitseg));
if (checkconstraints) {
setareabound(bseg, areabound(*splitseg));
setareabound(bseg, areabound(*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 squeesed. 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.
setpoint2sh(insertpt, sencode(aseg));
// Update the point-to-seg map.
setpoint2sh(pa, sencode(aseg));
setpoint2sh(pb, sencode(bseg));
} // if (splitseg != 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) {
// 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. //
// //
// If 'lawson' > 0, the Lawson flip algorithm is used to recover Delaunay- //
// ness after p is removed. //
// //
// Within each facet, we first use a sequence of 2-to-2 flips to flip any //
// edge at p, finally use a 3-to-1 flip to remove p. //
// //
// All new created subfaces are returned in the global array 'caveshbdlist'. //
// The new segment (when p is on segment) is returned in 'parentseg'. //
// //
///////////////////////////////////////////////////////////////////////////////
int tetgenmesh::sremovevertex(point delpt, face* parentsh, face* parentseg,
int lawson)
{
face flipfaces[4], *parysh;
face spinsh, startsh, neighsh, nextsh, fakesh;
face abseg, prevseg, checkseg;
face adjseg1, adjseg2;
point pa, pb, pc, pd;
int it, i, j;
REAL *norm, n1[3], n2[3];
REAL len, len1, len2;
REAL ori1, ori2;
if (parentseg != NULL) {
assert(sorg(*parentseg) == delpt);
assert(parentseg->shver == 0);
// 'delpt' (p) should be a Steiner point inserted in a segment [a,b],
// where 'parentseg' should be [p,b]. Find the segment [a,p].
senext2(*parentseg, prevseg);
spivotself(prevseg);
assert(prevseg.sh != NULL);
prevseg.shver = 0;
assert(sdest(prevseg) == delpt);
// Restore the original segment [a,b].
pa = sorg(prevseg);
pb = sdest(*parentseg);
if (b->verbose > 2) {
printf(" Remove vertex %d from segment [%d, %d].\n",
pointmark(delpt), pointmark(pa), pointmark(pb));
}
makeshellface(subsegs, &abseg);
setshvertices(abseg, pa, pb, NULL);
setshellmark(abseg, shellmark(*parentseg));
setshelltype(abseg, shelltype(*parentseg));
if (checkconstraints) {
setareabound(abseg, areabound(*parentseg));
}
// Connect [#, a]<->[a, b].
senext2(prevseg, adjseg1);
spivotself(adjseg1);
if (adjseg1.sh != NULL) {
adjseg1.shver = 0;
assert(sdest(adjseg1) == pa);
senextself(adjseg1);
senext2(abseg, adjseg2);
sbond(adjseg1, adjseg2);
}
// Connect [a, b]<->[b, #].
senext(*parentseg, adjseg1);
spivotself(adjseg1);
if (adjseg1.sh != NULL) {
adjseg1.shver = 0;
assert(sorg(adjseg1) == pb);
senext2self(adjseg1);
senext(abseg, adjseg2);
sbond(adjseg1, adjseg2);
}
// Update the point-to-segment map.
setpoint2sh(pa, sencode(abseg));
setpoint2sh(pb, sencode(abseg));
// Get the faces in face ring at segment [p, b].
// Re-use array 'caveshlist'.
spivot(*parentseg, *parentsh);
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;
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);
assert(sorg(startsh) == delpt);
}
// startsh is [p, b, #1], find the subface [a, p, #2].
neighsh = startsh;
while (1) {
senext2self(neighsh);
sspivot(neighsh, checkseg);
if (checkseg.sh != NULL) {
// It must be the segment [a, p].
assert(checkseg.sh == prevseg.sh);
break;
}
spivotself(neighsh);
assert(neighsh.sh != NULL);
if (sorg(neighsh) != delpt) sesymself(neighsh);
}
// Now neighsh is [a, p, #2].
if (neighsh.sh != startsh.sh) {
// Detach the two subsegments [a,p] and [p,b] from subfaces.
ssdissolve(startsh);
ssdissolve(neighsh);
// Create a degenerated subface [a,b,p]. It is used to: (1) hold the
// new segment [a,b]; (2) connect to the two adjacent subfaces
// [p,b,#] and [a,p,#].
makeshellface(subfaces, &fakesh);
setshvertices(fakesh, pa, pb, delpt);
setshellmark(fakesh, shellmark(startsh));
// Connect fakesh to the segment [a,b].
ssbond(fakesh, abseg);
// Connect fakesh to adjacent subfaces: [p,b,#1] and [a,p,#2].
senext(fakesh, nextsh);
sbond(nextsh, startsh);
senext2(fakesh, nextsh);
sbond(nextsh, neighsh);
smarktest(fakesh); // Mark it as faked.
} else {
// Special case. There exists already a degenerated face [a,b,p]!
// There is no need to create a faked subface here.
senext2self(neighsh); // [a,b,p]
assert(sapex(neighsh) == delpt);
// Since we will re-connect the face ring using the faked subfaces.
// We put the adjacent face of [a,b,p] to the list.
spivot(neighsh, startsh); // The original adjacent subface.
if (sorg(startsh) != pa) {
sesymself(startsh);
}
assert(sorg(startsh) == pa);
assert(sdest(startsh) == pb);
assert(sapex(startsh) != delpt);
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));
}
assert(sorg(*parentsh) == delpt);
// Let 'delpt' be its apex.
senextself(*parentsh);
// For unifying the code, we add parentsh to list.
cavesegshlist->newindex((void **) &parysh);
*parysh = *parentsh;
}
// Remove the point (p).
for (it = 0; it < cavesegshlist->objects; it++) {
parentsh = (face *) fastlookup(cavesegshlist, it); // [a,b,p]
senextself(*parentsh); // [b,p,a].
spivotself(*parentsh);
if (sorg(*parentsh) != delpt) {
sesymself(*parentsh);
}
// now parentsh is [p,b,#].
if (sorg(*parentsh) != delpt) {
// The vertex has already been removed in above special case.
assert(!smarktested(*parentsh));
continue;
}
while (1) {
// Initialize the flip edge list. Re-use 'caveshlist'.
spinsh = *parentsh; // [p, b, #]
while (1) {
caveshlist->newindex((void **) &parysh);
*parysh = spinsh;
senext2self(spinsh);
spivotself(spinsh);
assert(spinsh.sh != NULL);
if (spinsh.sh == parentsh->sh) break;
if (sorg(spinsh) != delpt) {
sesymself(spinsh);
assert(sorg(spinsh) == delpt);
}
} // while (1)
if (caveshlist->objects == 3) {
// Delete the point by a 3-to-1 flip.
for (i = 0; i < 3; i++) {
parysh = (face *) fastlookup(caveshlist, i);
flipfaces[i] = *parysh;
}
flip31(flipfaces, lawson);
for (i = 0; i < 3; i++) {
shellfacedealloc(subfaces, flipfaces[i].sh);
}
caveshlist->restart();
// Save the new subface.
caveshbdlist->newindex((void **) &parysh);
*parysh = flipfaces[3];
// The vertex is removed.
break;
} else {
// There should be more than 3 subfaces in list.
assert(caveshlist->objects > 3);
}
// 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]);
// Select a base.
facenormal(pa, pb, pc, n1, 1, NULL);
len1 = sqrt(DOT(n1, n1));
facenormal(pa, pb, pd, n2, 1, NULL);
len2 = sqrt(DOT(n2, n2));
if (len1 > len2) {
norm = n1;
len = len1;
} else {
norm = n2;
len = len2;
}
assert(len > 0);
norm[0] /= len;
norm[1] /= len;
norm[2] /= len;
len = DIST(pa, pb);
dummypoint[0] = pa[0] + len * norm[0];
dummypoint[1] = pa[1] + len * norm[1];
dummypoint[2] = pa[2] + len * norm[2];
// Check if a 2-to-2 flip is possible.
ori1 = orient3d(pc, pd, dummypoint, pa);
ori2 = orient3d(pc, pd, dummypoint, pb);
if (ori1 * ori2 < 0) {
// A 2-to-2 flip is found.
flip22(flipfaces, lawson, 0);
// The i-th edge is flipped. The i-th and (i-1)-th subfaces are
// changed. The 'flipfaces[1]' contains p as its apex.
senext2(flipfaces[1], *parentsh);
// Save the new subface.
caveshbdlist->newindex((void **) &parysh);
*parysh = flipfaces[0];
break;
}
} //
} // i
if (i == caveshlist->objects) {
// This can happen only if there are 4 edges at p, and they are
// orthogonal to each other, see Fig. 2010-11-01.
assert(caveshlist->objects == 4);
// Do a flip22 and a flip31 to remove p.
parysh = (face *) fastlookup(caveshlist, 0);
flipfaces[0] = *parysh;
spivot(flipfaces[0], flipfaces[1]);
if (sorg(flipfaces[0]) != sdest(flipfaces[1])) {
sesymself(flipfaces[1]);
}
flip22(flipfaces, lawson, 0);
senext2(flipfaces[1], *parentsh);
// Save the new subface.
caveshbdlist->newindex((void **) &parysh);
*parysh = flipfaces[0];
}
// The edge list at p are changed.
caveshlist->restart();
} // while (1)
} // it
cavesegshlist->restart();
if (b->verbose > 2) {
printf(" Created %ld new subfaces.\n", caveshbdlist->objects);
}
if (lawson) {
lawsonflip();
}
return 0;
}
///////////////////////////////////////////////////////////////////////////////
// //
// slocate() Locate a point in a surface triangulation. //
// //
// Staring the search from 'searchsh'(it should not be NULL). Perform a line //
// walk search for a subface containing the point (p). //
// //
// If 'aflag' is set, the 'dummypoint' is pre-calculated so that it lies //
// above the 'searchsh' in its current orientation. The test if c is CCW to //
// the line a->b can be done by the test if c is below the oriented plane //
// a->b->dummypoint. //
// //
// If 'cflag' is not TRUE, the triangulation may not be convex. Stop search //
// when a segment is met and return OUTSIDE. //
// //
// If 'rflag' (rounding) is set, after the location of the point is found, //
// either ONEDGE or ONFACE, round the result using an epsilon. //
// //
// The returned value inducates the following cases: //
// - ONVERTEX, p is the origin of 'searchsh'. //
// - ONEDGE, p lies on the edge of 'searchsh'. //
// - ONFACE, p lies in the interior of 'searchsh'. //
// - OUTSIDE, p lies outside of the triangulation, p is on the left-hand //
// side of the edge 'searchsh'(s), i.e., org(s), dest(s), p are CW. //
// //
///////////////////////////////////////////////////////////////////////////////
enum tetgenmesh::locateresult tetgenmesh::slocate(point searchpt,
face* searchsh, int aflag, int cflag, int rflag)
{
face neighsh;
face checkseg;
point pa, pb, pc, pd, *parypt;
enum locateresult loc;
enum {MOVE_BC, MOVE_CA} nextmove;
REAL ori, ori_bc, ori_ca;
REAL dist_bc, dist_ca;
int i;
// For finding an approximate location.
//REAL n[3], len, len3;
REAL n[3], area_abc, area_abp, area_bcp, area_cap;
pa = sorg(*searchsh);
pb = sdest(*searchsh);
pc = sapex(*searchsh);
if (!aflag) {
// No above point is given. Calculate an above point for this facet.
// Re-use the 'cavetetvertlist'.
cavetetvertlist->newindex((void **) &parypt);
*parypt = pa;
cavetetvertlist->newindex((void **) &parypt);
*parypt = pb;
cavetetvertlist->newindex((void **) &parypt);
*parypt = pc;
cavetetvertlist->newindex((void **) &parypt);
*parypt = searchpt;
calculateabovepoint(cavetetvertlist, NULL, NULL, NULL);
cavetetvertlist->restart();
}
// 'dummypoint' is given. Make sure it is above [a,b,c]
ori = orient3d(pa, pb, pc, dummypoint);
assert(ori != 0); // SELF_CHECK
if (ori > 0) {
sesymself(*searchsh); // Reverse the face orientation.
}
// Find an edge of the face s.t. p lies on its right-hand side (CCW).
for (i = 0; i < 3; i++) {
pa = sorg(*searchsh);
pb = sdest(*searchsh);
ori = orient3d(pa, pb, dummypoint, searchpt);
if (ori > 0) break;
senextself(*searchsh);
}
assert(i < 3); // SELF_CHECK
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.
senext(*searchsh, neighsh); // At edge [b, c].
spivotself(neighsh);
if (neighsh.sh != NULL) {
pd = sapex(neighsh);
dist_bc = NORM2(searchpt[0] - pd[0], searchpt[1] - pd[1],
searchpt[2] - pd[2]);
} else {
dist_bc = NORM2(xmax - xmin, ymax - ymin, zmax - zmin);
}
senext2(*searchsh, neighsh); // At edge [c, a].
spivotself(neighsh);
if (neighsh.sh != NULL) {
pd = sapex(neighsh);
dist_ca = NORM2(searchpt[0] - pd[0], searchpt[1] - pd[1],
searchpt[2] - pd[2]);
} else {
dist_ca = dist_bc;
}
if (dist_ca < dist_bc) {
nextmove = MOVE_CA;
} else {
nextmove = MOVE_BC;
}
} 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;
}
}
}
}
// 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.
sspivot(*searchsh, checkseg);
if (checkseg.sh != NULL) {
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);
}
assert(sorg(neighsh) == sdest(*searchsh)); // SELF_CHECK
// Update the newly discovered face and its endpoints.
*searchsh = neighsh;
pa = sorg(*searchsh);
pb = sdest(*searchsh);
pc = sapex(*searchsh);
if (pc == searchpt) {
senext2self(*searchsh);
return ONVERTEX;
}
} // while (1)
// assert(loc == ONFACE || loc == ONEDGE);
if (rflag) {
// Round the locate result before return.
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) {
assert(area_cap != 0);
senextself(*searchsh);
loc = ONVERTEX; // p is close to b.
} else {
if (area_cap == 0) {
loc = ONVERTEX; // p is close to a.
} else {
loc = ONEDGE; // p is on edge [a,b].
}
}
} 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)
return loc;
}
///////////////////////////////////////////////////////////////////////////////
// //
// sscoutsegment() Look for a segment in surface triangulation. //
// //
// The segment is given by the origin of 'searchsh' and 'endpt'. Assume the //
// orientation of 'searchsh' is CCW w.r.t. the above point. //
// //
// If an edge in T is found matching this segment, the segment is "locaked" //
// in T at the edge. Otherwise, flip the first edge in T that the segment //
// crosses. Continue the search from the flipped face. //
// //
///////////////////////////////////////////////////////////////////////////////
enum tetgenmesh::interresult
tetgenmesh::sscoutsegment(face *searchsh, point endpt)
{
face flipshs[2], neighsh;
face newseg, checkseg;
point startpt, pa, pb, pc, pd;
enum interresult dir;
enum {MOVE_AB, MOVE_CA} nextmove;
REAL ori_ab, ori_ca;
REAL dist_b, dist_c;
int shmark = 0;
// The origin of 'searchsh' is fixed.
startpt = sorg(*searchsh); // pa = startpt;
nextmove = MOVE_AB; // Avoid compiler warning.
if (b->verbose > 2) {
printf(" Scout segment (%d, %d).\n", pointmark(startpt),
pointmark(endpt));
}
// Search an edge in 'searchsh' on the path of this segment.
while (1) {
pb = sdest(*searchsh);
if (pb == endpt) {
dir = SHAREEDGE; // Found!
break;
}
pc = sapex(*searchsh);
if (pc == endpt) {
senext2self(*searchsh);
sesymself(*searchsh);
dir = SHAREEDGE; // Found!
break;
}
ori_ab = orient3d(startpt, pb, dummypoint, endpt);
ori_ca = orient3d(pc, startpt, dummypoint, endpt);
if (ori_ab < 0) {
if (ori_ca < 0) { // (--)
// Both sides are viable moves.
spivot(*searchsh, neighsh); // At edge [a, b].
assert(neighsh.sh != NULL); // SELF_CHECK
pd = sapex(neighsh);
dist_b = NORM2(endpt[0] - pd[0], endpt[1] - pd[1], endpt[2] - pd[2]);
senext2(*searchsh, neighsh); // At edge [c, a].
spivotself(neighsh);
assert(neighsh.sh != NULL); // SELF_CHECK
pd = sapex(neighsh);
dist_c = NORM2(endpt[0] - pd[0], endpt[1] - pd[1], endpt[2] - pd[2]);
if (dist_c < dist_b) {
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 collinear with edge [a, b].
dir = ACROSSVERT;
break;
} else { // (00)
// startpt == endpt. Not possible.
assert(0); // SELF_CHECK
}
}
}
}
// Move 'searchsh' to the next face, keep the origin unchanged.
if (nextmove == MOVE_AB) {
spivot(*searchsh, neighsh);
if (sorg(neighsh) != pb) sesymself(neighsh);
senext(neighsh, *searchsh);
} else {
senext2(*searchsh, neighsh);
spivotself(neighsh);
if (sdest(neighsh) != pc) sesymself(neighsh);
*searchsh = neighsh;
}
assert(sorg(*searchsh) == startpt); // SELF_CHECK
} // while
if (dir == SHAREEDGE) {
// Insert the segment into the triangulation.
makeshellface(subsegs, &newseg);
setshvertices(newseg, startpt, endpt, NULL);
// Set the actual segment marker.
if (in->facetmarkerlist != NULL) {
shmark = shellmark(*searchsh);
setshellmark(newseg, in->facetmarkerlist[shmark - 1]);
}
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.
return dir;
}
if (dir == ACROSSEDGE) {
// Edge [b, c] intersects with the segment.
senext(*searchsh, flipshs[0]);
sspivot(flipshs[0], checkseg);
if (checkseg.sh != NULL) {
printf("Error: Invalid PLC.\n");
pb = sorg(flipshs[0]);
pc = sdest(flipshs[0]);
printf(" Two segments (%d, %d) and (%d, %d) intersect.\n",
pointmark(startpt), pointmark(endpt), pointmark(pb), pointmark(pc));
terminatetetgen(3);
}
// Flip edge [b, c], queue unflipped edges (for Delaunay checks).
spivot(flipshs[0], flipshs[1]);
assert(flipshs[1].sh != NULL); // SELF_CHECK
if (sorg(flipshs[1]) != sdest(flipshs[0])) sesymself(flipshs[1]);
flip22(flipshs, 1, 0);
// The flip may create an invered triangle, check it.
pa = sapex(flipshs[1]);
pb = sapex(flipshs[0]);
pc = sorg(flipshs[0]);
pd = sdest(flipshs[0]);
// Check if pa and pb are on the different sides of [pc, pd].
// Re-use ori_ab, ori_ca for the tests.
ori_ab = orient3d(pc, pd, dummypoint, pb);
ori_ca = orient3d(pd, pc, dummypoint, pa);
//assert(ori_ab * ori_ca != 0); // SELF_CHECK
if (ori_ab < 0) {
if (b->verbose > 2) {
printf(" Queue an inversed triangle (%d, %d, %d) %d\n",
pointmark(pc), pointmark(pd), pointmark(pb), pointmark(pa));
}
flipshpush(&(flipshs[0])); // push it to 'flipstack'
} else if (ori_ca < 0) {
if (b->verbose > 2) {
printf(" Queue an inversed triangle (%d, %d, %d) %d\n",
pointmark(pd), pointmark(pc), pointmark(pa), pointmark(pb));
}
flipshpush(&(flipshs[1])); // // push it to 'flipstack'
}
// Set 'searchsh' s.t. its origin is 'startpt'.
*searchsh = flipshs[0];
assert(sorg(*searchsh) == startpt);
}
return sscoutsegment(searchsh, endpt);
}
///////////////////////////////////////////////////////////////////////////////
// //
// scarveholes() Remove triangles not in the facet. //
// //
// This routine re-uses the two global arrays: caveshlist and caveshbdlist. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::scarveholes(int holes, REAL* holelist)
{
face *parysh, searchsh, neighsh;
face checkseg;
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.
sspivot(searchsh, checkseg);
if (checkseg.sh == NULL) {
// 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);
*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) {
sspivot(searchsh, checkseg);
if (checkseg.sh == NULL) {
if (!sinfected(neighsh)) {
sinfect(neighsh);
caveshbdlist->newindex((void **) &parysh);
*parysh = neighsh;
}
} else {
sdissolve(neighsh); // Disconnect a protected face.
}
}
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();
}
///////////////////////////////////////////////////////////////////////////////
// //
// triangulate() Create a CDT for the facet. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::triangulate(int shmark, arraypool* ptlist, arraypool* conlist,
int holes, REAL* holelist)
{
face searchsh, newsh, *parysh;
face newseg;
point pa, pb, pc, *ppt, *cons;
enum locateresult loc;
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;
} if (ptlist->objects == 2l) {
pa = * (point *) fastlookup(ptlist, 0);
pb = * (point *) fastlookup(ptlist, 1);
if (distance(pa, pb) > 0) {
// It is a single segment.
makeshellface(subsegs, &newseg);
setshvertices(newseg, pa, pb, NULL);
// Set the actual segment marker.
if (in->facetmarkerlist != NULL) {
setshellmark(newseg, in->facetmarkerlist[shmark - 1]);
}
}
if (pointtype(pa) == VOLVERTEX) {
setpointtype(pa, RIDGEVERTEX);
}
if (pointtype(pb) == VOLVERTEX) {
setpointtype(pb, RIDGEVERTEX);
}
return;
} if (ptlist->objects == 3l) {
// The facet has only one triangle.
pa = * (point *) fastlookup(ptlist, 0);
pb = * (point *) fastlookup(ptlist, 1);
pc = * (point *) fastlookup(ptlist, 2);
if (triarea(pa, pb, pc) > 0) {
makeshellface(subfaces, &newsh);
setshvertices(newsh, pa, pb, pc);
setshellmark(newsh, shmark);
// Create three new segments.
for (i = 0; i < 3; i++) {
makeshellface(subsegs, &newseg);
setshvertices(newseg, sorg(newsh), sdest(newsh), NULL);
// Set the actual segment marker.
if (in->facetmarkerlist != NULL) {
setshellmark(newseg, in->facetmarkerlist[shmark - 1]);
}
ssbond(newsh, newseg);
senextself(newsh);
}
if (pointtype(pa) == VOLVERTEX) {
setpointtype(pa, FACETVERTEX);
}
if (pointtype(pb) == VOLVERTEX) {
setpointtype(pb, FACETVERTEX);
}
if (pointtype(pc) == VOLVERTEX) {
setpointtype(pc, FACETVERTEX);
}
}
return;
}
// Calulcate an above point of this facet.
if (!calculateabovepoint(ptlist, &pa, &pb, &pc)) {
return; // 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);
}
// 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;
if (b->verbose > 2) printf(" # %d", i);
loc = (enum locateresult) sinsertvertex(*ppt, &searchsh, NULL, iloc, 1);
assert(loc != ONVERTEX); // SELF_CHECK
if (pointtype(*ppt) == VOLVERTEX) {
setpointtype(*ppt, FACETVERTEX);
}
// Delete all removed subfaces.
for (j = 0; j < caveshlist->objects; j++) {
parysh = (face *) fastlookup(caveshlist, j);
shellfacedealloc(subfaces, parysh->sh);
}
// Clear the global lists.
caveshbdlist->restart();
caveshlist->restart();
cavesegshlist->restart();
} else {
puninfect(*ppt); // This point has inserted.
}
}
// Insert the segments.
for (i = 0; i < conlist->objects; i++) {
cons = (point *) fastlookup(conlist, i);
searchsh = recentsh;
loc = slocate(cons[0], &searchsh, 1, 1, 0);
assert(loc == ONVERTEX); // SELF_CHECK
// Recover the segment. Some edges may be flipped.
sscoutsegment(&searchsh, cons[1]);
if (flipstack != NULL) {
// Recover locally Delaunay edges.
lawsonflip();
}
}
// Remove exterior and hole triangles.
scarveholes(holes, holelist);
}
///////////////////////////////////////////////////////////////////////////////
// //
// unifysubfaces() Unify two identical subfaces. //
// //
// Two subfaces, f1 [a, b, c] and f2 [a, b, d], share the same edge [a, b]. //
// If c = d, then f1 and f2 are identical. Otherwise, these two subfaces //
// intersect, and the mesher is stopped. //
// //
// If the two subfaces are indentical, we try to replace f2 by f1, i.e, all //
// neighbors of f2 are re-connected to f1. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::unifysubfaces(face *f1, face *f2)
{
face casout, casin, neighsh;
face sseg, checkseg;
point pa, pb, pc, pd;
int i;
assert(f1->sh != f2->sh); // SELF_CHECK
pa = sorg(*f1);
pb = sdest(*f1);
pc = sapex(*f1);
assert(sorg(*f2) == pa); // SELF_CHECK
assert(sdest(*f2) == pb); // SELF_CHECK
pd = sapex(*f2);
if (pc != pd) {
printf("Found two facets intersect each other.\n");
printf(" 1st: [%d, %d, %d] #%d\n",
pointmark(pa), pointmark(pb), pointmark(pc), shellmark(*f1));
printf(" 2nd: [%d, %d, %d] #%d\n",
pointmark(pa), pointmark(pb), pointmark(pd), shellmark(*f2));
terminatetetgen(3);
} 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));
terminatetetgen(3);
}
// f1 and f2 are identical, replace f2 by f1.
if (!b->quiet) {
printf("Warning: Facet #%d is duplicated with Facet #%d. Removed!\n",
shellmark(*f2), shellmark(*f1));
}
// Make possible disconnections/reconnections at neighbors of f2.
for (i = 0; i < 3; i++) {
spivot(*f1, casout);
if (casout.sh == NULL) {
// f1 has no adjacent subfaces yet.
spivot(*f2, casout);
if (casout.sh != NULL) {
// Re-direct the adjacent connections of f2 to f1.
casin = casout;
spivot(casin, neighsh);
while (neighsh.sh != f2->sh) {
casin = neighsh;
spivot(casin, neighsh);
}
// Connect casout <= f1 <= casin.
sbond1(*f1, casout);
sbond1(casin, *f1);
}
}
sspivot(*f2, sseg);
if (sseg.sh != NULL) {
// f2 has a segment. It must be different to f1's.
sspivot(*f1, checkseg); // SELF_CHECK
if (checkseg.sh != NULL) { // SELF_CHECK
assert(checkseg.sh != sseg.sh); // SELF_CHECK
}
// Disconnect bonds of subfaces to this segment.
spivot(*f2, casout);
if (casout.sh != NULL) {
casin = casout;
ssdissolve(casin);
spivot(casin, neighsh);
while (neighsh.sh != f2->sh) {
casin = neighsh;
ssdissolve(casin);
spivot(casin, neighsh);
}
}
// Delete the segment.
shellfacedealloc(subsegs, sseg.sh);
}
spivot(*f2, casout);
if (casout.sh != NULL) {
// Find the subface (casin) pointing to f2.
casin = casout;
spivot(casin, neighsh);
while (neighsh.sh != f2->sh) {
casin = neighsh;
spivot(casin, neighsh);
}
// Disconnect f2 <= casin.
sdissolve(casin);
}
senextself(*f1);
senextself(*f2);
} // i
// Delete f2.
shellfacedealloc(subfaces, f2->sh);
}
///////////////////////////////////////////////////////////////////////////////
// //
// unifysegments() Remove redundant segments and create face links. //
// //
///////////////////////////////////////////////////////////////////////////////
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];
int *idx2faclist;
int idx, k, m;
if (b->verbose > 1) {
printf(" Unifying segments.\n");
}
// 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.
assert(sorg(sface) == torg); // SELF_CHECK
if (sdest(sface) != tdest) {
senext2self(sface);
sesymself(sface);
}
if (sdest(sface) != tdest) continue;
// Save the face f in facelink.
if (flippool->items >= 2) {
f1 = facelink;
for (m = 0; m < flippool->items - 1; m++) {
f2 = f1->nextitem;
ori1 = orient3d(torg, tdest, sapex(f1->ss), sapex(f2->ss));
ori2 = orient3d(torg, tdest, sapex(f1->ss), sapex(sface));
if (ori1 > 0) {
// apex(f2) is below f1.
if (ori2 > 0) {
// apex(f) is below f1 (see Fig.1).
ori3 = orient3d(torg, tdest, sapex(f2->ss), sapex(sface));
if (ori3 > 0) {
// apex(f) is below f2, insert it.
break;
} else if (ori3 < 0) {
// apex(f) is above f2, continue.
} else { // ori3 == 0;
// f is coplanar and codirection with f2.
unifysubfaces(&(f2->ss), &sface);
break;
}
} else if (ori2 < 0) {
// apex(f) is above f1 below f2, inset it (see Fig. 2).
break;
} else { // ori2 == 0;
// apex(f) is coplanar with f1 (see Fig. 5).
ori3 = orient3d(torg, tdest, sapex(f2->ss), sapex(sface));
if (ori3 > 0) {
// apex(f) is below f2, insert it.
break;
} else {
// f is coplanar and codirection with f1.
unifysubfaces(&(f1->ss), &sface);
break;
}
}
} else if (ori1 < 0) {
// apex(f2) is above f1.
if (ori2 > 0) {
// apex(f) is below f1, continue (see Fig. 3).
} else if (ori2 < 0) {
// apex(f) is above f1 (see Fig.4).
ori3 = orient3d(torg, tdest, sapex(f2->ss), sapex(sface));
if (ori3 > 0) {
// apex(f) is below f2, insert it.
break;
} else if (ori3 < 0) {
// apex(f) is above f2, continue.
} else { // ori3 == 0;
// f is coplanar and codirection with f2.
unifysubfaces(&(f2->ss), &sface);
break;
}
} else { // ori2 == 0;
// f is coplanar and with f1 (see Fig. 6).
ori3 = orient3d(torg, tdest, sapex(f2->ss), sapex(sface));
if (ori3 > 0) {
// f is also codirection with f1.
unifysubfaces(&(f1->ss), &sface);
break;
} else {
// f is above f2, continue.
}
}
} else { // ori1 == 0;
// apex(f2) is coplanar with f1. By assumption, f1 is not
// coplanar and codirection with f2.
if (ori2 > 0) {
// apex(f) is below f1, continue (see Fig. 7).
} else if (ori2 < 0) {
// apex(f) is above f1, insert it (see Fig. 7).
break;
} else { // ori2 == 0.
// apex(f) is coplanar with f1 (see Fig. 8).
// f is either codirection with f1 or is codirection with f2.
facenormal(torg, tdest, sapex(f1->ss), n1, 1, NULL);
facenormal(torg, tdest, sapex(sface), n2, 1, NULL);
if (DOT(n1, n2) > 0) {
unifysubfaces(&(f1->ss), &sface);
} else {
unifysubfaces(&(f2->ss), &sface);
}
break;
}
}
// Go to the next item;
f1 = f2;
} // for (m = 0; ...)
if (sface.sh[3] != NULL) {
// Insert sface between f1 and f2.
newlinkitem = (badface *) flippool->alloc();
newlinkitem->ss = sface;
newlinkitem->nextitem = f1->nextitem;
f1->nextitem = newlinkitem;
}
} else if (flippool->items == 1) {
f1 = facelink;
// Make sure that f is not coplanar and codirection with f1.
ori1 = orient3d(torg, tdest, sapex(f1->ss), sapex(sface));
if (ori1 == 0) {
// f is coplanar with f1 (see Fig. 8).
facenormal(torg, tdest, sapex(f1->ss), n1, 1, NULL);
facenormal(torg, tdest, sapex(sface), n2, 1, NULL);
if (DOT(n1, n2) > 0) {
// The two faces are codirectional as well.
unifysubfaces(&(f1->ss), &sface);
}
}
// Add this face to link if it is not deleted.
if (sface.sh[3] != NULL) {
// Add this face into link.
newlinkitem = (badface *) flippool->alloc();
newlinkitem->ss = sface;
newlinkitem->nextitem = NULL;
f1->nextitem = newlinkitem;
}
} else {
// The first face.
newlinkitem = (badface *) flippool->alloc();
newlinkitem->ss = sface;
newlinkitem->nextitem = NULL;
facelink = newlinkitem;
}
} // for (k = idx2faclist[idx]; ...)
if (b->verbose > 2) {
printf(" Found %ld segments at (%d %d).\n", flippool->items,
pointmark(torg), pointmark(tdest));
}
//if (b->nobisect || b->nomerge) { // -Y or -M
// Set the vertex types of the endpoints of the segment.
setpointtype(torg, RIDGEVERTEX);
setpointtype(tdest, RIDGEVERTEX);
//}
// Set the 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;
if (b->verbose > 3) {
printf(" Bond subfaces (%d, %d, %d) and (%d, %d, %d).\n",
pointmark(torg), pointmark(tdest), pointmark(sapex(f1->ss)),
pointmark(torg), pointmark(tdest), pointmark(sapex(f2->ss)));
}
sbond1(f1->ss, f2->ss);
f1 = f2;
}
}
// // All identified segments has a marker "-1".
//setshellmark(subsegloop, -1);
// All identified segments has an init marker "0".
flippool->restart();
subsegloop.sh = shellfacetraverse(subsegs);
}
delete [] idx2faclist;
delete [] facperverlist;
}
///////////////////////////////////////////////////////////////////////////////
// //
// mergefacets() Merge adjacent facets. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::mergefacets()
{
face parentsh, neighsh, neineish;
face segloop;
point pa, pb, pc, pd;
REAL ang_tol, ang;
int remsegcount;
int fidx1, fidx2;
int fmrk1, fmrk2;
if (b->verbose > 1) {
printf(" Merging adjacent facets.\n");
}
// The dihedral angle bound for two different facets.
// Set by -p option. Default is 179 degree.
ang_tol = b->facet_ang_tol / 180.0 * PI;
remsegcount = 0;
// Loop all segments, merge adjacent coplanar facets.
subsegs->traversalinit();
segloop.sh = shellfacetraverse(subsegs);
while (segloop.sh != (shellface *) NULL) {
spivot(segloop, parentsh);
if (parentsh.sh != NULL) {
spivot(parentsh, neighsh);
if (neighsh.sh != NULL) {
spivot(neighsh, neineish);
if (neineish.sh == parentsh.sh) {
// Exactly two subfaces at this segment.
fidx1 = shellmark(parentsh) - 1;
fidx2 = shellmark(neighsh) - 1;
// Only merge them if they are in different facet.
if (fidx1 != fidx2) {
// The two subfaces are not in the same facet.
if (in->facetmarkerlist != NULL) {
fmrk1 = in->facetmarkerlist[fidx1];
fmrk2 = in->facetmarkerlist[fidx2];
} else {
fmrk1 = fmrk2 = 0;
}
// Only merge them if they have the same boundary marker.
if (fmrk1 == fmrk2) {
pa = sorg(segloop);
pb = sdest(segloop);
pc = sapex(parentsh);
pd = sapex(neighsh);
// Calculate the dihedral angle at the segment [a,b].
ang = facedihedral(pa, pb, pc, pd);
if (ang > PI) ang = (2 * PI - ang);
if (ang > ang_tol) {
if (b->verbose > 2) {
printf(" Merge at segment (%d, %d)-(%d, %d) ang = %g\n",
pointmark(pa), pointmark(pb), pointmark(pc),
pointmark(pd), ang / PI * 180.0);
}
remsegcount++;
ssdissolve(parentsh);
ssdissolve(neighsh);
shellfacedealloc(subsegs, segloop.sh);
// Add the edge to flip stack.
flipshpush(&parentsh);
} // if (ang > ang_tol)
} // if (fmrk1 == fmrk2)
} // if (fidx1 != fidx2)
} // if (neineish.sh == parentsh.sh)
}
}
segloop.sh = shellfacetraverse(subsegs);
}
if (flipstack != NULL) {
lawsonflip(); // Recover Delaunayness.
}
if (b->verbose > 1) {
printf(" %d segments are removed.\n", remsegcount);
}
}
///////////////////////////////////////////////////////////////////////////////
// //
// identifypscedges() Identify PSC edges. //
// //
// The set of PSC edges are provided in the 'in->edgelist'. Each edge should //
// also be an edge in the surface mesh. We find the corresponding edges in //
// the surface mesh and make them segments of the mesh. //
// //
// It is possible to give an edge which is not in any facet, i.e., it is a //
// dangling edge inside the volume. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::identifypscedges(point *idx2verlist)
{
face* shperverlist;
int* idx2shlist;
face searchsh, neighsh;
face segloop, checkseg, newseg;
point checkpt, pa, pb;
int *endpts;
int edgemarker;
int idx, i, j;
if (!b->quiet) {
printf("Inserting edges ...\n");
}
//assert(in->numberofedges > 0); // SELF_CHECK
//assert(in->edgemarkerlist != NULL); // SELF_CHECK
// All identified segments have the initial marker '0'.
// All segments inserted here should have a non-zero marker.
// Construct a map from points to subfaces.
makepoint2submap(subfaces, idx2shlist, shperverlist);
// Process the set of PSC edges.
for (i = 0; i < in->numberofedges; i++) {
endpts = &(in->edgelist[(i << 1)]);
// Find a face contains the edge.
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
edgemarker = 0;
if (in->edgemarkerlist) {
edgemarker = in->edgemarkerlist[i];
}
if (edgemarker == 0) {
edgemarker = 1;
}
// We should find a subface having this edge.
if (searchsh.sh != NULL) {
// Check if this edge is already a segment of the mesh.
sspivot(searchsh, checkseg);
if (checkseg.sh != NULL) {
// There should be no duplicated edges.
assert(shellmark(checkseg) == 0);
setshellmark(checkseg, edgemarker);
} else {
// Create a new segment at this edge.
pa = sorg(searchsh);
pb = sdest(searchsh);
if (b->verbose > 2) {
printf(" Create a new segment (%d, %d).\n",
pointmark(pa), pointmark(pb));
}
makeshellface(subsegs, &newseg);
setshvertices(newseg, pa, pb, NULL);
setshellmark(newseg, edgemarker);
ssbond(searchsh, newseg);
spivot(searchsh, neighsh);
if (neighsh.sh != NULL) {
ssbond(neighsh, newseg);
// There should be only two subfaces at this segment.
spivotself(neighsh); // SELF_CHECK
assert(neighsh.sh == searchsh.sh);
}
if (!b->psc) {
setpointtype(pa, RIDGEVERTEX);
setpointtype(pb, RIDGEVERTEX);
}
}
} else {
// It is a dangling segment (not belong to any facets).
// Get the two endpoints of this segment.
pa = idx2verlist[endpts[0]];
pb = idx2verlist[endpts[1]];
if (b->verbose > 2) {
printf(" Create a new segment (%d, %d) - dangling.\n",
pointmark(pa), pointmark(pb));
}
makeshellface(subsegs, &newseg);
setshvertices(newseg, pa, pb, NULL);
setshellmark(newseg, edgemarker);
//if (!b->psc) {
setpointtype(pa, RIDGEVERTEX);
setpointtype(pb, RIDGEVERTEX);
//}
}
} // i
if (b->psc) {
// Delete all segments of the mesh with a marker '0'.
subsegs->traversalinit();
segloop.sh = shellfacetraverse(subsegs);
while (segloop.sh != NULL) {
if (shellmark(segloop) == 0) {
if (b->verbose > 2) {
printf(" Remove a segment (%d, %d).\n",
pointmark(sorg(segloop)), pointmark(sdest(segloop)));
}
spivot(segloop, searchsh);
if (searchsh.sh != NULL) {
ssdissolve(searchsh);
spivot(searchsh, neighsh);
if (neighsh.sh != NULL) {
ssdissolve(neighsh);
// There should be only two subfaces at this segment.
spivotself(neighsh); // SELF_CHECK
assert(neighsh.sh == searchsh.sh);
}
}
shellfacedealloc(subsegs, segloop.sh);
}
segloop.sh = shellfacetraverse(subsegs);
}
}
delete [] shperverlist;
delete [] idx2shlist;
}
///////////////////////////////////////////////////////////////////////////////
// //
// 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(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);
}
// Triangulate F into a CDT.
triangulate(shmark, 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 (!b->nomerge && !b->nobisect && !b->diagnose) {
// Merge adjacent coplanar facets.
mergefacets();
}
if (in->numberofedges > 0) { // if (b->psc)
// There are segments specified by the user. Read and create them.
identifypscedges(idx2verlist);
}
if (b->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;
}
///////////////////////////////////////////////////////////////////////////////
// //
// interecursive() Recursively do intersection test on a set of triangles.//
// //
// Recursively split the set 'subfacearray' of subfaces into two sets using //
// a cut plane parallel to x-, or, y-, or z-axies. The split criteria are //
// follows. Assume the cut plane is H, and H+ denotes the left halfspace of //
// H, and H- denotes the right halfspace of H; and s be a subface: //
// //
// (1) If all points of s lie at H+, put it into left array; //
// (2) If all points of s lie at H-, put it into right array; //
// (3) If some points of s lie at H+ and some of lie at H-, or some //
// points lie on H, put it into both arraies. //
// //
// Partitions by x-axis if axis == '0'; by y-axis if axis == '1'; by z-axis //
// if axis == '2'. If current cut plane is parallel to the x-axis, the next //
// one will be parallel to y-axis, and the next one after the next is z-axis,//
// and then alternately return back to x-axis. //
// //
// Stop splitting when the number of triangles of the input array is not //
// decreased anymore. Do tests on the current set. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::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(1);
}
rightarray = new shellface*[arraysize];
if (rightarray == NULL) {
terminatetetgen(1);
}
leftsize = rightsize = 0;
if (axis == 0) {
// Split along x-axis.
split = 0.5 * (bxmin + bxmax);
} else if (axis == 1) {
// Split along y-axis.
split = 0.5 * (bymin + bymax);
} else {
// Split along z-axis.
split = 0.5 * (bzmin + bzmax);
}
for (i = 0; i < arraysize; i++) {
sface1.sh = subfacearray[i];
p1 = (point) sface1.sh[3];
p2 = (point) sface1.sh[4];
p3 = (point) sface1.sh[5];
toleft = toright = false;
if (p1[axis] < split) {
toleft = true;
if (p2[axis] >= split || p3[axis] >= split) {
toright = true;
}
} else if (p1[axis] > split) {
toright = true;
if (p2[axis] <= split || p3[axis] <= split) {
toleft = true;
}
} else {
// p1[axis] == split;
toleft = true;
toright = true;
}
// At least one is true;
#ifdef SELF_CHECK
assert(!(toleft == false && toright == false));
#endif
if (toleft) {
leftarray[leftsize] = sface1.sh;
leftsize++;
}
if (toright) {
rightarray[rightsize] = sface1.sh;
rightsize++;
}
}
if (leftsize < arraysize && rightsize < arraysize) {
// Continue to partition the input set. Now 'subfacearray' has been
// split into two sets, it's memory can be freed. 'leftarray' and
// 'rightarray' will be freed in the next recursive (after they're
// partitioned again or performing tests).
delete [] subfacearray;
// Continue to split these two sets.
if (axis == 0) {
interecursive(leftarray, leftsize, 1, bxmin, split, bymin, bymax,
bzmin, bzmax, internum);
interecursive(rightarray, rightsize, 1, split, bxmax, bymin, bymax,
bzmin, bzmax, internum);
} else if (axis == 1) {
interecursive(leftarray, leftsize, 2, bxmin, bxmax, bymin, split,
bzmin, bzmax, internum);
interecursive(rightarray, rightsize, 2, bxmin, bxmax, split, bymax,
bzmin, bzmax, internum);
} else {
interecursive(leftarray, leftsize, 0, bxmin, bxmax, bymin, bymax,
bzmin, split, internum);
interecursive(rightarray, rightsize, 0, bxmin, bxmax, bymin, bymax,
split, bzmax, internum);
}
} else {
if (b->verbose > 1) {
printf(" Checking intersecting faces.\n");
}
// Perform a brute-force compare on the set.
for (i = 0; i < arraysize; i++) {
sface1.sh = subfacearray[i];
p1 = (point) sface1.sh[3];
p2 = (point) sface1.sh[4];
p3 = (point) sface1.sh[5];
for (j = i + 1; j < arraysize; j++) {
sface2.sh = subfacearray[j];
p4 = (point) sface2.sh[3];
p5 = (point) sface2.sh[4];
p6 = (point) sface2.sh[5];
intersect = (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;
}
}
///////////////////////////////////////////////////////////////////////////////
// //
// detectinterfaces() Detect intersecting triangles. //
// //
// Given a set of triangles, find the pairs of intersecting triangles from //
// them. Here the set of triangles is in 'subfaces' which is a surface mesh //
// of a PLC (.poly or .smesh). //
// //
// To detect whether two triangles are intersecting is done by the routine //
// 'tri_tri_inter()'. The algorithm for the test is very simple and stable. //
// It is based on geometric orientation test which uses exact arithmetics. //
// //
// Use divide-and-conquer algorithm for reducing the number of intersection //
// tests. Start from the bounding box of the input point set, recursively //
// partition the box into smaller boxes, until the number of triangles in a //
// box is not decreased anymore. Then perform triangle-triangle tests on the //
// remaining set of triangles. The memory allocated in the input set is //
// freed immediately after it has been partitioned into two arrays. So it //
// can be re-used for the consequent partitions. //
// //
// On return, the pool 'subfaces' will be cleared, and only the intersecting //
// triangles remain for output (to a .face file). //
// //
///////////////////////////////////////////////////////////////////////////////
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;
shloop.sh = shellfacetraverse(subfaces);
i++;
}
internum = 0;
// Recursively split the set of triangles into two sets using a cut plane
// parallel to x-, or, y-, or z-axies. Stop splitting when the number
// of subfaces is not decreasing anymore. Do tests on the current set.
interecursive(subfacearray, subfaces->items, 0, xmin, xmax, ymin, ymax,
zmin, zmax, &internum);
if (!b->quiet) {
if (internum > 0) {
printf("\n!! Found %d pairs of faces are intersecting.\n\n", internum);
} else {
printf("\nNo faces are intersecting.\n\n");
}
}
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();
}
}
//// ////
//// ////
//// surface_cxx //////////////////////////////////////////////////////////////
//// constrained_cxx //////////////////////////////////////////////////////////
//// ////
//// ////
///////////////////////////////////////////////////////////////////////////////
// //
// markacutevertices() Classify vertices as ACUTEVERTEXs or RIDGEVERTEXs. //
// //
// Initially all segment vertices have type RIDGEVERTEX. A segment is acute //
// if there are at least two segments incident at it form an angle less than //
// theta (= 60 degree). //
// //
// The minimum segment-segment angle (minfaceang) is calculated. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::markacutevertices()
{
face* segperverlist;
int* idx2seglist;
point pa, pb, pc;
REAL anglimit, ang;
bool acuteflag;
int acutecount;
int idx, i, j;
REAL sharpanglimit;
int sharpsegcount;
if (b->verbose) {
printf(" Marking acute vertices.\n");
}
anglimit = PI / 3.0; // 60 degree.
sharpanglimit = 10.0 / 180.0 * PI; // 10 degree.
minfaceang = PI; // 180 degree.
acutecount = sharpsegcount = 0;
// Construct a map from points to segments.
makepoint2submap(subsegs, idx2seglist, segperverlist);
// Loop over the set of vertices.
points->traversalinit();
pa = pointtraverse();
while (pa != NULL) {
idx = pointmark(pa) - in->firstnumber;
// Mark it if it is an endpoint of some segments.
if (idx2seglist[idx + 1] > idx2seglist[idx]) {
if (b->psc) {
// Only test it if it is an input vertex.
if (pointtype(pa) == FREESEGVERTEX) {
pa = pointtraverse();
continue;
}
}
acuteflag = false;
// Do a brute-force pair-pair check.
for (i=idx2seglist[idx]; i<idx2seglist[idx + 1]; i++) {
pb = sdest(segperverlist[i]);
//for (j = i + 1; j < idx2seglist[idx + 1] && !acuteflag; j++) {
for (j = i + 1; j < idx2seglist[idx + 1]; j++) {
pc = sdest(segperverlist[j]);
ang = interiorangle(pa, pb, pc, NULL);
//acuteflag = ang < anglimit;
if (!acuteflag) {
acuteflag = ang < anglimit;
}
// Remember the smallest angle.
if (ang < minfaceang) minfaceang = ang;
// Mark segments at extremely small angle.
if (ang < sharpanglimit) {
if (shelltype(segperverlist[i]) != SHARP) {
setshelltype(segperverlist[i], SHARP);
sharpsegcount++;
}
if (shelltype(segperverlist[j]) != SHARP) {
setshelltype(segperverlist[j], SHARP);
sharpsegcount++;
}
}
} // j
} // i
if (!acuteflag) {
if ((idx2seglist[idx + 1] - idx2seglist[idx]) > 4) {
// There are at least 5 segments shared at this vertices.
acuteflag = true;
}
}
if (acuteflag) {
if (b->verbose > 2) {
printf(" Mark %d as ACUTEVERTEX.\n", pointmark(pa));
}
setpointtype(pa, ACUTEVERTEX);
acutecount++;
}
}
pa = pointtraverse();
}
if (b->verbose) {
if (acutecount > 0) {
printf(" Found %d acute vertices.\n", acutecount);
}
if (sharpsegcount > 0) {
printf(" Found %d sharp segments.\n", sharpsegcount);
}
printf(" Minimum seg-seg angle = %g.\n", minfaceang / PI * 180.0);
}
delete [] idx2seglist;
delete [] segperverlist;
}
///////////////////////////////////////////////////////////////////////////////
// //
// reportselfintersect() Report a self-intersection. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::reportselfintersect(face *checkseg, face *checksh)
{
face parentsh;
point pa, pb, pc, pd, pe;
point fa, fb;
pa = sorg(*checkseg);
pb = sdest(*checkseg);
fa = farsorg(*checkseg);
fb = farsdest(*checkseg);
pc = sorg(*checksh);
pd = sdest(*checksh);
pe = sapex(*checksh);
printf(" !! Detected a self-intersection between:\n");
printf(" A segment [%d,%d] < [%d,%d], \n", pointmark(pa), pointmark(pb),
pointmark(fa), pointmark(fb));
printf(" a subface [%d,%d,%d] in facet #%d.\n", pointmark(pc),
pointmark(pd), pointmark(pe), shellmark(*checksh));
}
///////////////////////////////////////////////////////////////////////////////
// //
// finddirection() Find the tet on the path from one point to another. //
// //
// The path starts from 'searchtet''s origin and ends at 'endpt'. On finish, //
// 'searchtet' contains a tet on the path, its origin does not change. //
// //
// The return value indicates one of the following cases (let 'searchtet' be //
// abcd, a is the origin of the path): //
// - ACROSSVERT, edge ab is collinear with the path; //
// - ACROSSEDGE, edge bc intersects with the path; //
// - ACROSSFACE, face bcd intersects with the path. //
// //
// WARNING: This routine is designed for convex triangulations, and will not //
// generally work after the holes and concavities have been carved. //
// //
///////////////////////////////////////////////////////////////////////////////
enum tetgenmesh::interresult
tetgenmesh::finddirection(triface* searchtet, point endpt, int randflag)
{
triface neightet;
point pa, pb, pc, pd;
enum {HMOVE, RMOVE, LMOVE} nextmove;
REAL hori, rori, lori;
int s;
// The origin is fixed.
pa = org(*searchtet);
if ((point) searchtet->tet[7] == dummypoint) {
// A hull tet. Choose the neighbor of its base face.
searchtet->ver = 11;
fsymself(*searchtet);
// Reset the origin to be pa.
if ((point) searchtet->tet[4] == pa) {
searchtet->ver = 11;
} else if ((point) searchtet->tet[5] == pa) {
searchtet->ver = 3;
} else if ((point) searchtet->tet[6] == pa) {
searchtet->ver = 7;
} else {
assert((point) searchtet->tet[7] == pa); // SELF_CHECK
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.
eprevself(*searchtet);
esymself(*searchtet);
return ACROSSVERT;
}
// Walk through tets around pa until the right one is found.
while (1) {
pd = oppo(*searchtet);
if (b->verbose > 3) {
printf(" From tet (%d, %d, %d, %d) to %d.\n", pointmark(pa),
pointmark(pb), pointmark(pc), pointmark(pd), pointmark(endpt));
}
// Check whether the opposite vertex is 'endpt'.
if (pd == endpt) {
// pa->pd is the search edge.
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.
assert(nonconvex);
return ACROSSSUB; // Hit a bounday.
}
// Now assume that the base face abc coincides with the horizon plane,
// and d lies above the horizon. The search point 'endpt' may lie
// above or below the horizon. We test the orientations of 'endpt'
// with respect to three planes: abc (horizon), bad (right plane),
// and acd (left plane).
hori = orient3d(pa, pb, pc, endpt);
rori = orient3d(pb, pa, pd, endpt);
lori = orient3d(pa, pc, pd, endpt);
orient3dcount += 3;
// Now decide the tet to move. It is possible there are more than one
// tet are viable moves. Use the opposite points of thier neighbors
// to discriminate, i.e., we choose the tet whose opposite point has
// the shortest distance to 'endpt'.
if (hori > 0) {
if (rori > 0) {
if (lori > 0) {
// Any of the three neighbors is a viable move.
if (0) { // if (!randflag) {
} else {
// Randomly choose a direction.
s = randomnation(3); // 's' is in {0,1,2}.
if (s == 0) {
nextmove = HMOVE;
} else if (s == 1) {
nextmove = RMOVE;
} else {
nextmove = LMOVE;
}
} // if (randflag)
} else {
// Two tets, below horizon and below right, are viable.
if (0) { // if (!randflag) {
} else {
// Randomly choose a direction.
s = randomnation(2); // 's' is in {0,1}.
if (s == 0) {
nextmove = HMOVE;
} else {
nextmove = RMOVE;
}
} // if (randflag)
}
} else {
if (lori > 0) {
// Two tets, below horizon and below left, are viable.
if (0) { // if (!randflag) {
} else {
// Randomly choose a direction.
s = randomnation(2); // 's' is in {0,1}.
if (s == 0) {
nextmove = HMOVE;
} else {
nextmove = LMOVE;
}
} // if (randflag)
} else {
// The tet below horizon is chosen.
nextmove = HMOVE;
}
}
} else {
if (rori > 0) {
if (lori > 0) {
// Two tets, below right and below left, are viable.
if (0) { // if (!randflag) {
} else {
// Randomly choose a direction.
s = randomnation(2); // 's' is in {0,1}.
if (s == 0) {
nextmove = RMOVE;
} else {
nextmove = LMOVE;
}
} // if (randflag)
} else {
// The tet below right is chosen.
nextmove = RMOVE;
}
} 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.
eprevself(*searchtet);
esymself(*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.
eprevself(*searchtet);
esymself(*searchtet); // face acd
return ACROSSEDGE;
}
// pa->'endpt' crosses the face bcd.
return ACROSSFACE;
}
}
}
// 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);
}
assert(org(*searchtet) == pa); // SELF_CHECK
pb = dest(*searchtet);
pc = apex(*searchtet);
} // while (1)
}
///////////////////////////////////////////////////////////////////////////////
// //
// scoutsegment() Look for a given segment in the tetrahedralization T. //
// //
// Search an edge in the tetrahedralization that matches the given segmment. //
// If such an edge exists, the segment is 'locked' at the edge. 'searchtet' //
// returns this (constrained) edge. Otherwise, the segment is missing. //
// //
// The returned value indicates one of the following cases: //
// - SHAREEDGE, the segment exists and is inserted in T; //
// - ACROSSEDGE, the segment intersects an edge (in 'searchtet'). //
// - ACROSSFACE, the segment crosses a face (in 'searchtet'). //
// //
// The following cases can happen when the input PLC is not valid. //
// - ACROSSVERT, the segment intersects a vertex ('refpt'). //
// - ACROSSSEG, the segment intersects a segment(returned by 'searchtet'). //
// - ACROSSSUB, the segment intersects a subface(returned by 'searchtet'). //
// //
// If the returned value is ACROSSEDGE or ACROSSFACE, i.e., the segment is //
// missing, 'refpt' returns the reference point for splitting thus segment, //
// 'searchtet' returns a tet containing the 'refpt'. //
// //
///////////////////////////////////////////////////////////////////////////////
enum tetgenmesh::interresult
tetgenmesh::scoutsegment(point startpt, point endpt, triface* searchtet,
point* refpt, arraypool* intfacelist)
{
triface neightet, reftet;
face checkseg, checksh;
point pa, pb, pc, pd;
badface *bface;
enum interresult dir;
REAL angmax, ang;
long facecount;
int types[2], poss[4];
int pos, i, j;
if (b->verbose > 2) {
printf(" Scout seg (%d, %d).\n", pointmark(startpt), pointmark(endpt));
}
point2tetorg(startpt, *searchtet);
dir = finddirection(searchtet, endpt, 0);
if (dir == ACROSSVERT) {
pd = dest(*searchtet);
if (pd == endpt) {
// The job is done.
return SHAREEDGE;
} else {
// A point is on the path.
*refpt = pd;
return ACROSSVERT;
}
} // if (dir == ACROSSVERT)
if (b->verbose > 2) {
printf(" Seg is missing.\n");
}
// dir is either ACROSSEDGE or ACROSSFACE.
enextesymself(*searchtet); // Go to the opposite face.
fsymself(*searchtet); // Enter the adjacent tet.
if (dir == ACROSSEDGE) {
// Check whether two segments are intersecting.
tsspivot1(*searchtet, checkseg);
if (checkseg.sh != NULL) {
return ACROSSSEG;
}
across_edge_count++;
} else if (dir == ACROSSFACE) {
if (checksubfaceflag) {
// Check whether a segment and a subface are intersecting.
tspivot(*searchtet, checksh);
if (checksh.sh != NULL) {
return ACROSSSUB;
}
}
}
if (refpt == NULL) {
return dir;
}
if (b->verbose > 2) {
printf(" Scout a ref-point for it.\n");
}
facecount = across_face_count;
pa = org(*searchtet);
angmax = interiorangle(pa, startpt, endpt, NULL);
*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) {
if (intfacelist != NULL) {
if (dir == ACROSSFACE) {
// Save the intersecting face.
intfacelist->newindex((void **) &bface);
bface->tt = *searchtet;
bface->forg = org(*searchtet);
bface->fdest = dest(*searchtet);
bface->fapex = apex(*searchtet);
// Save the intersection type (ACROSSFACE or ACROSSEDGE).
bface->key = (REAL) dir;
} else { // dir == ACROSSEDGE
i = 0;
if (intfacelist->objects > 0l) {
// Get the last saved one.
bface = (badface *) fastlookup(intfacelist, intfacelist->objects - 1);
if (((enum interresult) (int) bface->key) == ACROSSEDGE) {
// Skip this edge if it is the same as the last saved one.
if (((bface->forg == org(*searchtet)) &&
(bface->fdest == dest(*searchtet))) ||
((bface->forg == dest(*searchtet)) &&
(bface->fdest == org(*searchtet)))) {
i = 1; // Skip this edge.
}
}
}
if (i == 0) {
// Save this crossing edge.
intfacelist->newindex((void **) &bface);
bface->tt = *searchtet;
bface->forg = org(*searchtet);
bface->fdest = dest(*searchtet);
// bface->fapex = apex(*searchtet);
// Save the intersection type (ACROSSFACE or ACROSSEDGE).
bface->key = (REAL) dir;
}
}
}
pd = oppo(*searchtet);
assert(pd != dummypoint); // SELF_CHECK
if (b->verbose > 3) {
printf(" Passing face (%d, %d, %d, %d), dir(%d).\n",
pointmark(pa), pointmark(pb), pointmark(pc), pointmark(pd),
(int) dir);
}
across_face_count++;
// Stop if we meet 'endpt'.
if (pd == endpt) break;
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;
}
}
assert(dir != DISJOINT); // SELF_CHECK
} else { // 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);
}
pd = org(neightet);
if (b->verbose > 2) {
angmax = interiorangle(pd, startpt, endpt, NULL);
}
*refpt = pd;
// break;
return ACROSSVERT;
} else if (dir == ACROSSEDGE) {
// Get the edge intersects with the segment.
for (i = 0; i < pos; i++) {
enextself(neightet);
}
}
// Go to the next tet.
fsym(neightet, *searchtet);
if (dir == ACROSSEDGE) {
// Check whether two segments are intersecting.
tsspivot1(*searchtet, checkseg);
if (checkseg.sh != NULL) {
return ACROSSSEG;
}
across_edge_count++;
} else if (dir == ACROSSFACE) {
if (checksubfaceflag) {
// Check whether a segment and a subface are intersecting.
tspivot(*searchtet, checksh);
if (checksh.sh != NULL) {
return ACROSSSUB;
}
}
}
} // 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;
}
// dir is either ACROSSVERT, or ACROSSEDGE, or ACROSSFACE.
if (b->verbose > 2) {
if (*refpt != NULL) {
printf(" Refpt %d (%g), visited %ld faces.\n", pointmark(*refpt),
angmax / PI * 180.0, across_face_count - facecount);
} else {
printf(" No refpt (%g) is found, visited %ld faces.\n",
angmax / PI * 180.0, across_face_count - facecount);
}
}
if (across_face_count - facecount > across_max_count) {
across_max_count = across_face_count - facecount;
}
*searchtet = reftet;
return dir;
}
///////////////////////////////////////////////////////////////////////////////
// //
// getsteinerpointonsegment() Get a Steiner point on a segment. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::getsteinerptonsegment(face* seg, point refpt, point steinpt)
{
point ei, ej;
REAL Li, Lj, L, dj, dr;
REAL ti = 0.0, tj = 0.0, t;
int type, eid = 0, i;
REAL diff, stept = 0.0, L1;
int iter;
ei = sorg(*seg);
ej = sdest(*seg);
if (b->verbose > 2) {
printf(" Get Steiner point on seg [%d (%c), %d (%c)].\n",
pointmark(ei), pointtype(ei) == ACUTEVERTEX ? 'A' : 'N',
pointmark(ej), pointtype(ej) == ACUTEVERTEX ? 'A' : 'N');
}
if (b->psc) {
eid = shellmark(*seg);
if (pointtype(ei) != FREESEGVERTEX) {
ti = in->getvertexparamonedge(in->geomhandle, pointmark(ei), eid);
} else {
ti = pointgeomuv(ei, 0);
}
if (pointtype(ej) != FREESEGVERTEX) {
tj = in->getvertexparamonedge(in->geomhandle, pointmark(ej), eid);
} else {
tj = pointgeomuv(ej, 0);
}
}
if (refpt != NULL) {
if (pointtype(ei) == ACUTEVERTEX) {
if (pointtype(ej) == ACUTEVERTEX) {
// Choose the vertex which is closer to refpt.
Li = distance(ei, refpt);
Lj = distance(ej, refpt);
if (Li > Lj) {
// Swap ei and ej;
sesymself(*seg);
ei = sorg(*seg);
ej = sdest(*seg);
t = ti;
ti = tj;
tj = t;
}
type = 1;
} else {
type = 1;
}
} else {
if (pointtype(ej) == ACUTEVERTEX) {
type = 1;
// Swap ei and ej;
sesymself(*seg);
ei = sorg(*seg);
ej = sdest(*seg);
t = ti;
ti = tj;
tj = t;
} else {
type = 0;
}
}
} else {
type = 0;
}
if (type == 1) {
L = distance(ei, ej);
Li = distance(ei, refpt);
// Cut the segment by a sphere centered at ei with radius Li.
if (b->psc) {
stept = (tj - ti) / 100.0;
iter = 0;
t = ti + (Li / L) * (tj - ti);
while (1) {
in->getsteineronedge(in->geomhandle, eid, t, steinpt);
L1 = distance(steinpt, ei);
diff = L1 - Li;
if ((fabs(diff) / L) < 1e-3) {
break;
}
if (diff > 0) {
t -= stept; // Move it towards ei.
} else {
t += stept; // Move it towards ej.
}
iter++;
if (iter > 10) {
printf("Warning: Get the right Steiner point failed.\n");
break;
}
} // while (1)
} else {
t = Li / L;
for (i = 0; i < 3; i++) {
steinpt[i] = ei[i] + t * (ej[i] - ei[i]);
}
}
// Avoid creating a too short edge.
dj = distance(steinpt, ej);
dr = distance(steinpt, refpt);
if (dj < dr) {
// Cut the segment by the radius equal to Li / 2.
if (b->psc) {
iter = 0;
t = ti + ((Li / 2.0) / L) * (tj - ti);
while (1) {
in->getsteineronedge(in->geomhandle, eid, t, steinpt);
L1 = distance(steinpt, ei);
diff = L1 - (Li / 2.0);
if ((fabs(diff) / L) < 1e-3) {
break;
}
if (diff > 0) {
t -= stept; // Move it towards ei.
} else {
t += stept; // Move it towards ej.
}
iter++;
if (iter > 10) {
printf("Warning: Get the right Steiner point failed.\n");
break;
}
} // while (1)
} else {
t = (Li / 2.0) / L;
for (i = 0; i < 3; i++) {
steinpt[i] = ei[i] + t * (ej[i] - ei[i]);
}
}
r3count++;
} else {
r2count++;
}
} else {
// Split the point at the middle.
if (b->psc) {
t = 0.5 * (ti + tj);
in->getsteineronedge(in->geomhandle, eid, t, steinpt);
} else {
t = 0.5;
for (i = 0; i < 3; i++) {
steinpt[i] = ei[i] + t * (ej[i] - ei[i]);
}
}
r1count++;
}
if (b->psc) {
setpointgeomuv(steinpt, 0, t);
setpointgeomtag(steinpt, eid);
}
if (pointtype(steinpt) == UNUSEDVERTEX) {
setpointtype(steinpt, FREESEGVERTEX);
}
if (b->verbose > 2) {
printf(" Split at t(%g)", t);
if (b->psc) {
printf(", ti(%g), tj(%g)", ti, tj);
}
printf(".\n");
}
}
///////////////////////////////////////////////////////////////////////////////
// //
// delaunizesegments() Recover segments in a DT. //
// //
// All segments need to be recovered are in 'subsegstack' (Q). They will be //
// be recovered one by one (in a random order). //
// //
// Given a segment s in the Q, this routine first queries s in the DT, if s //
// matches an edge in DT, it is 'locked' at the edge. Otherwise, s is split //
// by inserting a new point p in both the DT and itself. The two new subseg- //
// ments of s are queued in Q. The process continues until Q is empty. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::delaunizesegments()
{
triface searchtet, spintet;
face searchsh, checksh;
face sseg, checkseg, *psseg;
point refpt, newpt;
enum interresult dir;
insertvertexflags ivf;
int loc;
// For reporting PLC problems.
point forg1, fdest1; // The 1st segment.
point forg2, fdest2, fapex2; // The 2nd segment.
// Does this mesh containing subfaces?
if (checksubfaceflag) {
ivf.bowywat = 2; // The mesh is a CDT.
ivf.lawson = 2; // Do flip to recover Delaunayness.
ivf.validflag = 1; // Validation is needed.
} else {
ivf.bowywat = 1; // The mesh is a DT.
ivf.lawson = 0; // No need to do flip.
ivf.validflag = 0; // No need to valid the B-W cavity.
}
searchsh.sh = NULL;
// 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;
assert(!sinfected(sseg)); // FOR DEBUG
// Check if this segment has been recovered.
sstpivot1(sseg, searchtet);
if (searchtet.tet != NULL) {
// FOR DEBUG
// Check if the tet contains the same segment.
tsspivot1(searchtet, checkseg);
assert(checkseg.sh == sseg.sh);
continue; // Not a missing segment.
}
// Search the segment.
dir = scoutsegment(sorg(sseg), sdest(sseg), &searchtet, &refpt, NULL);
if (dir == SHAREEDGE) {
// Found this segment, insert it.
tsspivot1(searchtet, checkseg); // SELF_CHECK
if (checkseg.sh == NULL) {
// Let the segment remember an adjacent tet.
sstbond1(sseg, searchtet);
// Bond the segment to all tets containing it.
spintet = searchtet;
do {
tssbond1(spintet, sseg);
fnextself(spintet);
} while (spintet.tet != searchtet.tet);
} else {
// Collision! Should not happen.
assert(0);
}
} else {
if ((dir == ACROSSFACE) || (dir == ACROSSEDGE)) {
// The segment is missing. Split it.
// Create a new point.
makepoint(&newpt, FREESEGVERTEX);
//setpointtype(newpt, FREESEGVERTEX);
getsteinerptonsegment(&sseg, refpt, newpt);
// Start searching from the 'searchtet'.
ivf.iloc = (int) OUTSIDE;
//ivf.bowywat;
//ivf.lawson;
ivf.rejflag = 0;
ivf.chkencflag = 0;
ivf.sloc = ivf.iloc;
ivf.sbowywat = ivf.bowywat;
ivf.splitbdflag = 0;
// ivf.validflag
ivf.respectbdflag = 0;
ivf.assignmeshsize = 0;
// Insert the new point into the tetrahedralization T.
// Missing segments and subfaces are queued for recovery.
// Note that T is convex (nonconvex = 0).
loc = insertvertex(newpt, &searchtet, &searchsh, &sseg, &ivf);
assert(loc != (int) ONVERTEX);
if (loc != (int) NEARVERTEX) {
// The new point has been inserted.
if (ivf.lawson > 0) {
// For CDT, use flips to reocver Delaunayness.
lawsonflip3d(newpt, ivf.lawson, 0, 0, 0);
}
st_segref_count++; //st_segpro_count++;
if (steinerleft > 0) steinerleft--;
} else {
// The new point is either ON or VERY CLOSE to an existing point.
refpt = point2ppt(newpt);
printf(" !! Avoid to create a short edge (length = %g)\n",
distance(newpt, refpt));
// It is probably an input problem. Two possible cases are:
// (1) An input vertex is very close an input segment; or
// (2) Two input segments are nearly intersect each other.
forg1 = farsorg(sseg);
fdest1 = farsdest(sseg);
if ((pointtype(refpt) == RIDGEVERTEX) ||
(pointtype(refpt) == ACUTEVERTEX) ||
(pointtype(refpt) == VOLVERTEX)) {
// Case (1)
printf(" !! Point %d is very close to segment (%d, %d).\n",
pointmark(refpt), pointmark(forg1), pointmark(fdest1));
} else if (pointtype(refpt) == FREESEGVERTEX) {
// Case (2). Find a subsegment contain 'refpt'.
subsegs->traversalinit();
checkseg.sh = shellfacetraverse(subsegs);
while (checkseg.sh != NULL) {
if (((point) checkseg.sh[3] == refpt) ||
((point) checkseg.sh[4] == refpt)) break;
checkseg.sh = shellfacetraverse(subsegs);
}
assert(checkseg.sh != NULL);
checkseg.shver = 0;
forg2 = farsorg(checkseg);
fdest2 = farsdest(checkseg);
printf(" !! Two segments are very close to each other.\n");
printf(" 1st: (%d, %d), 2nd: (%d, %d)\n", pointmark(forg1),
pointmark(fdest1), pointmark(forg2), pointmark(fdest2));
} else {
// Unknown case
assert(0);
}
// Indicate it may be an input problem.
printf(" Short edge length bound is: %g. Tolerance is %g.\n",
b->minedgelength, b->epsilon);
terminatetetgen(4);
}
} else {
// The input PLC contains self-intersections.
if (dir == ACROSSVERT) {
// refpt is the vertex intersecting the segment.
forg1 = farsorg(sseg);
fdest1 = farsdest(sseg);
if ((pointtype(refpt) == RIDGEVERTEX) ||
(pointtype(refpt) == ACUTEVERTEX) ||
(pointtype(refpt) == FACETVERTEX) ||
(pointtype(refpt) == VOLVERTEX)) {
printf("Point %d is on segment (%d, %d).\n",
pointmark(refpt), pointmark(forg1), pointmark(fdest1));
} else if (pointtype(refpt) == FREESEGVERTEX) {
// Case (2). Find a subsegment contain 'refpt'.
subsegs->traversalinit();
checkseg.sh = shellfacetraverse(subsegs);
while (checkseg.sh != NULL) {
if (((point) checkseg.sh[3] == refpt) ||
((point) checkseg.sh[4] == refpt)) break;
checkseg.sh = shellfacetraverse(subsegs);
}
assert(checkseg.sh != NULL);
checkseg.shver = 0;
forg2 = farsorg(checkseg);
fdest2 = farsdest(checkseg);
printf("Two segments intersect.\n");
printf(" 1st: (%d, %d), 2nd: (%d, %d)", pointmark(forg1),
pointmark(fdest1), pointmark(forg2), pointmark(fdest2));
} else if (pointtype(refpt) == FREEFACETVERTEX) {
assert(0); // Report this case.
}
} else if (dir == ACROSSSEG) {
tsspivot1(searchtet, checkseg);
if (!b->quiet) {
printf("Two segments intersect.\n");
forg1 = farsorg(sseg);
fdest1 = farsdest(sseg);
forg2 = farsorg(checkseg);
fdest2 = farsdest(checkseg);
printf(" 1st: (%d, %d), 2nd: (%d, %d).\n", pointmark(forg1),
pointmark(fdest1), pointmark(forg2), pointmark(fdest2));
}
} else if (dir == ACROSSSUB) {
tspivot(searchtet, checksh);
if (!b->quiet) {
printf("A segment and a subface intersect.\n");
forg1 = farsorg(sseg);
fdest1 = farsdest(sseg);
forg2 = sorg(checksh);
fdest2 = sdest(checksh);
fapex2 = sapex(checksh);
printf(" Seg: (%d, %d), Sub: (%d, %d, %d).\n",
pointmark(forg1), pointmark(fdest1),
pointmark(forg2), pointmark(fdest2), pointmark(fapex2));
}
} else {
// Unknown cases.
assert(0);
}
// Indicate it is an input problem.
terminatetetgen(3);
}
}
} // while
}
///////////////////////////////////////////////////////////////////////////////
// //
// scoutsubface() Look for a given subface in the tetrahedralization T. //
// //
// 'searchsh' is searched in T. If it exists, it is 'locked' at the face in //
// T. 'searchtet' refers to the face. Otherwise, it is missing. //
// //
// The return value indicates one of the following cases: //
// - SHAREFACE, 'searchsh' exists and is inserted in T. //
// - COLLISIONFACE, 'searchsh' exists in T, but it conflicts with another //
// subface which was inserted earlier. It is not inserted. //
// //
///////////////////////////////////////////////////////////////////////////////
enum tetgenmesh::interresult
tetgenmesh::scoutsubface(face* searchsh, triface* searchtet)
{
triface spintet;
face checksh;
point pa, pb, pc;
enum interresult dir;
pa = sorg(*searchsh);
pb = sdest(*searchsh);
if (b->verbose > 2) {
printf(" Scout subface (%d, %d, %d).\n", pointmark(pa), pointmark(pb),
pointmark(sapex(*searchsh)));
}
// Get a tet whose origin is a.
point2tetorg(pa, *searchtet);
// Search the edge [a,b].
dir = finddirection(searchtet, pb, 0);
if (dir == ACROSSVERT) {
// Check validity of a PLC.
if (dest(*searchtet) != pb) {
// A vertex lies on the search edge. Return it.
enextself(*searchtet);
return TOUCHEDGE;
}
// The edge exists. Check if the face exists.
pc = sapex(*searchsh);
// Searchtet holds edge [a,b]. Search a face with apex c.
spintet = *searchtet;
while (1) {
if (apex(spintet) == pc) {
// Found a face matching to 'searchsh'!
tspivot(spintet, checksh);
if (checksh.sh == NULL) {
// Insert 'searchsh'.
tsbond(spintet, *searchsh);
fsymself(spintet);
sesymself(*searchsh);
tsbond(spintet, *searchsh);
*searchtet = spintet;
return SHAREFACE;
} else {
// Another subface is already inserted.
assert(checksh.sh != searchsh->sh); // SELF_CHECK
// This is possibly an input problem, i.e., two facets overlap.
// Report this problem and exit.
printf("Warning: Found two facets nearly overlap.\n");
terminatetetgen(5);
// unifysubfaces(&checksh, searchsh);
*searchtet = spintet;
return COLLISIONFACE;
}
}
fnextself(spintet);
if (spintet.tet == searchtet->tet) break;
}
}
// dir is either ACROSSEDGE or ACROSSFACE.
return dir; //ACROSSTET;
}
///////////////////////////////////////////////////////////////////////////////
// //
// formmissingregion() Form the missing region of a missing subface. //
// //
// 'missh' is a missing subface. From it we form a missing region R which is //
// a collection of missing subfaces connected through adjacent edges. //
// //
// The missing region R is returned in the array 'missingshs'. All subfaces //
// in R are oriented as 'missh'. The array 'missingshverts' returns all ver- //
// tices of R. All subfaces and vertices of R are marktested. //
// //
// 'adjtets' returns a list of tetrahedra adjacent to R. They are used to //
// search a crossing tetrahedron of R. //
// //
// Many ways are possible to form the missing region. The method used here //
// is to search missing edges in R. Starting from 'missh', its three edges //
// are checked. If one of the edges is missing, then the adjacent subface at //
// this edge is also missing. It is added to the array. By an incrementally //
// broad-first searching, we can find all subfaces of R. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::formmissingregion(face* missh, arraypool* missingshs,
arraypool* missingshbds,
arraypool* missingshverts,
arraypool* adjtets)
{
triface searchtet, *parytet;
face neighsh, *parysh;
point pa, pb, *parypt;
enum interresult dir;
int i, j;
if (b->verbose > 2) {
printf(" Form missing region from subface (%d, %d, %d)\n",
pointmark(sorg(*missh)), pointmark(sdest(*missh)),
pointmark(sapex(*missh)));
}
smarktest(*missh);
missingshs->newindex((void **) &parysh);
*parysh = *missh;
// 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);
// Get a tet whose origin is a.
point2tetorg(pa, searchtet);
// Search the edge [a,b].
dir = finddirection(&searchtet, pb, 0);
if (dir != ACROSSVERT) {
// This edge is missing. Its neighbor is a missing subface.
spivot(*missh, neighsh);
assert(neighsh.sh != NULL);
if (!smarktested(neighsh)) {
// Adjust the face orientation.
if (sorg(neighsh) != pb) {
sesymself(neighsh);
}
if (b->verbose > 3) {
printf(" Add a missing subface (%d, %d, %d)\n",
pointmark(pb), pointmark(pa), pointmark(sapex(neighsh)));
}
smarktest(neighsh);
missingshs->newindex((void **) &parysh);
*parysh = neighsh;
}
} else {
if (dest(searchtet) == pb) {
// Remember an existing edge for searching the first crossing tet.
adjtets->newindex((void **) &parytet);
*parytet = searchtet;
// Found an existing edge, it must be a boundary edge of R.
if (b->verbose > 3) {
printf(" -- A boundary edge (%d, %d)\n", pointmark(pa),
pointmark(pb));
}
missingshbds->newindex((void **) &parysh);
*parysh = *missh; // It is only queued once.
} else {
// The input PLC has problem.
//assert(0);
terminatetetgen(3);
}
}
// Collect the vertices of R.
if (!pmarktested(pa)) {
pmarktest(pa);
missingshverts->newindex((void **) &parypt);
*parypt = pa;
}
senextself(*missh);
} // j
} // i
if (b->verbose > 2) {
printf(" Region has: %ld subfaces, %ld vertices\n",
missingshs->objects, missingshverts->objects);
}
if (missingshs->objects > maxregionsize) {
maxregionsize = missingshs->objects;
}
// Unmarktest collected missing subfaces.
for (i = 0; i < missingshs->objects; i++) {
missh = (face *) fastlookup(missingshs, i);
sunmarktest(*missh);
}
// Comment: All vertices in R are pmarktested.
}
///////////////////////////////////////////////////////////////////////////////
// //
// scoutcrossedge() Search an edge that crosses the missing region. //
// //
// Assumption: All vertices of the missing region are marktested. //
// //
///////////////////////////////////////////////////////////////////////////////
int tetgenmesh::scoutcrossedge(triface& crosstet, arraypool* adjtets,
arraypool* missingshs)
{
triface *searchtet, spintet;
face *parysh;
face checkseg;
point pa, pb, pc, pd, pe;
enum interresult dir;
REAL ori;
int types[2], poss[4];
int searchflag, interflag;
int i, j;
if (b->verbose > 2) {
printf(" Search a crossing edge.\n");
}
searchflag = 0;
for (j = 0; j < adjtets->objects && !searchflag; j++) {
searchtet = (triface *) fastlookup(adjtets, j);
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.
dir = (enum interresult) types[0];
if ((dir == ACROSSFACE) || (dir == ACROSSEDGE)) {
//pos = poss[0];
// Go to the crossing edge [d,e,#,#].
eprev(spintet, crosstet);
esymself(crosstet);
enextself(crosstet); // [d,e,#,#].
// Check if it is a segment.
tsspivot1(crosstet, checkseg);
if (checkseg.sh != NULL) {
reportselfintersect(&checkseg, parysh);
terminatetetgen(3);
}
// Adjust the edge such that d lies below [a,b,c].
ori = orient3d(pa, pb, pc, pd);
assert(ori != 0);
if (ori < 0) {
esymself(crosstet);
}
if (b->verbose > 2) {
printf(" Found edge (%d, %d) intersect", pointmark(pd),
pointmark(pe));
printf(" face (%d, %d, %d)\n", pointmark(pa), pointmark(pb),
pointmark(pc));
}
// Save the corners of this subface.
plane_pa = pa;
plane_pb = pb;
plane_pc = pc;
searchflag = 1;
} else {
// An improper intersection type.
// Maybe it is a PLC problem.
// At the moment, just ignore it.
}
}
break;
} // 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
adjtets->restart();
return searchflag;
}
///////////////////////////////////////////////////////////////////////////////
// //
// formcavity() Form the cavity of a missing region. //
// //
// The missing region R is formed by a set of missing subfaces 'missingshs'. //
// In the following, we assume R is horizontal and oriented. (All subfaces //
// of R are oriented in the same way.) 'searchtet' is a tetrahedron [d,e,#, //
// #] which intersects R in its interior, where the edge [d,e] intersects R, //
// and d lies below R. //
// //
// By knowing a crossing edge [d,e], all tetrahedra sharing at [d,e] must //
// cross R. They are added into the list 'crosstets'. From this set of tets, //
// new crossing edges (if there exist) can be detected. The key is how to //
// detect them correctly. The approach used here is described as follows: //
// Suppose [d,e,a,b] is a crossing tet, where [d,e] intersects R and d lies //
// below R. We look at the face [d,e,a]. If the apex a is not pmarktested, //
// i.e., it is not a vertex of R, then either edge [e,a] or [a,d] intersects //
// R. A simple way is to perform above/below test between [a] and R. But it //
// is not clear which subface of R is the best for this test. Also, this is //
// not safe when R is not strictly planar. A second way is to test if [e,a] //
// intersects one of the subfaces of R. If an intersection is found, then [e,//
// a] is a crossing edge. Otherwise, the edge [a,d] must be a crossing edge //
// of R. NOTE: This statement is correct if the input PLC is correct. We can //
// also detect the incorrectness of the input, e.g., if [a] only touches a //
// subface of R. The second approach is implemented here. It is slow, but is //
// more robust. //
// //
// 'crosstets' returns the set of crossing tets. Every tet in it has the //
// form [d,e,#,#] where [d,e] is a crossing edge, and d lies below R. The //
// set of tets form the cavity C, which is divided into two parts by R, one //
// at top and one at bottom. 'topfaces' and 'botfaces' return the upper and //
// lower boundary faces of C. 'toppoints' contains vertices of 'crosstets' //
// in the top part of C, and so does 'botpoints'. Both 'toppoints' and //
// 'botpoints' contain vertices of R. //
// //
// NOTE: 'toppoints' may contain points which are not vertices of any top //
// faces, and so may 'botpoints'. Such points may belong to other facets and //
// need to be present after the recovery of this cavity (P1029.poly). //
// //
// A pair of boundary faces: 'firsttopface' and 'firstbotface', are saved. //
// They share the same edge in the boundary of the missing region. //
// //
// Important: This routine assumes all vertices of the facet containing this //
// subface are marked, i.e., pmarktested(p) returns true. //
// //
///////////////////////////////////////////////////////////////////////////////
bool tetgenmesh::formcavity(triface* searchtet, arraypool* missingshs,
arraypool* crosstets, arraypool* topfaces,
arraypool* botfaces, arraypool* toppoints,
arraypool* botpoints)
{
arraypool *crossedges, *testededges;
triface spintet, neightet, *parytet;
face checksh, *parysh = NULL;
face checkseg; // *paryseg;
point pa, pd, pe, *parypt;
enum interresult dir;
//REAL ori;
//REAL elen[3];
bool testflag, invalidflag;
int types[2], poss[4];
int i, j, k;
// Temporarily re-use 'topfaces' for all crossing edges.
crossedges = topfaces;
// Temporarily re-use 'botfaces' for all tested edges.
testededges = botfaces; // Only used by 'b->psc'.
if (b->verbose > 2) {
printf(" Form the cavity of missing region.\n");
}
missingsubfacecount += missingshs->objects;
// 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 corossing edge, d lies below R.
// NEITHER d NOR e is a vertex of R (!pmarktested).
// NOTE: hull tets may be collected. See fig/dump-cavity-case2a(b).lua.
// Make sure that neither d nor e is dummypoint.
for (i = 0; i < crossedges->objects; i++) {
// Get a crossing edge [d,e,#,#].
searchtet = (triface *) fastlookup(crossedges, i);
// Sort vertices into the bottom and top arrays.
pd = org(*searchtet);
assert(!pmarktested(pd)); // pd is not on R.
if (!pinfected(pd)) {
pinfect(pd);
botpoints->newindex((void **) &parypt);
*parypt = pd;
}
pe = dest(*searchtet);
assert(!pmarktested(pe)); // pe is not on R.
if (!pinfected(pe)) {
pinfect(pe);
toppoints->newindex((void **) &parypt);
*parypt = pe;
}
// All tets sharing this edge are crossing tets.
spintet = *searchtet;
while (1) {
if (!infected(spintet)) {
if (b->verbose > 3) {
printf(" Add a crossing tet (%d, %d, %d, %d)\n",
pointmark(org(spintet)), pointmark(dest(spintet)),
pointmark(apex(spintet)), pointmark(oppo(spintet)));
}
infect(spintet);
crosstets->newindex((void **) &parytet);
*parytet = spintet;
}
// 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) || b->psc) {
// There exists a crossing edge, either [e,a] or [a,d]. First check
// if the crossing edge has already be added. This is to check if
// a tetrahedron at this edge is marked.
testflag = true;
for (j = 0; j < 2 && testflag; j++) {
if (j == 0) {
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);
plane_pa = sorg(*parysh);
plane_pb = sdest(*parysh);
plane_pc = sapex(*parysh);
// Test if this face intersects [e,a].
if (tri_edge_test(plane_pa, plane_pb, plane_pc, pe, pa,
NULL, 1, types, poss)) {
// Found intersection. 'a' lies below R.
enext(spintet, neightet);
dir = (enum interresult) types[0];
if ((dir == ACROSSFACE) || (dir == ACROSSEDGE)) {
// A valid intersection.
} else {
// A non-valid intersection. Maybe a PLC problem.
invalidflag = 1;
}
break;
}
// Test if this face intersects [a,d].
if (tri_edge_test(plane_pa, plane_pb, plane_pc, pa, pd,
NULL, 1, types, poss)) {
// Found intersection. 'a' lies above R.
eprev(spintet, neightet);
dir = (enum interresult) types[0];
if ((dir == ACROSSFACE) || (dir == ACROSSEDGE)) {
// A valid intersection.
} else {
// A non-valid intersection. Maybe a PLC problem.
invalidflag = 1;
}
break;
}
} // k
if (k < missingshs->objects) {
// Found a pair of triangle - edge interseciton.
if (invalidflag) {
if (b->verbose > 2) {
printf(" A non-valid subface - edge intersection\n");
printf(" subface: (%d, %d, %d) edge: (%d, %d)\n",
pointmark(plane_pa), pointmark(plane_pb),
pointmark(plane_pc), pointmark(org(neightet)),
pointmark(dest(neightet)));
}
// It may be a PLC problem.
terminatetetgen(3);
} else if (b->psc) {
if (pmarktested(pa)) {
// The intersection is invalid.
if (b->verbose > 2) {
printf(" A non-valid subface - edge intersection\n");
printf(" subface: (%d, %d, %d) edge: (%d, %d)\n",
pointmark(plane_pa), pointmark(plane_pb),
pointmark(plane_pc), pointmark(org(neightet)),
pointmark(dest(neightet)));
}
// Split the subface intersecting this edge.
recentsh = *parysh;
recenttet = neightet; // For point location.
invalidflag = 1;
break;
} // if (pmarktested(pa))
} // if (b->psc)
// Adjust the edge direction, so that its origin lies below R,
// and its destination lies above R.
esymself(neightet);
// Check if this edge is a segment.
tsspivot1(neightet, checkseg);
if (checkseg.sh != NULL) {
// Invalid PLC!
reportselfintersect(&checkseg, parysh);
terminatetetgen(3);
}
if (b->verbose > 3) {
printf(" Add a crossing edge (%d, %d)\n",
pointmark(org(neightet)), pointmark(dest(neightet)));
}
// Mark this edge to avoid testing it again.
markedge(neightet);
crossedges->newindex((void **) &parytet);
*parytet = neightet;
} else {
// No intersection is found. It may be a PLC problem.
//assert(b->psc);
// Mark this edge to avoid testing it again.
//markedge(neightet);
//testededges->newindex((void **) &parytet);
//*parytet = neightet;
invalidflag = 1;
// Split the subface intersecting [d,e].
for (k = 0; k < missingshs->objects; k++) {
parysh = (face *) fastlookup(missingshs, k);
plane_pa = sorg(*parysh);
plane_pb = sdest(*parysh);
plane_pc = sapex(*parysh);
// Test if this face intersects [e,a].
if (tri_edge_test(plane_pa, plane_pb, plane_pc, pd, pe,
NULL, 1, types, poss)) {
break;
}
} // k
assert(k < missingshs->objects);
recentsh = *parysh;
recenttet = spintet; // For point location.
break; // the while (1) loop
} // if (k == missingshs->objects)
} // if (testflag)
} // if (!pmarktested(pa) || b->psc)
}
// Go to the next crossing tet.
fnextself(spintet);
if (spintet.tet == searchtet->tet) break;
} // while (1)
//if (b->psc) {
if (invalidflag) break;
//}
} // i
if (b->verbose > 2) {
printf(" Formed cavity: %ld (%ld) cross tets (edges).\n",
crosstets->objects, crossedges->objects);
}
crossingtetcount += crosstets->objects;
// Unmark all marked edges.
for (i = 0; i < crossedges->objects; i++) {
searchtet = (triface *) fastlookup(crossedges, i);
assert(edgemarked(*searchtet)); // SELF_CHECK
unmarkedge(*searchtet);
}
crossedges->restart();
if (b->psc) {
// Unmark all marked edges.
for (i = 0; i < testededges->objects; i++) {
searchtet = (triface *) fastlookup(testededges, i);
assert(edgemarked(*searchtet)); // SELF_CHECK
unmarkedge(*searchtet);
}
testededges->restart();
} else { // only p->plc
assert(testededges->objects == 0l);
}
if (invalidflag) {
// Unmark all collected tets.
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();
return false;
}
// Find a pair of cavity boundary faces from the top and bottom sides of
// the facet each, and they share the same edge. Save them in the
// global variables: firsttopface, firstbotface. They will be used in
// fillcavity() for gluing top and bottom new tets.
for (i = 0; i < crosstets->objects; i++) {
searchtet = (triface *) fastlookup(crosstets, i);
// Crosstet is [d,e,a,b].
enextesym(*searchtet, spintet);
eprevself(spintet); // spintet is [b,a,e,d]
fsym(spintet, neightet); // neightet is [a,b,e,#]
if (!infected(neightet)) {
// A top face.
firsttopface = neightet;
} else {
continue; // Go to the next cross tet.
}
eprevesym(*searchtet, spintet);
enextself(spintet); // spintet is [a,b,d,e]
fsym(spintet, neightet); // neightet is [b,a,d,#]
if (!infected(neightet)) {
// A bottom face.
firstbotface = neightet;
} else {
continue;
}
break;
} // i
assert(i < crosstets->objects); // SELF_CHECK
// Collect the top and bottom faces and the middle vertices. Since all top
// and bottom vertices have been infected. Uninfected vertices must be
// 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].
enextesym(*searchtet, spintet);
eprevself(spintet); // spintet is [b,a,e,d]
fsym(spintet, neightet); // neightet is [a,b,e,#]
if (!infected(neightet)) {
// A top face.
topfaces->newindex((void **) &parytet);
*parytet = neightet;
}
eprevesym(*searchtet, spintet);
enextself(spintet); // spintet is [a,b,d,e]
fsym(spintet, neightet); // neightet is [b,a,d,#]
if (!infected(neightet)) {
// A bottom face.
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;
}
///////////////////////////////////////////////////////////////////////////////
// //
// delaunizecavity() Fill a cavity by Delaunay tetrahedra. //
// //
// The cavity C to be tetrahedralized is the top or bottom part of a whole //
// cavity. 'cavfaces' contains the boundary faces of C. NOTE: faces in 'cav- //
// faces' do not form a closed polyhedron. The "open" side are subfaces of //
// the missing facet. These faces will be recovered later in fillcavity(). //
// //
// This routine first constructs the DT of the vertices. Then it identifies //
// the half boundary faces of the cavity in DT. Possiblely the cavity C will //
// be enlarged. //
// //
// The DT is returned in 'newtets'. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::delaunizecavity(arraypool *cavpoints, arraypool *cavfaces,
arraypool *cavshells, arraypool *newtets,
arraypool *crosstets, arraypool *misfaces)
{
triface searchtet, neightet, spintet, *parytet, *parytet1;
face checksh, tmpsh, *parysh;
face checkseg;
point pa, pb, pc, pd, pt[3], *parypt;
enum interresult dir;
insertvertexflags ivf;
REAL ori; //, ang, len;
long baknum, bakhullsize;
int bakchecksubsegflag, bakchecksubfaceflag;
//int iloc;
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 do unifypoint();
// Get four non-coplanar points (no dummypoint).
parytet = (triface *) fastlookup(cavfaces, 0);
pa = org(*parytet);
pb = dest(*parytet);
pc = apex(*parytet);
pd = NULL;
for (i = 1; i < cavfaces->objects; i++) {
parytet = (triface *) fastlookup(cavfaces, i);
pt[0] = org(*parytet);
pt[1] = dest(*parytet);
pt[2] = apex(*parytet);
for (j = 0; j < 3; j++) {
if (pt[j] != dummypoint) { // Do not include a hull point.
// if (!pinfected(pt[j])) {
ori = orient3d(pa, pb, pc, pt[j]);
if (ori != 0) {
pd = pt[j];
if (ori > 0) { // Swap pa and pb.
pt[j] = pa; pa = pb; pb = pt[j];
}
break;
}
// }
}
}
if (pd != NULL) break;
}
assert(i < cavfaces->objects); // SELF_CHECK
// 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);
assert(pt[0] != dummypoint); // SELF_CHECK
searchtet = recenttet;
ivf.iloc = (int) OUTSIDE;
ivf.bowywat = 1;
insertvertex(pt[0], &searchtet, NULL, NULL, &ivf);
}
if (b->verbose > 2) {
printf(" Identfying %ld boundary faces of the cavity.\n",
cavfaces->objects);
}
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;
// This face may contain dummypoint (See fig/dum-cavity-case2).
// If so, dummypoint must be its apex.
j = (parytet->ver & 3); // j is the face number.
parytet->ver = epivot[j]; // [4,5,2,11]
pt[0] = org(*parytet);
pt[1] = dest(*parytet);
pt[2] = apex(*parytet);
// Create a temp subface.
makeshellface(subfaces, &tmpsh);
setshvertices(tmpsh, pt[0], pt[1], pt[2]);
// Insert tmpsh in DT.
searchtet.tet = NULL;
dir = scoutsubface(&tmpsh, &searchtet);
if (dir == SHAREFACE) {
// Inserted. Make sure that tmpsh connects an interior tet of C.
stpivot(tmpsh, neightet);
// neightet and tmpsh refer to the same edge [pt[0], pt[1]].
// If the origin of neightet is pt[1], it is inside.
if (org(neightet) != pt[1]) {
fsymself(neightet);
assert(org(neightet) == pt[1]); // SELF_CHECK
// Make sure that tmpsh is connected with an interior tet.
sesymself(tmpsh);
tsbond(neightet, tmpsh);
}
assert(dest(neightet) == pt[0]); // SELF_CHECK
} else if (dir == COLLISIONFACE) {
// This case is not possible anymore. 2010-02-01
assert(0);
} else {
if (b->verbose > 2) {
printf(" bdry face (%d, %d, %d) -- %d is missing\n",
pointmark(pt[0]), pointmark(pt[1]), pointmark(pt[2]), i);
}
shellfacedealloc(subfaces, tmpsh.sh);
// Save this face in list.
misfaces->newindex((void **) &parytet1);
*parytet1 = *parytet;
continue;
}
// Remember the boundary tet (outside the cavity) in tmpsh
// (use the adjacent tet slot).
tmpsh.sh[0] = (shellface) encode(*parytet);
// Save this subface.
cavshells->newindex((void **) &parysh);
*parysh = tmpsh;
} // i
if (misfaces->objects > 0) {
if (b->verbose > 2) {
printf(" Enlarging the cavity. %ld missing bdry faces\n",
misfaces->objects);
}
// Removing all tempoaray 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;
ivf.bowywat = 1;
insertvertex(pd, &searchtet, NULL, NULL, &ivf);
if (b->verbose > 2) {
printf(" Add point %d into list.\n", pointmark(pd));
}
pinfect(pd);
cavpoints->newindex((void **) &parypt);
*parypt = pd;
}
// Add three opposite faces into the boundary list.
for (j = 0; j < 3; j++) {
esym(*parytet, neightet);
fsymself(neightet);
if (!infected(neightet)) {
if (b->verbose > 2) {
printf(" Add a cavface (%d, %d, %d).\n",
pointmark(org(neightet)), pointmark(dest(neightet)),
pointmark(apex(neightet)));
}
cavfaces->newindex((void **) &parytet1);
*parytet1 = neightet;
} else {
}
enextself(*parytet);
} // j
} // if (!infected(parytet))
} // 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 (searchtet.ver = 0; searchtet.ver < 4; searchtet.ver++) {
fsym(searchtet, neightet);
if (!marktested(neightet)) {
marktest(neightet);
newtets->newindex((void **) &parytet);
*parytet = neightet;
}
}
}
cavpoints->restart();
cavfaces->restart();
if (cavshells->objects > maxcavsize) {
maxcavsize = cavshells->objects;
}
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;
}
///////////////////////////////////////////////////////////////////////////////
// //
// fillcavity() Fill new tets into the cavity. //
// //
// The new tets are stored in two disjoint sets(which share the same facet). //
// 'topfaces' and 'botfaces' are the boundaries of these two sets, respect- //
// ively. 'midfaces' is empty on input, and will store faces in the facet. //
// //
// Important: This routine assumes all vertices of the missing region R are //
// marktested, i.e., pmarktested(p) returns true. //
// //
///////////////////////////////////////////////////////////////////////////////
bool tetgenmesh::fillcavity(arraypool* topshells, arraypool* botshells,
arraypool* midfaces, arraypool* missingshs)
{
arraypool *cavshells;
triface *parytet, bdrytet, toptet, bottet, midface;
triface neightet, spintet;
face checksh, *parysh;
face checkseg;
point pa, pb, pc, pf, pg; //, *pts;
int types[2], poss[4];
//REAL elen[3]; //ori, len, n[3];
bool mflag, bflag;
int i, j, k;
// Connect newtets to tets outside the cavity. These connections are needed
// for identifying the middle faces (which belong to R).
for (k = 0; k < 2; k++) {
cavshells = (k == 0 ? topshells : botshells);
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.
decode(parysh->sh[0], bdrytet);
pa = org(bdrytet);
pb = dest(bdrytet);
pc = apex(bdrytet);
// Get the adjacent new tet.
stpivot(*parysh, neightet);
assert(org(neightet) == pb); // SELF_CHECK
assert(dest(neightet) == pa); // SELF_CHECK
// Mark neightet as an interior tet of this cavity, 2009-04-24.
// Comment: We know neightet is an interior tet.
if (!infected(neightet)) {
infect(neightet);
}
assert(oppo(bdrytet) != NULL); // No faked tet.
// if (oppo(bdrytet) != NULL) {
// Bond the two tets.
bond(bdrytet, neightet); // Also cleared the pointer to tmpsh.
// }
tsdissolve(neightet); // Clear the pointer to tmpsh.
// Update the point-to-tets map.
setpoint2tet(pa, encode(neightet));
setpoint2tet(pb, encode(neightet));
setpoint2tet(pc, encode(neightet));
// Delete the temp subface.
// shellfacedealloc(subfacepool, parysh->sh);
} // i
} // if (cavshells != NULL)
} // k
mflag = true; // Initialize it.
if (midfaces != NULL) {
// The first pair of top and bottom tets share the same edge [a, b].
// toptet = * (triface *) fastlookup(topfaces, 0);
if (infected(firsttopface)) {
// This is due to he enlargement of the cavity. Find the updated top
// boundary face at edge [a,b].
// Comment: An uninfected tet at [a,b] should be found since [a,b] is a
// boundary edge of the missing region R. It should not be enclosed
// by the enlarged cavity.
pa = apex(firsttopface); // SELF_CHECK
while (1) {
fnextself(firsttopface);
if (!infected(firsttopface)) break;
assert(apex(firsttopface) != pa); // SELF_CHECK
}
}
toptet = firsttopface;
pa = apex(toptet);
fsymself(toptet);
// Search a subface from the top mesh.
while (1) {
esymself(toptet); // The next face in the same tet.
pc = apex(toptet);
assert(pc != pa); // We should not return to the starting point.
if (pmarktested(pc)) break; // [a,b,c] is a subface.
fsymself(toptet); // Go to the adjacent tet.
}
// Search the subface [a,b,c] in the bottom mesh.
// bottet = * (triface *) fastlookup(botfaces, 0);
if (infected(firstbotface)) {
pa = apex(firstbotface); // SELF_CHECK
while (1) {
fnextself(firstbotface);
if (!infected(firstbotface)) break;
assert(apex(firstbotface) != pa); // SELF_CHECK
}
}
bottet = firstbotface;
pa = apex(bottet);
fsymself(bottet);
while (1) {
esymself(bottet); // The next face in the same tet.
pf = apex(bottet);
assert(pf != pa); // We should not return to the starting point.
if (pf == pc) break; // Face matched.
if (pmarktested(pf)) {
mflag = false; break; // Not matched.
}
fsymself(bottet);
}
if (mflag) {
// Connect the two tets together.
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;
}
// Match pairs of subfaces (middle faces), connect top and bottom tets.
for (i = 0; i < midfaces->objects && mflag; i++) {
// Get a matched middle face [a, b, c]
midface = * (triface *) fastlookup(midfaces, i);
// The tet must be a new created tet (marktested).
assert(marktested(midface)); // SELF_CHECK
// Check the neighbors at edges [b, c] and [c, a].
for (j = 0; j < 2 && mflag; j++) {
enextself(midface); // [b, c] or [c, a].
pg = apex(midface);
toptet = midface;
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) {
break; // Find a subface.
}
// Go to the adjacent tet.
fsymself(toptet);
// Do we walk outside the cavity?
if (!marktested(toptet)) {
// Yes, the adjacent face is not a middle face.
bflag = true; break;
}
}
if (!bflag) {
// assert(marktested(toptet)); // SELF_CHECK
if (!facemarked(toptet)) {
fsym(midface, bottet);
while (1) {
esymself(bottet);
pf = apex(bottet);
if (pf == pc) break; // Face matched.
if (pmarktested(pf)) {
mflag = false; break; // Not matched
}
fsymself(bottet);
}
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!
// This case can happen when the cavity was enlarged, and the
// 'toptet' is a co-facet (sub)face adjacent to the missing
// region, and it is a boundary face of the top cavity.
// So the toptet and bottet should be bonded already through
// a temp subface. See fig/dump-cavity-case18. Check it.
fsym(toptet, neightet);
assert(neightet.tet == bottet.tet); // SELF_CHECK
assert(neightet.ver == bottet.ver); // SELF_CHECK
// Do not add this face into 'midfaces'.
}
}
} // if (!facemarked(toptet))
}
} // j
} // i
} // if (midfaces != NULL)
if (mflag) {
if (midfaces != NULL) {
if (b->verbose > 2) {
printf(" Found %ld middle subfaces.\n", midfaces->objects);
}
if (midfaces->objects > maxregionsize) {
maxregionsize = midfaces->objects;
}
// Unmark middle faces.
for (i = 0; i < midfaces->objects; i++) {
// Get a matched middle face [a, b, c]
midface = * (triface *) fastlookup(midfaces, i);
assert(facemarked(midface)); // SELF_CHECK
unmarkface(midface);
}
}
} else {
// Faces at top and bottom are not matched. There exists non-Delaunay
// subedges. See fig/dump-cavity-case5.lua.
pa = org(toptet);
pb = dest(toptet);
pc = apex(toptet);
pf = apex(bottet);
pf = oppo(toptet);
pg = oppo(bottet);
// Find a subface in R which intersects the edge [f,g].
for (i = 0; i < missingshs->objects; i++) {
parysh = (face *) fastlookup(missingshs, i);
pa = sorg(*parysh);
pb = sdest(*parysh);
pc = sapex(*parysh);
if (tri_edge_test(pa, pb, pc, pf, pg, NULL, 1, types, poss)) {
// Found a subface.
break;
}
}
if (i < missingshs->objects) {
// Such subface exist.
recentsh = *parysh;
} else {
assert(0); // Debug this case.
}
// Set a tet for searching the new point.
recenttet = firsttopface;
}
// Delete the temp subfaces.
for (k = 0; k < 2; k++) {
cavshells = (k == 0 ? topshells : botshells);
if (cavshells != NULL) {
for (i = 0; i < cavshells->objects; i++) {
parysh = (face *) fastlookup(cavshells, i);
shellfacedealloc(subfaces, parysh->sh);
}
}
}
topshells->restart();
if (botshells != NULL) {
botshells->restart();
}
if (midfaces != NULL) {
midfaces->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;
triface *parytet, *pnewtet, newtet, neightet, spintet;
face checksh, *parysh;
face checkseg, *paryseg;
int i, j, k;
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);
assert(infected(*parytet)); // SELF_CHECK
for (parytet->ver = 0; parytet->ver < 4; parytet->ver++) {
tspivot(*parytet, checksh);
if (checksh.sh != NULL) {
if (!sinfected(checksh)) {
sinfect(checksh);
cavetetshlist->newindex((void **) &parysh);
*parysh = checksh;
}
}
}
for (j = 0; j < 6; j++) {
parytet->ver = edge2ver[j];
tsspivot1(*parytet, checkseg);
if (checkseg.sh != NULL) {
if (!sinfected(checkseg)) {
sinfect(checkseg);
cavetetseglist->newindex((void **) &paryseg);
*paryseg = checkseg;
}
}
}
} // i
// Uninfect collected subfaces.
for (i = 0; i < cavetetshlist->objects; i++) {
checksh = * (face *) fastlookup(cavetetshlist, i);
suninfect(checksh);
}
// Uninfect collected segments.
for (i = 0; i < cavetetseglist->objects; i++) {
checkseg = * (face *) fastlookup(cavetetseglist, i);
suninfect(checkseg);
}
// 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;
if (b->verbose > 2) {
printf(" Found an interior subface (%d, %d, %d)\n",
pointmark(sorg(checksh)), pointmark(sdest(checksh)),
pointmark(sapex(checksh)));
}
stdissolve(checksh);
caveencshlist->newindex((void **) &parysh);
*parysh = checksh;
}
}
if (!infected(neightet)) {
// Found an outside tet. Re-connect this subface to a new tet.
fsym(neightet, newtet);
assert(marktested(newtet)); // It's a new tet.
sesymself(*parysh);
tsbond(newtet, *parysh);
}
} // i
if (b->verbose > 2) {
printf(" %ld (%ld) cavity (interior) subfaces.\n",
cavetetshlist->objects, caveencshlist->objects);
}
for (i = 0; i < cavetetseglist->objects; i++) {
checkseg = * (face *) fastlookup(cavetetseglist, i);
// 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) {
if (b->verbose > 2) {
printf(" Found an interior seg (%d, %d)\n",
pointmark(sorg(checkseg)), pointmark(sdest(checkseg)));
}
sstdissolve1(checkseg);
caveencseglist->newindex((void **) &paryseg);
*paryseg = checkseg;
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 (b->verbose > 2) {
printf(" %ld (%ld) cavity (interior) segments.\n",
cavetetseglist->objects, caveencseglist->objects);
}
cavetetshlist->restart();
cavetetseglist->restart();
// Delete the old tets in cavity.
for (i = 0; i < crosstets->objects; i++) {
parytet = (triface *) fastlookup(crosstets, i);
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 (k = 0; k < 2; k++) {
newtets = (k == 0 ? topnewtets : botnewtets);
if (newtets != NULL) {
for (i = 0; i < newtets->objects; i++) {
parytet = (triface *) fastlookup(newtets, i);
if (infected(*parytet)) {
crosstets->newindex((void **) &pnewtet);
*pnewtet = *parytet;
}
} // i
}
} // k
// Now we collect all new tets in cavity.
for (i = 0; i < crosstets->objects; i++) {
parytet = (triface *) fastlookup(crosstets, i);
if (i == 0) {
recenttet = *parytet; // Remember a live handle.
}
for (j = 0; j < 4; j++) {
decode(parytet->tet[j], neightet);
if (marktested(neightet)) { // Is it a new tet?
if (!infected(neightet)) {
// Find an interior tet.
assert((point) neightet.tet[7] != dummypoint); // SELF_CHECK
infect(neightet);
crosstets->newindex((void **) &pnewtet);
*pnewtet = neightet;
}
}
} // j
} // i
// Delete outer new tets.
for (k = 0; k < 2; k++) {
newtets = (k == 0 ? topnewtets : botnewtets);
if (newtets != NULL) {
for (i = 0; i < newtets->objects; i++) {
parytet = (triface *) fastlookup(newtets, i);
if (infected(*parytet)) {
// This is an interior tet.
uninfect(*parytet);
unmarktest(*parytet);
} 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)
{
triface *parytet, neightet;
face checksh;
face checkseg;
point *ppt;
int i, j;
// Reconnect crossing tets to cavity boundary.
for (i = 0; i < crosstets->objects; i++) {
parytet = (triface *) fastlookup(crosstets, i);
assert(infected(*parytet)); // SELF_CHECK
if (i == 0) {
recenttet = *parytet; // Remember a live handle.
}
parytet->ver = 0;
for (parytet->ver = 0; parytet->ver < 4; parytet->ver++) {
fsym(*parytet, neightet);
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);
}
// 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)
{
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]);
assert(insph != 0);
if (insph > 0) {
// Add the face into queue.
if (b->verbose > 2) {
printf(" A locally non-Delanay face (%d, %d, %d)-%d,%d\n",
pointmark(p[0]), pointmark(p[1]), pointmark(p[2]),
pointmark(p[3]), pointmark(p[4]));
}
parybf = (badface *) flippool->alloc();
parybf->key = 0.; // tau = 0, do immediately.
parybf->tt = *chkface;
parybf->forg = p[0];
parybf->fdest = p[1];
parybf->fapex = p[2];
parybf->foppo = p[3];
parybf->noppo = p[4];
// Add it at the top of the priority queue.
if (*pqueue == NULL) {
*pqueue = parybf;
parybf->nextitem = NULL;
} else {
parybf->nextitem = *pqueue;
*pqueue = parybf;
}
} // if (insph > 0)
} // if (checksh.sh == NULL)
}
//return;
}
return; // Test: omit this face.
}
// 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] = orient3d(plane_pa, plane_pb, plane_pc, p[i]);
if (w[i] < 0) w[i] = -w[i];
assert(w[i] != 0);
} else {
w[i] = 0;
}
}
// Make sure orient3d(p[1], p[0], p[2], p[3]) > 0;
// Hence if (insphere(p[1], p[0], p[2], p[3], p[4]) > 0) means that
// p[4] lies inside the circumsphere of p[1], p[0], p[2], p[3].
// The same if orient4d(p[1], p[0], p[2], p[3], p[4]) > 0 means that
// p[4] lies below the oriented hyperplane passing through
// p[1], p[0], p[2], p[3].
insph = insphere(p[1], p[0], p[2], p[3], p[4]);
if (b->flipinsert_ori4dexact) {
ori4 = orient4dexact(p[1], p[0], p[2], p[3], p[4],w[1],w[0],w[2],w[3],w[4]);
} else {
ori4 = orient4d(p[1], p[0], p[2], p[3], p[4], w[1], w[0], w[2], w[3], w[4]);
}
if (b->verbose > 2) {
printf(" Heights: (%g, %g, %g, %g, %g)\n", w[0],w[1],w[2],w[3],w[4]);
printf(" Insph: %g, ori4: %g, tau = %g\n", insph, ori4, -insph/ori4);
}
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) // -L1
// 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[5], baktets[2], fliptet, newface;
triface neightet, *parytet;
face checksh;
face checkseg;
badface *pqueue;
badface *popbf, bface;
point p1, p2, pd, pe;
point *parypt;
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);
}
if (crossfaces->objects > maxcrossfacecount) {
maxcrossfacecount = 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);
}
crossfaces->restart();
// The list for temporarily storing unflipable faces.
bfacearray = new arraypool(sizeof(triface), 4);
fliploop:
fcount = 0; // Count the number of flips.
// Flip insert the facet.
while (pqueue != NULL) {
// Pop a face from the priotity 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);
assert(!isdeadtet(neightet));
if (oppo(neightet) == bface.noppo) {
pd = oppo(fliptet);
pe = oppo(neightet);
if (b->verbose > 2) {
printf(" Get face (%d, %d, %d) - %d, %d, tau = %.17g\n",
pointmark(bface.forg), pointmark(bface.fdest),
pointmark(bface.fapex), pointmark(bface.foppo),
pointmark(bface.noppo), bface.key);
}
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.
// The face should not be a subface.
tspivot(fliptet, checksh);
assert(checksh.sh == NULL);
fliptets[0] = fliptet; // abcd, d may be the new vertex.
fliptets[1] = neightet; // bace.
flip23(fliptets, 1, 0, 0);
// Put the link faces into check list.
for (i = 0; i < 3; i++) {
eprevesym(fliptets[i], newface);
crossfaces->newindex((void **) &parytet);
*parytet = newface;
}
for (i = 0; i < 3; i++) {
enextesym(fliptets[i], newface);
crossfaces->newindex((void **) &parytet);
*parytet = newface;
}
flipflag = 1;
} else if (convcount == 2) {
assert(copcount <= 1);
//if (copcount <= 1) {
// A 3-to-2 or 4-to-4 may be possible.
// Get the edge which is locally non-convex or flat.
for (i = 0; i < 3; i++) {
if (ori[i] <= 0) break;
enextself(fliptet);
}
// The edge should not be a segment.
tsspivot1(fliptet, checkseg);
assert(checkseg.sh == NULL);
// Collect tets sharing at this edge.
esym(fliptet, fliptets[0]); // [b,a,d,c]
n = 0;
do {
fnext(fliptets[n], fliptets[n + 1]);
n++;
} while ((fliptets[n].tet != fliptet.tet) && (n < 5));
if (n == 3) {
// Found a 3-to-2 flip.
flip32(fliptets, 1, 0, 0);
// 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, 0, 0);
// Put the "outer" link faces into check list.
// fliptets[0] = [e,d,a,b] => will be flipped, so
// [a,b,d] and [a,b,e] are not "outer" link faces.
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, 0, 0);
// 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 {
//n == 4, convflag != 0; assert(0);
}
} else {
// n > 4 => unflipable. //assert(0);
}
} 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);
}
dbg_ignore_facecount++;
} // if (convcount == 1)
if (flipflag == 1) {
// Update the priority queue.
for (i = 0; i < crossfaces->objects; i++) {
parytet = (triface *) fastlookup(crossfaces, i);
flipcertify(parytet, &pqueue);
}
crossfaces->restart();
if (!b->flipinsert_random) {
// Insert all queued unflipped faces.
for (i = 0; i < bfacearray->objects; i++) {
parytet = (triface *) fastlookup(bfacearray, i);
// This face may be changed.
if (!isdeadtet(*parytet)) {
flipcertify(parytet, &pqueue);
}
}
bfacearray->restart();
}
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);
assert(0);
}
if (b->flipinsert_random) {
// Insert all queued unflipped faces.
for (i = 0; i < bfacearray->objects; i++) {
parytet = (triface *) fastlookup(bfacearray, i);
// This face may be changed.
if (!isdeadtet(*parytet)) {
flipcertify(parytet, &pqueue);
}
}
bfacearray->restart();
goto fliploop;
}
}
// 'bfacearray' may be not empty (for what reason ??).
dbg_unflip_facecount += bfacearray->objects;
assert(flippool->items == 0l);
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 (totalfcount > maxflipsequence) {
maxflipsequence = totalfcount;
}
if (b->verbose > 2) {
printf(" Total %ld flips. f23(%ld), f32(%ld), f44(%ld).\n",
totalfcount, f23count, f32count, f44count);
}
}
///////////////////////////////////////////////////////////////////////////////
// //
// fillregion() Fill the missing region by a set of new subfaces. //
// //
// 'missingshs' contains the list of subfaces in R. Moreover, each subface //
// (except the first one) in this list represents an interior edge of R. //
// Note: All subfaces in R are smarktested. //
// //
// Note: We assume that all vertices of R are marktested so we can detect //
// new subface by checking the flag in apexes. //
// //
///////////////////////////////////////////////////////////////////////////////
bool tetgenmesh::fillregion(arraypool* missingshs, arraypool* missingshbds,
arraypool* newshs)
{
badface *newflipface, *popface;
triface searchtet, spintet;
face oldsh, newsh, opensh, *parysh;
face casout, casin, neighsh, checksh;
face checkseg, fakeseg;
point pc, pd, pe, pf, ppt[2];
enum interresult dir;
REAL n[3], len; // elen[3];
bool insideflag;
int types[2], poss[4];
int i, j, k;
if (b->verbose > 2) {
printf(" Fill region: %ld old subfaces (%ld).\n", missingshs->objects,
fillregioncount);
}
// Search the first constrained face of R. It is found from the set of
// faces sharing at a boundary edge [a,b]. Such face must be found.
// The search takes the following two steps:
// - First, finds a candidate face [a,b,c] where c is also a vertex of R;
// Note that [a,b,c] may not be the right face to fill R. For instance,
// when R is concave at b.
// - Second, check if [a,b,c] can fill R. This can be checked if an
// adjacent tet of [a,b,c] intersects R. This is a tetrahedron-triangle
// intersection test. It can be reduced to two triangle-edge intersect
// tests, i.e., intersect the two faces not containing the edge [a,b] in
// this tet with all interior edges of R.
// We start from the first boundary edge of R.
oldsh = * (face *) fastlookup(missingshbds, 0);
ppt[0] = sorg(oldsh);
ppt[1] = sdest(oldsh);
point2tetorg(ppt[0], searchtet);
dir = finddirection(&searchtet, ppt[1], 0);
assert(dir == ACROSSVERT); // SELF_CHECK
insideflag = false;
// Each face has two adjacent tets.
for (k = 0; k < 2; k++) {
if (b->verbose > 2) {
printf(" Search an interior face from edge (%d, %d).\n",
pointmark(ppt[0]), pointmark(ppt[1]));
}
spintet = searchtet;
while (1) {
pc = apex(spintet);
if (pmarktested(pc)) {
// Found a candidate face. Check if it is inside R.
if (missingshs->objects > 2l) {
// pd = oppo(spintet);
// if (pd == dummypoint) {
// Calculate an above point for this subface.
facenormal(ppt[0], ppt[1], pc, n, 1, NULL);
len = sqrt(DOT(n, n));
n[0] /= len;
n[1] /= len;
n[2] /= len;
len = DIST(ppt[0], ppt[1]);
len += DIST(ppt[1], pc);
len += DIST(pc, ppt[0]);
len /= 3.0;
dummypoint[0] = ppt[0][0] + len * n[0];
dummypoint[1] = ppt[0][1] + len * n[1];
dummypoint[2] = ppt[0][2] + len * n[2];
pd = dummypoint;
// }
//if (pd != dummypoint) {
for (j = 0; j < 2 && !insideflag; j++) {
for (i = 1; i < missingshs->objects && !insideflag; i++) {
parysh = (face *) fastlookup(missingshs, i);
// Get an interior edge of R.
pe = sorg(*parysh);
pf = sdest(*parysh);
if (tri_edge_test(ppt[j],pc,pd,pe,pf,NULL,1,types,poss)) {
dir = (enum interresult) types[0];
if (dir == ACROSSFACE) {
searchtet = spintet;
insideflag = true;
} else if (dir == ACROSSEDGE) {
searchtet = spintet;
insideflag = true;
}
}
} // i
} // j
// }
// if (pd == dummypoint) {
dummypoint[0] = 0;
dummypoint[1] = 0;
dummypoint[2] = 0;
// }
} else {
// It is a simple 2-to-2 flip.
searchtet = spintet;
insideflag = true;
}
} // if (pmarktested(pc))
if (insideflag) break;
fnextself(spintet);
if (spintet.tet == searchtet.tet) break;
} // while (1)
if (insideflag) break;
esymself(searchtet);
ppt[0] = org(searchtet);
ppt[1] = dest(searchtet);
} // k
if (!insideflag) {
// Something strange is happening.
// Refine the missing region by adding a Steiner point.
recentsh = oldsh;
recenttet = searchtet; // For point location.
return false;
}
// Create a new subface at the boundary edge.
if (b->verbose > 2) {
printf(" Create a new subface (%d, %d, %d)\n", pointmark(ppt[0]),
pointmark(ppt[1]), pointmark(pc));
}
makeshellface(subfaces, &newsh);
setsorg(newsh, ppt[0]);
setsdest(newsh, ppt[1]);
setsapex(newsh, pc);
// The new subface gets its markers from the old one.
setshellmark(newsh, shellmark(oldsh));
if (checkconstraints) {
setareabound(newsh, areabound(oldsh));
}
// Connect the new subface to adjacent tets.
tspivot(searchtet, checksh); // SELF_CHECK
assert(checksh.sh == NULL); // SELF_CHECK
tsbond(searchtet, newsh);
fsymself(searchtet);
sesymself(newsh);
tsbond(searchtet, newsh);
// Connect newsh to outer subfaces.
sspivot(oldsh, checkseg);
spivot(oldsh, casout);
if (casout.sh != NULL) {
casin = casout;
if (checkseg.sh != NULL) {
// Make sure that the subface has the right ori at the segment.
checkseg.shver = 0;
if (sorg(newsh) != sorg(checkseg)) {
sesymself(newsh);
}
spivot(casin, neighsh);
while (neighsh.sh != oldsh.sh) {
casin = neighsh;
spivot(casin, neighsh);
}
}
sbond1(newsh, casout);
sbond1(casin, newsh);
}
if (checkseg.sh != NULL) {
ssbond(newsh, checkseg);
}
// Add this new subface into list.
sinfect(newsh);
newshs->newindex((void **) &parysh);
*parysh = newsh;
// Push two "open" side of the new subface into stack.
for (i = 0; i < 2; i++) {
senextself(newsh);
newflipface = (badface *) flippool->alloc();
newflipface->ss = newsh;
newflipface->nextitem = flipstack;
flipstack = newflipface;
}
// Every other boundary edge of R is identified as a segment. Insert a faked
// segments at the place if it is not a segment.
for (i = 1; i < missingshbds->objects; i++) {
parysh = (face *) fastlookup(missingshbds, i);
ppt[0] = sorg(*parysh);
ppt[1] = sdest(*parysh);
point2tetorg(ppt[0], searchtet);
dir = finddirection(&searchtet, ppt[1], 0);
assert(dir == ACROSSVERT); // SELF_CHECK
tsspivot1(searchtet, checkseg);
if (checkseg.sh == NULL) {
// Insert a fake segment at this tet.
if (b->verbose > 2) {
printf(" Insert a fake segment (%d, %d)\n", pointmark(ppt[0]),
pointmark(ppt[1]));
}
makeshellface(subsegs, &fakeseg);
setsorg(fakeseg, ppt[0]);
setsdest(fakeseg, ppt[1]);
sinfect(fakeseg); // Mark it as faked.
// Connect it to all tets at this edge.
spintet = searchtet;
while (1) {
tssbond1(spintet, fakeseg);
fnextself(spintet);
if (spintet.tet == searchtet.tet) break;
}
checkseg = fakeseg;
}
// Let the segment hold the old subface.
checkseg.shver = 0;
sbond1(checkseg, *parysh);
// Remember it to free it later.
*parysh = checkseg;
}
// Loop until 'flipstack' is empty.
while (flipstack != NULL) {
// Pop an "open" side from the stack.
popface = flipstack;
opensh = popface->ss;
flipstack = popface->nextitem; // The next top item in stack.
flippool->dealloc((void *) popface);
// Process it if it is still open.
spivot(opensh, casout);
if (casout.sh == NULL) {
if (b->verbose > 2) {
printf(" Get an open side (%d, %d) - %d.\n",
pointmark(sorg(opensh)), pointmark(sdest(opensh)),
pointmark(sapex(opensh)));
}
// Search a neighbor to close this side.
stpivot(opensh, searchtet);
tsspivot1(searchtet, checkseg);
if (checkseg.sh == NULL) {
// No segment. It is inside R. Search for a new face to fill in R.
// Note that the face may not be found (see fig 2010-05-25-c).
spintet = searchtet;
fnextself(spintet); // Skip the current face.
while (1) {
pc = apex(spintet);
if (pmarktested(pc)) {
// Found a place for a new subface inside R -- Case (i).
tspivot(spintet, checksh);
if (checksh.sh == NULL) {
// Create a new subface.
if (b->verbose > 2) {
printf(" Create a new subface (%d, %d, %d)\n",
pointmark(org(spintet)), pointmark(dest(spintet)),
pointmark(pc));
}
makeshellface(subfaces, &newsh);
setsorg(newsh, org(spintet));
setsdest(newsh, dest(spintet));
setsapex(newsh, pc);
// The new subface gets its markers from its neighbor.
setshellmark(newsh, shellmark(opensh));
if (checkconstraints) {
setareabound(newsh, areabound(opensh));
}
// Connect the new subface to adjacent tets.
tsbond(spintet, newsh);
fsymself(spintet);
sesymself(newsh);
tsbond(spintet, newsh);
// Connect newsh to its adjacent subface.
sbond(newsh, opensh);
// Add this new subface into list.
sinfect(newsh);
newshs->newindex((void **) &parysh);
*parysh = newsh;
// Push two "open" side of the new subface into stack.
for (i = 0; i < 2; i++) {
senextself(newsh);
newflipface = (badface *) flippool->alloc();
newflipface->ss = newsh;
newflipface->nextitem = flipstack;
flipstack = newflipface;
}
} else {
// A new subface has already been created.
assert(sinfected(checksh)); // It must be in stack.
spivot(checksh, neighsh); // SELF_CHECK
assert(neighsh.sh == NULL); // Its side must be open.
if (b->verbose > 2) {
printf(" Connect to another open side (%d, %d, %d)\n",
pointmark(sorg(checksh)), pointmark(sdest(checksh)),
pointmark(sapex(checksh)));
}
sbond(opensh, checksh); // Simply connect them.
}
break; // -- Case (i)
}
fnextself(spintet);
if (spintet.tet == searchtet.tet) {
// Not find any face to fill in R at this side.
// TO DO: suggest a point to split the edge.
assert(0);
}
} // while (1)
} else {
// This side coincident with a boundary edge of R.
checkseg.shver = 0;
spivot(checkseg, oldsh);
if (sinfected(checkseg)) {
// It's a faked segment. Delete it.
if (b->verbose > 2) {
printf(" Delete a fake segment (%d, %d)\n",
pointmark(sorg(checkseg)), pointmark(sdest(checkseg)));
}
spintet = searchtet;
while (1) {
tssdissolve1(spintet);
fnextself(spintet);
if (spintet.tet == searchtet.tet) break;
}
shellfacedealloc(subsegs, checkseg.sh);
}
if (b->verbose > 2) {
printf(" Connect to a boundary edge (%d, %d, %d)\n",
pointmark(sorg(oldsh)), pointmark(sdest(oldsh)),
pointmark(sapex(oldsh)));
}
sspivot(oldsh, checkseg);
spivot(oldsh, casout);
if (casout.sh != NULL) {
casin = casout;
if (checkseg.sh != NULL) {
// Make sure that the subface has the right ori at the segment.
checkseg.shver = 0;
if (sorg(opensh) != sorg(checkseg)) {
sesymself(opensh);
}
spivot(casin, neighsh);
while (neighsh.sh != oldsh.sh) {
casin = neighsh;
spivot(casin, neighsh);
}
}
sbond1(opensh, casout);
sbond1(casin, opensh);
}
if (checkseg.sh != NULL) {
ssbond(opensh, checkseg);
}
}
} // if (casout.sh == NULL)
} // while (flipstack != NULL)
// Uninfect all new subfaces.
for (i = 0; i < newshs->objects; i++) {
parysh = (face *) fastlookup(newshs, i);
suninfect(*parysh);
}
if (b->verbose > 2) {
printf(" Created %ld new subfaces.\n", newshs->objects);
}
fillregioncount++;
return true;
}
///////////////////////////////////////////////////////////////////////////////
// //
// refineregion() Refine a missing region by inserting points. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::refineregion()
{
triface searchtet;
face splitsh;
face *paryseg, sseg;
point steinpt, pa, pb, pc;
insertvertexflags ivf;
REAL auv[2], buv[2], newuv[2], t;
int fmark, fid, eid;
int loc; // iloc, sloc;
int s, i;
// The mesh is a CDT.
assert(subsegstack->objects == 0l); // SELF_CHECK
// Create a new point.
makepoint(&steinpt, FREEFACETVERTEX);
// The 'recentsh' saved an edge to be split.
splitsh = recentsh;
// Add the Steiner point at the barycenter of the face.
pa = sorg(splitsh);
pb = sdest(splitsh);
pc = sapex(splitsh);
if (b->psc) {
assert(in->facetmarkerlist != NULL);
fmark = shellmark(splitsh) - 1;
fid = in->facetmarkerlist[fmark];
if (pointtype(pa) == RIDGEVERTEX) {
in->getvertexparamonface(in->geomhandle, pointmark(pa), fid, auv);
} else if (pointtype(pa) == FREESEGVERTEX) {
eid = pointgeomtag(pa); // The Edge containing this Steiner point.
t = pointgeomuv(pa, 0); // The Steiner point's parameter on Edge.
in->getedgesteinerparamonface(in->geomhandle, eid, t, fid, auv);
} else if (pointtype(pa) == FREEFACETVERTEX) {
auv[0] = pointgeomuv(pa, 0);
auv[1] = pointgeomuv(pa, 1);
} else {
assert(0);
}
if (pointtype(pb) == RIDGEVERTEX) {
in->getvertexparamonface(in->geomhandle, pointmark(pb), fid, buv);
} else if (pointtype(pb) == FREESEGVERTEX) {
eid = pointgeomtag(pb); // The Edge containing this Steiner point.
t = pointgeomuv(pb, 0); // The Steiner point's parameter on Edge.
in->getedgesteinerparamonface(in->geomhandle, eid, t, fid, buv);
} else if (pointtype(pb) == FREEFACETVERTEX) {
buv[0] = pointgeomuv(pb, 0);
buv[1] = pointgeomuv(pb, 1);
} else {
assert(0);
}
newuv[0] = 0.5 * (auv[0] + buv[0]);
newuv[1] = 0.5 * (auv[1] + buv[1]);
in->getsteineronface(in->geomhandle, fid, newuv, steinpt);
setpointgeomuv(steinpt, 0, newuv[0]);
setpointgeomuv(steinpt, 1, newuv[1]);
setpointgeomtag(steinpt, fid);
} else {
for (i = 0; i < 3; i++) {
steinpt[i] = (pa[i] + pb[i] + pc[i]) / 3.0;
}
}
// Start searching it from 'recentet'.
searchtet = recenttet;
// Now insert the point p. The flags are chosen as follows:
// - boywat = 2, the current T is a CDT,
// - lawson = 2, do flip after inserting p, some existing segments
// and subfaces may be flipped, they are queued and
// and will be recovered.
// - rejflag = 1, reject p if it encroaches upon at least one segment,
// queue encroached segments.
ivf.iloc = (int) OUTSIDE;
ivf.bowywat = 2;
ivf.lawson = 2;
ivf.rejflag = 1;
ivf.chkencflag = 0;
ivf.sloc = (int) ONFACE;
ivf.sbowywat = 2;
ivf.splitbdflag = 0;
ivf.validflag = 1;
ivf.respectbdflag = 0;
ivf.assignmeshsize = 0;
loc = insertvertex(steinpt, &searchtet, &splitsh, NULL, &ivf);
assert((loc != OUTSIDE) && (loc != ONVERTEX));
if (loc == NEARVERTEX) {
// The new point is either ON or VERY CLOSE to an existing point.
pa = point2ppt(steinpt);
printf(" !! Avoid to create a short edge (length = %g)\n",
distance(steinpt, pa));
// Indicate it may be an input problem.
printf(" Short edge length bound is: %g. Tolerance is %g.\n",
b->minedgelength, b->epsilon);
terminatetetgen(4);
}
if (loc == ENCSEGMENT) {
// Some segments are encroached and queued.
assert(encseglist->objects > 0l);
// Randomly pick one encroached segment to split.
s = randomnation(encseglist->objects);
paryseg = (face *) fastlookup(encseglist, s);
sseg = *paryseg;
// The new point p is the midpoint of this segment.
getsteinerptonsegment(&sseg, NULL, steinpt);
setpointtype(steinpt, FREESEGVERTEX);
encseglist->restart(); // Clear the queue.
// Start searching from an adjacent tetrahedron (containing the segment).
sstpivot1(sseg, searchtet);
spivot(sseg, splitsh);
// Insert the point p. The flags are chosen as follows:
// - boywat = 2, the current T is a CDT,
// - lawson = 2, do flip after inserting p, some existing segments
// and subfaces may be flipped, they are queued and
// and will be recovered.
// - rejflag = 0, always insert p, even it will cause some segments
// or subfaces missing, queue missing boundaries.
ivf.iloc = (int) ONEDGE;
ivf.bowywat = 2;
ivf.lawson = 2;
ivf.rejflag = 0;
ivf.chkencflag = 0;
ivf.sloc = (int) ONEDGE;
ivf.sbowywat = 2;
ivf.splitbdflag = 0;
ivf.validflag = 1;
ivf.respectbdflag = 0;
ivf.assignmeshsize = 0;
loc = insertvertex(steinpt, &searchtet, &splitsh, &sseg, &ivf);
if (loc == NEARVERTEX) {
// The new point is either ON or VERY CLOSE to an existing point.
pa = point2ppt(steinpt);
printf(" !! Avoid to create a short edge (length = %g)\n",
distance(steinpt, pa));
// Indicate it may be an input problem.
printf(" Short edge length bound is: %g. Tolerance is %g.\n",
b->minedgelength, b->epsilon);
terminatetetgen(4);
}
st_segref_count++;
} else {
st_facref_count++;
}
if (steinerleft > 0) steinerleft--;
// Do flip to recover Delaunayniess.
lawsonflip3d(steinpt, 2, 0, 0, 0);
// Some vertices may be queued, recover them.
if (subvertstack->objects > 0l) {
assert(0); //delaunizevertices();
}
// Some subsegments may be queued, recover them.
if (subsegstack->objects > 0l) {
delaunizesegments();
}
}
///////////////////////////////////////////////////////////////////////////////
// //
// constrainedfacets() Recover subfaces saved 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;
face searchsh, neighsh, *parysh;
face checkseg, *paryseg;
point refpt, *parypt;
enum interresult dir;
bool success;
int facetcount;
//int bakhullsize;
int crossflag;
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;
// 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) {
// Find an unrecovered subface.
smarktest(searchsh);
tg_facfaces->newindex((void **) &parysh);
*parysh = searchsh;
// Collect all non-recovered subfaces of the same facet.
for (i = 0; i < tg_facfaces->objects; i++) {
searchsh = * (face *) fastlookup(tg_facfaces, i);
for (j = 0; j < 3; j++) {
sspivot(searchsh, checkseg);
if (checkseg.sh == NULL) {
spivot(searchsh, neighsh);
assert(neighsh.sh != NULL); // SELF_CHECK
if (!smarktested(neighsh)) {
// It may be already recovered.
stpivot(neighsh, neightet);
if (neightet.tet == NULL) {
smarktest(neighsh);
tg_facfaces->newindex((void **) &parysh);
*parysh = neighsh;
}
}
}
senextself(searchsh);
} // j
} // i
// Have found all facet subfaces (vertices). Uninfect them.
for (i = 0; i < tg_facfaces->objects; i++) {
parysh = (face *) fastlookup(tg_facfaces, i);
sunmarktest(*parysh);
}
if (b->verbose > 2) {
printf(" Recover facet #%d: %ld subfaces.\n", facetcount + 1,
tg_facfaces->objects);
}
facetcount++;
// Loop until 'tg_facfaces' is empty.
while (tg_facfaces->objects > 0l) {
// Get the last subface of this array.
tg_facfaces->objects--;
parysh = (face *) fastlookup(tg_facfaces, tg_facfaces->objects);
searchsh = *parysh;
if (searchsh.sh[3] == NULL) continue; // Skip a dead subface.
stpivot(searchsh, neightet);
if (neightet.tet != NULL) continue; // Not a missing subface.
// Insert the subface.
searchtet.tet = NULL;
dir = scoutsubface(&searchsh, &searchtet);
if (dir == SHAREFACE) continue; // The subface is inserted.
if (dir == COLLISIONFACE) continue; // The subface is removed.
// The subface is missing. Form the missing region.
// Re-use 'tg_crosstets' for 'adjtets'.
formmissingregion(&searchsh, tg_missingshs, tg_missingshbds,
tg_missingshverts, tg_crosstets);
// Search for a crossing edge (tg_crosstets is cleared).
crossflag = scoutcrossedge(searchtet, tg_crosstets, tg_missingshs);
if (crossflag == 1) {
// Recover subfaces by local retetrahedralization.
// Form a cavity of crossing tets.
if (formcavity(&searchtet, tg_missingshs, tg_crosstets, tg_topfaces,
tg_botfaces, tg_toppoints, tg_botpoints)) {
if (!b->flipinsert) {
// Tetrahedralize the top part. Re-use 'tg_midfaces'.
delaunizecavity(tg_toppoints, tg_topfaces, tg_topshells,
tg_topnewtets, tg_crosstets, tg_midfaces);
// Tetrahedralize the bottom part. Re-use 'tg_midfaces'.
delaunizecavity(tg_botpoints, tg_botfaces, tg_botshells,
tg_botnewtets, tg_crosstets, tg_midfaces);
// Fill the cavity with new tets.
success = fillcavity(tg_topshells, tg_botshells, tg_midfaces,
tg_missingshs);
if (success) {
// Cavity is remeshed. Delete old tets and outer new tets.
carvecavity(tg_crosstets, tg_topnewtets, tg_botnewtets);
// Insert the missing region into cavity.
j = 0; // FOR DEBUG! Count the number of non-recovered faces.
for (i = 0; i < tg_missingshs->objects; i++) {
searchsh = * (face *) fastlookup(tg_missingshs, i);
searchtet.tet = NULL;
dir = scoutsubface(&searchsh, &searchtet);
assert(dir != COLLISIONFACE); // SELF_CHECK
if (dir != SHAREFACE) {
// A subface is missing. This is possible that the subface
// is not actually a constrained Delaunay face in T.
// Add this face at the end of the list, so it will be
// processed immediately. This is necessary because we
// have created some non-locally Delaunay face (by the
// remesh of the cavity). We have to insert the subfaces
// to make these face constrained Delaunay.
tg_facfaces->newindex((void **) &parysh);
*parysh = searchsh;
j++; // FOR DEBUG!
}
} // i
// Recover interior subfaces.
for (i = 0; i < caveencshlist->objects; i++) {
searchsh = * (face *) fastlookup(caveencshlist, i);
searchtet.tet = NULL;
dir = scoutsubface(&searchsh, &searchtet);
assert(dir != COLLISIONFACE); // SELF_CHECK
if (dir != SHAREFACE) {
// The subface is missing. This is possible that the subface
// is removed by the enlargement of the cavity. It has to
// be recovered.
// Add this face at the end of the list, so it will be
// processed immediately. We have to insert the subfaces
// to make these face constrained Delaunay.
tg_facfaces->newindex((void **) &parysh);
*parysh = searchsh;
j++; // FOR DEBUG!
}
} // i
// Recover interior segments. This should always be recovered.
for (i = 0; i < caveencseglist->objects; i++) {
paryseg = (face *) fastlookup(caveencseglist, i);
searchtet.tet = NULL;
refpt = NULL;
dir = scoutsegment(sorg(*paryseg),sdest(*paryseg),&searchtet,
&refpt, NULL);
assert(dir == SHAREEDGE);
// Insert this segment.
tsspivot1(searchtet, checkseg); // SELF_CHECK
if (checkseg.sh == NULL) {
// Let the segment remember an adjacent tet.
sstbond1(*paryseg, searchtet);
// Bond the segment to all tets containing it.
neightet = searchtet;
do {
tssbond1(neightet, *paryseg);
fnextself(neightet);
} while (neightet.tet != searchtet.tet);
} else {
// Collision! Should not happen.
assert(0);
}
} // i
caveencshlist->restart();
caveencseglist->restart();
} else {
// Restore old tets and delete new tets.
restorecavity(tg_crosstets, tg_topnewtets, tg_botnewtets);
// Set a handle for searching subface.
//recentsh = searchsh;
}
} else {
// Use the flip algorithm of Shewchuk to recover the subfaces.
flipinsertfacet(tg_crosstets, tg_toppoints, tg_botpoints,
tg_missingshverts);
// Check the missing subfaces again.
j = 0; // FOR DEBUG! Count the number of non-recovered faces.
for (i = 0; i < tg_missingshs->objects; i++) {
searchsh = * (face *) fastlookup(tg_missingshs, i);
searchtet.tet = NULL;
dir = scoutsubface(&searchsh, &searchtet);
assert(dir != COLLISIONFACE); // SELF_CHECK
if (dir != SHAREFACE) {
// A subface is missing. This is possible that the subface
// is not actually a constrained Delaunay face in T.
// Add this face at the end of the list, so it will be
// processed immediately. This is necessary because we
// have created some non-locally Delaunay face (by the
// remesh of the cavity). We have to insert the subfaces
// to make these face constrained Delaunay.
tg_facfaces->newindex((void **) &parysh);
*parysh = searchsh;
j++; // FOR DEBUG!
}
} // i
// Clear working lists.
tg_crosstets->restart();
tg_topfaces->restart();
tg_botfaces->restart();
tg_toppoints->restart();
tg_botpoints->restart();
success = true;
} // if (b->flipinsert)
} else {
// Formcavity failed.
success = false;
}
} else { //if (crossflag == 0) {
// Recover subfaces by retriangulate the surface mesh.
// Re-use tg_topshells for newshs.
success = fillregion(tg_missingshs, tg_missingshbds, tg_topshells);
if (success) {
// Region is remeshed. Delete old subfaces (in tg_missingshs).
for (i = 0; i < tg_missingshs->objects; i++) {
parysh = (face *) fastlookup(tg_missingshs, i);
shellfacedealloc(subfaces, parysh->sh);
}
tg_topshells->restart();
} else {
// Search a handle for searching tetrahedron.
recenttet = searchtet;
}
}
// Unmarktest all points of the missing region.
for (i = 0; i < tg_missingshverts->objects; i++) {
parypt = (point *) fastlookup(tg_missingshverts, i);
punmarktest(*parypt);
}
tg_missingshverts->restart();
tg_missingshbds->restart();
tg_missingshs->restart();
if (!success) {
// The missing region can not be recovered. Refine it.
refineregion();
// Clean the current list of facet subfaces.
//tg_facfaces->restart();
}
} // while (tg_facfaces->objects > 0l)
} // if (neightet.tet == NULL)
} // while (subfacstack->objects > 0l)
// Delete arrays.
delete tg_crosstets;
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;
}
///////////////////////////////////////////////////////////////////////////////
// //
// 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;
if (!b->quiet) {
printf("Constrained Delaunay...\n");
}
//if (!b->psc) {
// Only identify acute vertex for PLC inputs.
markacutevertices();
//}
if (b->verbose) {
printf(" Delaunizing segments.\n");
}
checksubsegflag = 1;
// Put all segments into the list.
if (0) { //if (b->order == 4) { // '-o4' option (for debug)
// In sequential order.
subsegs->traversalinit();
for (i = 0; i < subsegs->items; i++) {
searchseg.sh = shellfacetraverse(subsegs);
//sinfect(searchseg); // Only save it once.
subsegstack->newindex((void **) &paryseg);
*paryseg = searchseg;
}
} else {
// In random order.
subsegs->traversalinit();
for (i = 0; i < subsegs->items; i++) {
s = randomnation(i + 1);
// Move the s-th seg to the i-th.
subsegstack->newindex((void **) &paryseg);
*paryseg = * (face *) fastlookup(subsegstack, s);
// Put i-th seg to be the s-th.
searchseg.sh = shellfacetraverse(subsegs);
//sinfect(searchseg); // Only save it once.
paryseg = (face *) fastlookup(subsegstack, s);
*paryseg = searchseg;
}
}
// Recover non-Delaunay segments.
delaunizesegments();
if (b->verbose) {
printf(" %ld Steiner points.\n", st_segref_count);
}
tv = clock();
if (b->verbose) {
printf(" Constraining facets.\n");
}
if (b->flipinsert) {
// Clear the counters.
flip23count = flip32count = flip44count = 0l;
}
// Subfaces will be introduced.
checksubfaceflag = 1;
bakfillregioncount = fillregioncount;
bakcavitycount = cavitycount;
bakcavityexpcount = cavityexpcount;
// 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 > 0) {
printf(" Inserted %ld (%ld, %ld) refine points.\n",
st_segref_count + st_facref_count, st_segref_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'. //
// //
// For avoiding mutually flipping, once a crossing face is flipped, it will //
// never be re-created again. Also, we never create a face or edge which is //
// intersecting the current recovering segment or subface. //
// //
///////////////////////////////////////////////////////////////////////////////
int tetgenmesh::checkflipeligibility(int fliptype, point pa, point pb,
point pc, point pd, point pe,
int level, int edgepivot,
flipconstraints* fc)
{
int rejflag;
int i;
point tmppts[3];
REAL normal[3], area, len;
REAL ori1, ori2;
REAL abovept[3];
enum interresult dir;
int types[2], poss[4];
int intflag;
rejflag = 0;
if (fc->seg[0] != NULL) {
// A constraining edge is given (e.g., for edge recovery).
if (fliptype == 1) {
// A 2-to-3 flip: [a,b,c] => [e,d,a], [e,d,b], [e,d,c].
if (pc != dummypoint) {
// Do not flip if the newly created faces intersect this edge in
// their interiors.
tmppts[0] = pa;
tmppts[1] = pb;
tmppts[2] = pc;
if (0) {
// Make sure that the three new faces are not degenerate.
for (i = 0; i < 3 && !rejflag; i++) {
facenormal(pe, pd, tmppts[i], normal, 1, &len);
area = sqrt(DOT(normal, normal));
if (area == 0) {
rejflag = 1; // A degenerate face.
} else {
if ((area / (len * len)) < b->epsilon) {
rejflag = 1; // A nearly degenerate face.
}
}
} // i
}
for (i = 0; i < 3 && !rejflag; i++) {
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 ((dir == ACROSSVERT) || (dir == TOUCHEDGE) ||
(dir == TOUCHFACE)) {
assert(0); // Check this case.
}
} // dir
} else if (intflag == 4) {
// They may intersect at either a point or a line segment.
dir = (enum interresult) types[0];
if (dir == ACROSSEDGE) {
if (poss[0] == 0) {
// The interior of [e,d] intersect the segment.
// Since [e,d] is the newly created edge. Reject this flip.
rejflag = 1;
}
} else {
if ((dir == ACROSSVERT) || (dir == TOUCHEDGE) ||
(dir == TOUCHFACE)) {
assert(0); // Check this case.
}
}
} // if (intflag == 4)
} // i
} else { // pc == dummypoint
// Do not flip if the new hull edge [e,d] will intersect this edge
// in its interior.
// Comment: Here we actually need a 3D edge-edge test.
// We only do test if the edge in 'fc' is coplanar with the plane
// containing a,b,e,and d.
// Choose a better triangle [a,b,e] or [a,b,d].
facenormal(pa, pb, pe, normal, 1, &len);
area = sqrt(DOT(normal, normal));
facenormal(pa, pb, pd, normal, 1, &len);
len = sqrt(DOT(normal, normal)); // Re-use len as area.
if (area > len) {
// Choose [a,b,e]
ori1 = orient3d(pa, pb, pe, fc->seg[0]);
ori2 = orient3d(pa, pb, pe, fc->seg[1]);
} else {
// Choose [a,b,d]
ori1 = orient3d(pa, pb, pd, fc->seg[0]);
ori2 = orient3d(pa, pb, pd, fc->seg[1]);
}
if ((ori1 == 0) && (ori2 == 0)) {
calculateabovepoint4(pa, pb, pe, pd);
for (i = 0; i < 3; i++) {
abovept[i] = dummypoint[i];
}
intflag = tri_edge_test(pe, pd, abovept, fc->seg[0], fc->seg[1],
NULL, 1, types, poss);
if (intflag == 2) {
dir = (enum interresult) types[0];
assert(dir != ACROSSFACE);
if (dir == ACROSSEDGE) {
if (poss[0] == 0) {
// The interior of [e,d] intersect the segment.
// Since [e,d] is the newly created edge. Reject this flip.
rejflag = 1;
}
} else {
if ((dir == ACROSSVERT) || (dir == TOUCHEDGE) ||
(dir == TOUCHFACE)) {
assert(0); // Check this case.
}
}
} else if (intflag == 4) {
// [e,d,abovept] is coplanar with the constraining edge 'fc'.
// This is poissible if the edge in 'fc' is just the edge [e,d]
// (SHAREEDGE) or they share a common vertex (SHAREVEER)
dir = (enum interresult) types[0];
if (dir == ACROSSEDGE) {
// This case can only happen if [e,d] is coplanar with 'fc'.
assert(0); // Not possible.
} else {
if ((dir == ACROSSVERT) || (dir == TOUCHEDGE) ||
(dir == TOUCHFACE)) {
assert(0); // Check this case.
}
}
}
}
} // if (pc == dummypoint)
} else if (fliptype == 2) {
// A 3-to-2 flip: [e,d,a], [e,d,b], [e,d,c] => [a,b,c]
if (pc != dummypoint) {
if (0) {
// Make sure that [a,b,c] is a valid face.
facenormal(pa, pb, pc, normal, 1, &len);
area = sqrt(DOT(normal, normal));
if (area == 0) {
rejflag = 1; // A degenerate face.
} else {
if ((area / (len * len)) < b->epsilon) {
rejflag = 1; // A nearly degenerate face.
}
}
}
if (!rejflag) {
// Check if the new face [a,b,c] intersect the edge in its interior.
intflag = tri_edge_test(pa, pb, pc, fc->seg[0], fc->seg[1], NULL,
1, types, poss);
if (intflag == 2) {
// They intersect at a single point.
dir = (enum interresult) types[0];
if (dir == ACROSSFACE) {
// The interior of [a,b,c] intersect the segment.
rejflag = 1; // Do not flip.
} else if (dir == ACROSSEDGE) {
// This case is possible since we allow a previous 2-to-3 flip
// even it will create a degenerate tet at edge [a,b].
} else {
if ((dir == ACROSSVERT) || (dir == TOUCHEDGE) ||
(dir == TOUCHFACE)) {
assert(0); // Check this case.
}
}
} else if (intflag == 4) {
// [a,b,c] is coplanar with the edge.
dir = (enum interresult) types[0];
if (dir == ACROSSEDGE) {
// The boundary of [a,b,c] intersect the segment.
// An example is found in case 'camila.poly', during the recovery
// of segment [151, 161] (at linklevel = 2). See: 2011-06-10-a.
rejflag = 1; // Do not flip.
}
}
} // if (!relflag)
} else { // pc == dummypoint
// The flip 3-to-2 will replace [e,d] with a new hull edge [a,b].
// Only do flip if [a,b] does not intersect the edge of 'fc'.
// Comment: Here we acutually need a 3D edge-edge intersection test.
// We only do test if the edge in 'fc' is coplanar with the plane
// containing a,b,e, and d.
// Choose a better triangle [a,b,e] or [a,b,d].
facenormal(pa, pb, pe, normal, 1, &len);
area = sqrt(DOT(normal, normal));
facenormal(pa, pb, pd, normal, 1, &len);
len = sqrt(DOT(normal, normal)); // Re-use len as area.
if (area > len) {
// Choose [a,b,e]
ori1 = orient3d(pa, pb, pe, fc->seg[0]);
ori2 = orient3d(pa, pb, pe, fc->seg[1]);
} else {
// Choose [a,b,d]
ori1 = orient3d(pa, pb, pd, fc->seg[0]);
ori2 = orient3d(pa, pb, pd, fc->seg[1]);
}
if ((ori1 == 0) && (ori2 == 0)) {
// The edge in 'fc' is coplanar with the plane containing [a,b,e,d].
calculateabovepoint4(pa, pb, pe, pd);
for (i = 0; i < 3; i++) {
abovept[i] = dummypoint[i];
}
intflag = tri_edge_test(pa, pb, abovept, fc->seg[0], fc->seg[1],
NULL, 1, types, poss);
if (intflag == 2) {
dir = (enum interresult) types[0];
assert(dir != ACROSSFACE);
if (dir == ACROSSEDGE) {
assert(0); // Check this case.
rejflag = 1; // Do not flip.
} else {
if ((dir == ACROSSVERT) || (dir == TOUCHEDGE) ||
(dir == TOUCHFACE)) {
assert(0); // Check this case.
}
}
} else if (intflag == 4) {
// The edge 'fc' is coplanar with [a,b,abovept].
// This is poissible if the edge in 'fc' is just the edge [a,b]
// (SHAREEDGE) or they share a common vertex (SHAREVEER)
dir = (enum interresult) types[0];
if (dir == ACROSSEDGE) {
// This case can only happen if [a,b] is coplanar with 'fc'.
assert(0); // Not possible.
} else {
if ((dir == ACROSSVERT) || (dir == TOUCHEDGE) ||
(dir == TOUCHFACE)) {
assert(0); // Check this case.
}
}
}
} // if (ori1 == 0 && ori2 == 0)
}
} else {
assert(0); // An unknown flip type.
}
} // if (fc->seg[0] != NULL)
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 ((dir == ACROSSVERT) || (dir == TOUCHEDGE) || (dir == TOUCHFACE)) {
assert(0); // Check this case.
}
}
} else if (intflag == 4) {
// The edge [e,d] is coplanar with the face.
// There may be two intersections.
for (i = 0; i < 2 && !rejflag; i++) {
dir = (enum interresult) types[i];
if (dir == ACROSSFACE) {
rejflag = 1;
} else if (dir == ACROSSEDGE) {
rejflag = 1;
}
}
}
} // if (fliptype == 1)
} // if (fc->fac[0] != NULL)
if ((fc->remvert != NULL) && !rejflag) {
// The vertex is going to be removed. Do not create a new edge which
// contains this vertex.
if (fliptype == 1) {
// A 2-to-3 flip.
if ((pd == fc->remvert) || (pe == fc->remvert)) {
rejflag = 1;
}
}
}
if (fc->remove_large_angle && !rejflag) {
// Remove a large dihedral angle. Do not create a new small angle.
REAL cosmaxd = 0, diff;
if (fliptype == 1) {
// We assume that neither 'a' nor 'b' is dummypoint.
assert((pa != dummypoint) && (pb != dummypoint)); // SELF_CHECK
// A 2-to-3 flip: [a,b,c] => [e,d,a], [e,d,b], [e,d,c].
// The new tet [e,d,a,b] will be flipped later. Only two new tets:
// [e,d,b,c] and [e,d,c,a] need to be checked.
if ((pc != dummypoint) && (pe != dummypoint) && (pd != dummypoint)) {
// Get the largest dihedral angle of [e,d,b,c].
tetalldihedral(pe, pd, pb, pc, NULL, &cosmaxd, NULL);
diff = cosmaxd - fc->cosdihed_in;
if (fabs(diff/fc->cosdihed_in) < b->epsilon) diff = 0.0; // Rounding.
if (diff <= 0) { //if (cosmaxd <= fc->cosdihed_in) {
rejflag = 1;
} else {
// 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.
assert((pe != dummypoint) && (pd != dummypoint)); // SELF_CHECK
if (level == 0) {
// Both new tets [a,b,c,d] and [b,a,c,e] are new tets.
if ((pa != dummypoint) && (pb != dummypoint) && (pc != dummypoint)) {
// Get the largest dihedral angle of [a,b,c,d].
tetalldihedral(pa, pb, pc, pd, NULL, &cosmaxd, NULL);
diff = cosmaxd - fc->cosdihed_in;
if (fabs(diff/fc->cosdihed_in) < b->epsilon) diff = 0.0; // Rounding
if (diff <= 0) { //if (cosmaxd <= fc->cosdihed_in) {
rejflag = 1;
} else {
// Record the largest new angle.
if (cosmaxd < fc->cosdihed_out) {
fc->cosdihed_out = cosmaxd;
}
// Get the largest dihedral angle of [b,a,c,e].
tetalldihedral(pb, pa, pc, pe, NULL, &cosmaxd, NULL);
diff = cosmaxd - fc->cosdihed_in;
if (fabs(diff/fc->cosdihed_in) < b->epsilon) diff = 0.0;// Rounding
if (diff <= 0) { //if (cosmaxd <= fc->cosdihed_in) {
rejflag = 1;
} else {
// Record the largest new angle.
if (cosmaxd < fc->cosdihed_out) {
fc->cosdihed_out = cosmaxd;
}
}
}
}
} else { // level > 0
assert(edgepivot != 0);
if (edgepivot == 1) {
// The new tet [a,b,c,d] will be flipped. Only check [b,a,c,e].
if ((pa != dummypoint) && (pb != dummypoint) && (pc != dummypoint)) {
// Get the largest dihedral angle of [b,a,c,e].
tetalldihedral(pb, pa, pc, pe, NULL, &cosmaxd, NULL);
diff = cosmaxd - fc->cosdihed_in;
if (fabs(diff/fc->cosdihed_in) < b->epsilon) diff = 0.0;// Rounding
if (diff <= 0) { //if (cosmaxd <= fc->cosdihed_in) {
rejflag = 1;
} else {
// Record the largest new angle.
if (cosmaxd < fc->cosdihed_out) {
fc->cosdihed_out = cosmaxd;
}
}
}
} else {
assert(edgepivot == 2);
// The new tet [b,a,c,e] will be flipped. Only check [a,b,c,d].
if ((pa != dummypoint) && (pb != dummypoint) && (pc != dummypoint)) {
// Get the largest dihedral angle of [b,a,c,e].
tetalldihedral(pa, pb, pc, pd, NULL, &cosmaxd, NULL);
diff = cosmaxd - fc->cosdihed_in;
if (fabs(diff/fc->cosdihed_in) < b->epsilon) diff = 0.0;// Rounding
if (diff <= 0) { //if (cosmaxd <= fc->cosdihed_in) {
rejflag = 1;
} else {
// Record the largest new angle.
if (cosmaxd < fc->cosdihed_out) {
fc->cosdihed_out = cosmaxd;
}
}
}
} // edgepivot
} // level
}
}
return rejflag;
}
///////////////////////////////////////////////////////////////////////////////
// //
// removeedgebyflips() Remove an edge by flips. //
// //
// 'flipedge' is a non-convex or flat edge [a,b,#,#]. //
// //
// The return value is a positive integer, it indicates whether the edge is //
// removed or not. A value "2" means the edge is removed, othereise, the //
// 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;
face checkseg, *paryseg;
int counter; // DEBUG
int n, nn, i;
// Bakup valuses (for free spaces in flipnm_post()).
int bakunflip = fc->unflip;
int bakcollectnewtets = fc->collectnewtets;
if (b->verbose > 2) {
printf(" Removing edge (%d, %d)\n", pointmark(org(*flipedge)),
pointmark(dest(*flipedge)));
}
//if (fc != NULL) {
fc->clearcounters();
//}
if (checksubsegflag) {
// Do not flip a segment.
tsspivot1(*flipedge, checkseg);
if (checkseg.sh != NULL) {
if (b->verbose > 2) {
printf(" Can't flip a segment (%d, %d).\n",
pointmark(sorg(checkseg)), pointmark(sdest(checkseg)));
}
//if (fc != NULL) {
fc->encsegcount++;
if (fc->collectencsegflag) {
if (!sinfected(checkseg)) {
// Queue this segment in list.
sinfect(checkseg);
caveencseglist->newindex((void **) &paryseg);
*paryseg = checkseg;
}
}
//}
return 0;
}
}
// Count the number of tets at edge [a,b].
n = 0;
counter = 0; // Sum of star counters;
spintet = *flipedge;
i = 0;
while (1) {
counter += elemcounter(spintet);
i++;
fnextself(spintet);
if (spintet.tet == flipedge->tet) break;
}
//assert(i >= 3);
if (i < 3) {
// It is only possible when the mesh contains inverted tetrahedra.
assert(checkinverttetflag);
// Since "return 2" means success, we return 0.
return 0;
}
assert(counter == 0); // SELF_CHECK
n = i;
flipstarcount++;
// Record the maximum star size.
if (n > maxflipstarsize) {
maxflipstarsize = n;
}
if ((b->flipstarsize > 0) && (n > b->flipstarsize)) {
// The star size exceeds the given limit (-YY__).
skpflipstarcount++;
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;
//marktest(abtets[i]); // Marktest it (in Star(ab)).
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 flipped.
if (b->verbose > 2) {
printf(" Edge is removed.\n");
}
} else {
// Edge is not flipped. Unmarktest the remaining tets in Star(ab).
for (i = 0; i < nn; i++) {
//assert(marktested(abtets[i]));
//unmarktest(abtets[i]);
assert(elemcounter(abtets[i]) == 1);
setelemcounter(abtets[i], 0);
}
if (b->verbose > 2) {
printf(" Edge is not removed. n(%d), nn(%d).\n", n, nn);
}
// Restore the input edge (needed by Lawson's flip).
*flipedge = abtets[0];
}
// Release the temporary allocated spaces.
// NOTE: fc->unflip must be 0.
fc->unflip = 0;
fc->collectnewtets = 0;
flipnm_post(abtets, n, nn, 0, fc);
fc->unflip = bakunflip;
fc->collectnewtets = bakcollectnewtets;
delete [] abtets;
return nn; //return nn == 2;
}
///////////////////////////////////////////////////////////////////////////////
// //
// removefacebyflips() Remove a face by flips. //
// //
// ASSUMPTIONS: //
// - 'flipface' must not be a hull face. //
// //
///////////////////////////////////////////////////////////////////////////////
int tetgenmesh::removefacebyflips(triface *flipface, flipconstraints* fc)
{
triface fliptets[3], flipedge;
face checksh;
point pa, pb, pc, pd, pe;
point pts[3];
enum interresult dir;
int types[2], poss[4], pos;
REAL ori;
int reducflag, rejflag;
int i, j;
if (checksubfaceflag) {
tspivot(*flipface, checksh);
if (checksh.sh != NULL) {
if (b->verbose > 2) {
printf(" Can't flip a subface.\n");
}
return 0;
}
}
fliptets[0] = *flipface;
fsym(*flipface, fliptets[1]);
assert(!ishulltet(fliptets[0]));
assert(!ishulltet(fliptets[1]));
pa = org(fliptets[0]);
pb = dest(fliptets[0]);
pc = apex(fliptets[0]);
pd = oppo(fliptets[0]);
pe = oppo(fliptets[1]);
if (b->verbose > 2) {
printf(" Removing face (%d, %d, %d) -- %d, %d\n", pointmark(pa),
pointmark(pb), pointmark(pc), pointmark(pd), pointmark(pe));
}
reducflag = 0;
ori = orient3d(pa, pb, pd, pe);
if (ori > 0) {
ori = orient3d(pb, pc, pd, pe);
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.
rejflag = 0;
if (fc != NULL) {
//rejflag = checkflipeligibility(1, pa, pb, pc, pd, pe, fc);
}
if (!rejflag) {
flip23(fliptets, 0, 0, 0);
if (b->verbose > 2) {
printf(" Face is removed by a 2-to-3 flip.\n");
}
return 1;
} else {
if (b->verbose > 2) {
printf(" -- Reject a 2-to-3 flip at face (%d, %d, %d)\n",
pointmark(pa), pointmark(pb), pointmark(pc));
}
if (fc != NULL) {
fc->rejf23count++;
}
}
} else {
if (0) {
// Try to flip one of the edges of this face.
pts[0] = org(flipedge);
pts[1] = dest(flipedge);
pts[2] = apex(flipedge);
// Start from the recorded locally non-convex edge 'flipedge'.
for (i = 0; i < 3; i++) {
if (removeedgebyflips(&flipedge, fc) == 2) {
if (b->verbose > 2) {
printf(" Face is removed by removing edge (%d, %d).\n",
pointmark(pts[i]), pointmark(pts[(i+1)%3]));
}
return 1;
}
// The 'flipedge' may be dead in above call.
point2tetorg(pts[i], flipedge);
finddirection(&flipedge, pts[(i+1)%3], 1);
if (dest(flipedge) != pts[(i+1)%3]) {
if (b->verbose > 2) {
printf(" Face is removed during removing edge (%d, %d).\n",
pointmark(pts[i]), pointmark(pts[(i+1)%3]));
}
return 1;
}
} // i
} else {
if (0) { //if (fc->seg[0] != NULL) {
// The face is intersecting a segment.
// Find the edge shared by three corssing faces.
// We assume that the 'flipface' is facing to 'fc->seg[0]'. It is the
// case when the function is called from 'recoveredgebyflips()'.
// DEBUG BEGIN
pa = org(*flipface);
pb = dest(*flipface);
pc = apex(*flipface);
ori = orient3d(pa, pb, pc, fc->seg[0]);
assert(ori < 0);
// DEBUG END
fsym(*flipface, flipedge);
pc = oppo(flipedge);
for (i = 0; i < 3; i++) {
pa = org(flipedge);
pb = dest(flipedge);
if (tri_edge_test(pa, pb, pc, fc->seg[0], fc->seg[1], NULL, 1,
types, poss)) {
dir = (enum interresult) types[0];
if (dir == ACROSSFACE) {
break; // Found the crossing face.
} else if (dir == ACROSSEDGE) {
// Found an edge intersects the segment.
esymself(flipedge);
pos = poss[0];
for (j = 0; j < pos; j++) {
eprevself(flipedge);
}
// Flip this edge.
break;
} else if (dir == SHAREVERT) {
// We have reached the endpoint of the segment.
assert(pc == fc->seg[1]);
// The face is not flippable.
return 0;
} else {
assert(0); // Not possible.
}
}
enextself(flipedge);
}
assert(i < 3);
} else {
if (b->verbose > 2) {
pa = org(flipedge);
pb = dest(flipedge);
}
}
// Try to flip the selected edge of this face.
if (removeedgebyflips(&flipedge, fc) == 2) {
if (b->verbose > 2) {
printf(" Face is removed by removing edge (%d, %d).\n",
pointmark(pa), pointmark(pb));
}
return 1;
}
}
}
// 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 flipped. //
// //
// If the parameter 'fullsearch' is set, it tries to flip any face or edge //
// that intersects the recovering edge. Otherwise, only the face or edge //
// which is visible by 'startpt' is tried. //
// //
///////////////////////////////////////////////////////////////////////////////
int tetgenmesh::recoveredgebyflips(point startpt, point endpt,
triface* searchtet, int fullsearch)
{
triface neightet, spintet; // *abtets;
point pa, pb, pc, pd;
badface bakface;
enum interresult dir, dir1;
flipconstraints fc;
int types[2], poss[4], pos = 0;
int success;
//int n, endi;
int i, j; //, k;
if (b->verbose > 2) {
printf(" Recovering edge (%d, %d)\n", pointmark(startpt),
pointmark(endpt));
}
fc.seg[0] = startpt;
fc.seg[1] = endpt;
// The mainloop of the edge reocvery.
while (1) { // Loop I
// Search the edge from 'startpt'.
point2tetorg(startpt, *searchtet);
assert(org(*searchtet) == startpt); // SELF_CHECK
dir = finddirection(searchtet, endpt, 1);
if (dir == ACROSSVERT) {
if (dest(*searchtet) == endpt) {
return 1; // Edge is recovered.
} else {
// A PLC problem, or there is a Steiner point.
terminatetetgen(3); //assert(0); // Debug
}
}
// The edge is missing.
// Try to flip the first intersecting face/edge.
enextesymself(*searchtet); // Go to the opposite face.
if (dir == ACROSSFACE) {
// A face is intersected with the segment. Try to flip it.
if (removefacebyflips(searchtet, &fc)) {
continue;
}
} else if (dir == ACROSSEDGE) {
// An edge is intersected with the segment. Try to flip it.
if (removeedgebyflips(searchtet, &fc) == 2) {
continue;
}
} else {
terminatetetgen(3); //assert(0); // A PLC problem.
}
// The edge is missing.
if (fullsearch) {
if (1) {
// Try to flip one of the faces/edges which intersects the edge.
success = 0;
// Loop through the sequence of intersecting faces/edges from
// 'startpt' to 'endpt'.
point2tetorg(startpt, *searchtet);
assert(org(*searchtet) == startpt); // SELF_CHECK
dir = finddirection(searchtet, endpt, 1);
assert(dir != ACROSSVERT);
// Go to the face/edge intersecting the searching edge.
enextesymself(*searchtet); // Go to the opposite face.
// This face/edge has been tried in previous step.
while (1) { // Loop I-I
// Find the next intersecting face/edge.
fsymself(*searchtet);
if (dir == ACROSSFACE) {
neightet = *searchtet;
j = (neightet.ver & 3); // j is the current face number.
for (i = j + 1; i < j + 4; i++) {
neightet.ver = (i % 4);
pa = org(neightet);
pb = dest(neightet);
pc = apex(neightet);
pd = oppo(neightet); // The above point.
if (tri_edge_test(pa,pb,pc,startpt,endpt, pd, 1, types, poss)) {
dir = (enum interresult) types[0];
pos = poss[0];
break;
} else {
dir = DISJOINT;
pos = 0;
}
} // i
// There must be an intersection face/edge.
assert(dir != DISJOINT); // SELF_CHECK
} else {
assert(dir == ACROSSEDGE);
while (1) { // Loop I-I-I
// Check the two opposite faces (of the edge) in 'searchtet'.
for (i = 0; i < 2; i++) {
if (i == 0) {
enextesym(*searchtet, neightet);
} else {
eprevesym(*searchtet, neightet);
}
pa = org(neightet);
pb = dest(neightet);
pc = apex(neightet);
pd = oppo(neightet); // The above point.
if (tri_edge_test(pa,pb,pc,startpt,endpt,pd,1, types, poss)) {
dir = (enum interresult) types[0];
pos = poss[0];
break; // for loop
} else {
dir = DISJOINT;
pos = 0;
}
} // i
if (dir != DISJOINT) {
// Find an intersection face/edge.
break; // Loop I-I-I
}
// No intersection. Rotate to the next tet at the edge.
fnextself(*searchtet);
} // while (1) // Loop I-I-I
}
// Adjust to the intersecting edge/vertex.
for (i = 0; i < pos; i++) {
enextself(neightet);
}
if (dir == SHAREVERT) {
// Check if we have reached the 'endpt'.
pd = org(neightet);
if (pd == endpt) {
// Failed to recover the edge.
break; // Loop I-I
} else {
// We need to further check this case. It might be a PLC problem
// or a Steiner point that was added at a bad location.
assert(0);
}
}
// The next to be flipped face/edge.
*searchtet = neightet;
// Bakup this face (tetrahedron).
bakface.forg = org(*searchtet);
bakface.fdest = dest(*searchtet);
bakface.fapex = apex(*searchtet);
bakface.foppo = oppo(*searchtet);
// Try to flip this intersecting face/edge.
if (dir == ACROSSFACE) {
if (removefacebyflips(searchtet, &fc)) {
success = 1;
break; // Loop I-I
}
} else if (dir == ACROSSEDGE) {
if (removeedgebyflips(searchtet, &fc) == 2) {
success = 1;
break; // Loop I-I
}
} else {
assert(0); // A PLC problem.
}
// The face/edge is not flipped.
if ((searchtet->tet == NULL) ||
(org(*searchtet) != bakface.forg) ||
(dest(*searchtet) != bakface.fdest) ||
(apex(*searchtet) != bakface.fapex) ||
(oppo(*searchtet) != bakface.foppo)) {
// 'searchtet' was flipped. We must restore it.
point2tetorg(bakface.forg, *searchtet);
dir1 = finddirection(searchtet, bakface.fdest, 1);
if (dir1 == ACROSSVERT) {
assert(dest(*searchtet) == bakface.fdest);
spintet = *searchtet;
while (1) {
if (apex(spintet) == bakface.fapex) {
// Found the face.
*searchtet = spintet;
break;
}
fnextself(spintet);
if (spintet.tet == searchtet->tet) {
searchtet->tet = NULL;
break; // Not find.
}
} // while (1)
if (searchtet->tet != NULL) {
if (oppo(*searchtet) != bakface.foppo) {
fsymself(*searchtet);
if (oppo(*searchtet) != bakface.foppo) {
assert(0); // Check this case.
searchtet->tet = NULL;
break; // Not find.
}
}
}
} else {
searchtet->tet = NULL; // Not find.
}
if (searchtet->tet == NULL) {
success = 0; // This face/edge has been destroed.
break; // Loop I-I
}
}
} // while (1) // Loop I-I
if (success) {
// One of intersecting faces/edges is flipped.
continue;
}
} // if (0)
} // if (fullsearch)
// The edge is missing.
break; // Loop I
} // while (1) // Loop I
// The edge is not recovered.
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]. //
// //
// These set of tets arises when we want to recover an edge from 'p0' to 'p_ //
// (n-1)', and the recoveredgenbyflips() routine fails. Note that the outer //
// faces of these tets defines a polyhedron P, and the set of tets gives the //
// ONLY tetrahedralization of P. If we replace the two boundary faces [a,b, //
// p0] and [a,b,p_(n-1)] by [p0,p_(n-1),a] and [p0,p_(n-1),b], and call the //
// new polyhedron P'. In this routine, we think P' is not tetrahedralizable //
// (since the routine recoveredgenbyflips() fails!! AND no flip is possible //
// on any of these edges: [a,p1], [b,p1], [a,p2], [b,p2], ..., [a,p_(n-2)], //
// and [b,p_(n-1)]). If n = 3, P' is just the famous Schoenhardt polyhedron. //
// For n > 3, we call P' the generalized Schoenhardt polyhedron, it includes //
// the Bagemihl's polyhedron as a special case. //
// //
// It is obvious that P is a Star-shaped polyhedron. The mid-point of [a,b] //
// is visible by all boundary faces of P, push it slightly inside P does not //
// change the visibilty. Indeed every interior point of [a,b] is visible by //
// the boundary faces of P. //
// //
// //
///////////////////////////////////////////////////////////////////////////////
int tetgenmesh::add_steinerpt_in_schoenhardtpoly(triface *abtets, int n,
int chkencflag)
{
triface worktet, *parytet;
triface faketet1, faketet2;
point pa, pb, pc, pd;
point p1, p2, p3;
point steinerpt;
insertvertexflags ivf;
optparameters opm;
REAL vcd[3], sampt[3], smtpt[3];
REAL maxminvol = 0.0, minvol = 0.0, ori;
int success, maxidx = 0;
int loc;
int it, i;
if (b->verbose > 2) {
printf(" Find a Steiner in Schoenhardt polyhedron (n=%d).\n", n);
}
pa = org(abtets[0]);
pb = dest(abtets[0]);
pc = apex(abtets[0]); // pc = p0
pd = oppo(abtets[n-1]); // pd = p_(n-1)
// 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++) {
eprev(abtets[i], worktet);
esymself(worktet); // [a,p_i,p_i+1].
cavetetlist->newindex((void **) &parytet);
*parytet = worktet;
enext(abtets[i], worktet);
esymself(worktet); // [p_i,b,p_i+1].
cavetetlist->newindex((void **) &parytet);
*parytet = worktet;
}
// Search the point along the edge [c,d].
for (i = 0; i < 3; i++) vcd[i] = pd[i] - pc[i];
// Sample 100 points in edge [c,d].
for (it = 1; it < 100; it++) {
for (i = 0; i < 3; i++) {
sampt[i] = pc[i] + (0.01 * (double) it) * vcd[i];
}
for (i = 0; i < cavetetlist->objects; i++) {
parytet = (triface *) fastlookup(cavetetlist, i);
p1 = org(*parytet);
p2 = dest(*parytet);
p3 = apex(*parytet);
ori = orient3d(p2, p1, p3, sampt);
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) {
if (b->verbose > 2) {
printf(" Unable to find a initial point: maxminvol = %g\n",
maxminvol);
}
cavetetlist->restart();
return 0;
}
for (i = 0; i < 3; i++) {
smtpt[i] = pc[i] + (0.01 * (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, pa, dummypoint);
cavetetlist->newindex((void **) &parytet);
*parytet = faketet1;
maketetrahedron(&faketet2);
setvertices(faketet2, pc, pd, pb, 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) {
if (b->verbose > 2) {
printf(" Unable to relocate the initial point.\n");
}
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.bowywat = 0; // Do not use Bowyer-Watson algorithm.
ivf.lawson = 0; // Do not flip.
ivf.rejflag = 0;
ivf.chkencflag = chkencflag;
ivf.sloc = 0;
ivf.sbowywat = 0;
ivf.splitbdflag = 0;
ivf.validflag = 0;
ivf.respectbdflag = 0;
ivf.assignmeshsize = 0;
// Insert the new point into the tetrahedralization T.
// Note that T is convex (nonconvex = 0).
loc = insertvertex(steinerpt, &worktet, NULL, NULL, &ivf);
if (loc == (int) INSTAR) {
// The vertex has been inserted.
st_volref_count++; //st_inpoly_count++;
if (steinerleft > 0) steinerleft--;
return 1;
} else {
// The Steiner point is too close to an existing vertex. Reject it.
pointdealloc(steinerpt);
return 0;
}
}
///////////////////////////////////////////////////////////////////////////////
// //
// addsteiner4recoversegment() Add a Steiner point for recoveing a seg. //
// //
///////////////////////////////////////////////////////////////////////////////
int tetgenmesh::addsteiner4recoversegment(face* misseg, int splitsegflag)
{
triface *abtets, searchtet, spintet;
face splitsh;
face checkseg;
face *paryseg;
point startpt, endpt;
point pa, pb, pd, steinerpt, *parypt;
enum interresult dir;
insertvertexflags ivf;
int types[2], poss[4];
REAL ip[3], u;
int n, endi, success;
int loc;
int 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);
assert(org(searchtet) == startpt); // SELF_CHECK
dir = finddirection(&searchtet, endpt, 1);
assert(dir != ACROSSVERT);
// Get the first intersecting face/edge.
assert(!ishulltet(searchtet));
enextself(searchtet);
//assert(apex(searchtet) == startpt);
if (dir == ACROSSFACE) {
// The segment is crossing at least 3 faces. Find the common edge of
// the first 3 crossing faces.
esymself(searchtet);
assert(oppo(searchtet) == startpt);
fsym(searchtet, spintet);
pd = oppo(spintet);
if (pd == endpt) {
// This should be possible.
assert(0); // Debug this case.
}
for (i = 0; i < 3; i++) {
pa = org(spintet);
pb = dest(spintet);
//pc = apex(neightet);
if (tri_edge_test(pa, pb, pd, startpt, endpt, NULL, 1, types, poss)) {
break; // Found the edge.
}
enextself(spintet);
eprevself(searchtet);
}
assert(i < 3);
esymself(searchtet);
} else {
assert(dir == ACROSSEDGE);
// PLC check.
tsspivot1(searchtet, checkseg);
if (checkseg.sh != NULL) {
printf("Found two segments intersect each other.\n");
pa = farsorg(*misseg);
pb = farsdest(*misseg);
printf(" 1st: [%d,%d] %d.\n", pointmark(pa), pointmark(pb),
shellmark(*misseg));
pa = farsorg(checkseg);
pb = farsdest(checkseg);
printf(" 2nd: [%d,%d] %d.\n", pointmark(pa), pointmark(pb),
shellmark(checkseg));
terminatetetgen(3);
}
}
assert(apex(searchtet) == startpt);
spintet = searchtet;
n = 0; endi = -1;
while (1) {
// Check if the endpt appears in the star.
if (apex(spintet) == endpt) {
endi = n; // Remember the position of endpt.
}
n++; // Count a tet in the star.
fnextself(spintet);
if (spintet.tet == searchtet.tet) break;
}
assert(n >= 3);
if (endi > 0) {
// endpt is also in the edge star
// Get all tets in the edge star.
abtets = new triface[n];
spintet = searchtet;
for (i = 0; i < n; i++) {
abtets[i] = spintet;
fnextself(spintet);
}
assert(apex(abtets[0]) == startpt);
assert(apex(abtets[endi]) == endpt);
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) {
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.
assert(0); // DEBUG IT
}
} else {
assert(0); // A PLC problem.
}
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));
}
if (endi == -1) {
// Let the missing segment be [a,b]. Let the edge [c,d] whose star contains
// a and intersects [a,b]. We choose the Steiner point at the intersection
// of the edge star of [c,d] and [a,b] (not a).
if (dir == ACROSSFACE) {
pa = org(searchtet);
pb = dest(searchtet);
spintet = searchtet;
n = 0; endi = -1;
while (1) {
n++; // Count a tet in the star.
fnextself(spintet);
if (spintet.tet == searchtet.tet) break;
// Check if the segment leaves the edge star.
pd = apex(spintet);
assert(pd != endpt);
if (!tri_edge_test(pa, pb, pd, startpt, endpt, NULL, 1, types, poss)) {
if (endi == -1) endi = (n - 1);
}
}
assert(n >= 3);
assert(endi != -1);
// 'abtets' is only for debug purpose.
abtets = new triface[endi];
spintet = searchtet;
for (i = 0; i < endi; i++) {
abtets[i] = spintet;
fnextself(spintet);
}
searchtet = abtets[endi - 1];
esymself(searchtet); // The exit face of [startpt, endpt].
delete [] abtets;
} else {
assert(dir == ACROSSEDGE);
assert(apex(searchtet) == startpt);
esymself(searchtet); // The exit face of [startpt, endpt].
//assert(oppo(searchtet) == startpt);
pa = org(searchtet);
pb = dest(searchtet);
}
pd = apex(searchtet);
// Get the intersection type (ACROSSFACE or ACROSSEDGE).
if (tri_edge_test(pa, pb, pd, startpt, endpt, NULL, 1, types, poss)) {
dir = (enum interresult) types[0];
assert((dir == ACROSSFACE) || (dir == ACROSSEDGE));
} else {
assert(0); // not possible.
}
// Calculate the intersection of the face [a,b,d] and the segment.
planelineint(pa, pb, pd, startpt, endpt, ip, &u);
assert((u > 0) && (u < 1));
// Create a Steiner point.
makepoint(&steinerpt, FREESEGVERTEX);
for (i = 0; i < 3; i++) steinerpt[i] = ip[i];
spivot(*misseg, splitsh);
if (dir == ACROSSFACE) {
ivf.iloc = (int) ONFACE;
} else {
ivf.iloc = (int) ONEDGE;
}
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 = 0;
loc = insertvertex(steinerpt, &searchtet, &splitsh, misseg, &ivf);
if (loc != ivf.iloc) {
if (loc == (int) NEARVERTEX) {
// The vertex is rejected. Too close to an existing vertex.
pointdealloc(steinerpt);
steinerpt = NULL;
} else {
assert(0); // Unknown case.
}
}
} else { // if (endi > 0)
steinerpt = NULL;
}
if (steinerpt == NULL) {
// Split the segment at its midpoint.
makepoint(&steinerpt, FREESEGVERTEX);
for (i = 0; i < 3; i++) {
steinerpt[i] = 0.5 * (startpt[i] + endpt[i]);
}
// We need to locate the point.
assert(searchtet.tet != NULL); // Start searching from 'searchtet'.
spivot(*misseg, splitsh);
ivf.iloc = (int) OUTSIDE;
ivf.bowywat = 1;
ivf.lawson = 0;
ivf.rejflag = 0;
ivf.chkencflag = 0;
ivf.sloc = (int) ONEDGE;
ivf.sbowywat = 1;
ivf.splitbdflag = 0;
ivf.validflag = 1;
ivf.respectbdflag = 1;
ivf.assignmeshsize = 0;
loc = insertvertex(steinerpt, &searchtet, &splitsh, misseg, &ivf);
assert(loc != (int) ONVERTEX);
assert(loc != (int) NEARVERTEX);
} // if (endi > 0)
// Save this Steiner point (for removal).
// 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' (Q). They will be //
// be recovered one by one. //
// //
// Each segment is first tried to be recovered by a sequence of flips which //
// removes faces intersecting this segment. However, it is not always possi- //
// ble to recover it by only this way. Then, Steiner points will be added to //
// help the recovery of it by flips. //
// //
// If 'steinerflag' is set, Steiner points will be added if a segment is not //
// able to recovered by flips. Otherwise, the segment is not recovered, and //
// it is returned in 'misseglist'. //
// //
///////////////////////////////////////////////////////////////////////////////
int tetgenmesh::recoversegments(arraypool *misseglist, int fullsearch,
int steinerflag)
{
triface searchtet, spintet;
face sseg, checkseg, *paryseg;
point startpt, endpt;
int success;
long bak_inpoly_count = st_volref_count; //st_inpoly_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, &searchtet, 0)) {
success = 1;
} else {
// Try to recover it from the other direction.
if (recoveredgebyflips(endpt, startpt, &searchtet, 0)) {
success = 1;
}
}
if (!success && fullsearch) {
if (recoveredgebyflips(startpt, endpt, &searchtet, fullsearch)) {
success = 1;
}
}
if (success) {
// Segment is recovered. Insert it.
tsspivot1(searchtet, checkseg); // SELF_CHECK
assert(checkseg.sh == NULL);
// Let the segment remember an adjacent tet.
sstbond1(sseg, searchtet);
// Bond the segment to all tets containing it.
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);
}
}
}
return 0;
}
///////////////////////////////////////////////////////////////////////////////
// //
// recoverfacebyflips() Recover a face by flips. //
// //
// If 'searchsh' is not NULL, it is a subface to be recovered. It is only //
// used for checking self-intersections. //
// //
///////////////////////////////////////////////////////////////////////////////
int tetgenmesh::recoverfacebyflips(point pa, point pb, point pc,
face *searchsh, triface* searchtet)
{
triface spintet, flipedge;
face checkseg;
point pd, pe;
enum interresult dir;
flipconstraints fc;
int success, success1;
int i, j;
int intflag;
int types[2], poss[4];
if (b->verbose > 2) {
printf(" Recovering face (%d, %d, %d) by flips\n", pointmark(pa),
pointmark(pb), pointmark(pc));
}
fc.fac[0] = pa;
fc.fac[1] = pb;
fc.fac[2] = pc;
success = 0;
for (i = 0; i < 3 && !success; i++) {
while (1) {
// Get a tet containing the edge [a,b].
point2tetorg(fc.fac[i], *searchtet);
assert(org(*searchtet) == fc.fac[i]); // SELF_CHECK
dir = finddirection(searchtet, fc.fac[(i+1)%3], 1);
//assert(dir == ACROSSVERT);
assert(dest(*searchtet) == fc.fac[(i+1)%3]);
// Search the face [a,b,c]
spintet = *searchtet;
while (1) {
if (apex(spintet) == fc.fac[(i+2)%3]) {
// Found the face.
*searchtet = spintet;
// Return the face [a,b,c].
for (j = i; j > 0; j--) {
eprevself(*searchtet);
}
success = 1;
break;
}
fnextself(spintet);
if (spintet.tet == searchtet->tet) break;
} // while (1)
if (success) break;
// The face is missing. Try to recover it.
success1 = 0;
// Find a crossing edge of this face.
spintet = *searchtet;
while (1) {
pd = apex(spintet);
pe = oppo(spintet);
if ((pd != dummypoint) && (pe != dummypoint)) {
// Check if [d,e] intersects [a,b,c]
intflag = tri_edge_test(pa, pb, pc, pd, pe, NULL, 1, types, poss);
if (intflag > 0) {
// By our assumptions, they can only intersect at a single point.
if (intflag == 2) {
// Check the intersection type.
dir = (enum interresult) types[0];
if ((dir == ACROSSFACE) || (dir == ACROSSEDGE)) {
// Go to the edge [d,e].
eprev(spintet, flipedge);
esymself(flipedge);
enextself(flipedge); // [d,e,a,b].
if (searchsh != NULL) {
// Check if [e,d] is a segment.
tsspivot1(flipedge, checkseg);
if (checkseg.sh != NULL) {
if (!b->quiet) {
printf("Found a segment and a subface intersect.\n");
pd = farsorg(checkseg);
pe = farsdest(checkseg);
printf(" 1st: [%d, %d] %d.\n", pointmark(pd),
pointmark(pe), shellmark(checkseg));
printf(" 2nd: [%d,%d,%d] %d\n", pointmark(pa),
pointmark(pb), pointmark(pc), shellmark(*searchsh));
}
terminatetetgen(3);
}
}
// Try to flip the edge [d,e].
success1 = (removeedgebyflips(&flipedge, &fc) == 2);
} else {
if (dir == TOUCHFACE) {
point touchpt, *parypt;
if (poss[0] == 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.
if (b->verbose > 2) {
printf(" Shift volume Steiner point %d to facet.\n",
pointmark(touchpt));
}
face checksh, *parysh;
int siloc = (int) ONFACE;
int sbowat = 0; // Only split this subface.
sinsertvertex(touchpt, searchsh, NULL, siloc, sbowat);
setpointtype(touchpt, FREEFACETVERTEX);
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) {
if (b->verbose > 3) {
printf(" Queue new subface (%d, %d, %d).\n",
pointmark(sorg(checksh)), pointmark(sdest(checksh)),
pointmark(sapex(checksh)));
}
//sdissolve(checksh); // It has not been connected yet.
subfacstack->newindex((void **) &parysh);
*parysh = checksh;
}
}
// Delete the old subfaces in sC(p).
assert(caveshlist->objects == 1);
for (i = 0; i < caveshlist->objects; i++) {
parysh = (face *) fastlookup(caveshlist, i);
shellfacedealloc(subfaces, parysh->sh);
}
// Clear working lists.
caveshlist->restart();
caveshbdlist->restart();
cavesegshlist->restart();
// We can return this function.
searchsh->sh = NULL; // It has been split.
success1 = 0;
success = 1;
} else {
// It should be a PLC problem.
if (pointtype(touchpt) == FREESEGVERTEX) {
// A segment and a subface intersect.
} else if (pointtype(touchpt) == FREEFACETVERTEX) {
// Two facets self-intersect.
}
terminatetetgen(3);
}
} else {
assert(0); // Unknown cases. Debug.
}
}
break;
} else { // intflag == 4. Coplanar case.
// This may be an input PLC error.
assert(0);
}
} // if (intflag > 0)
}
fnextself(spintet);
assert(spintet.tet != searchtet->tet);
} // while (1)
if (!success1) 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], checkseg;
point startpt, endpt, apexpt, *parypt;
point steinerpt;
enum interresult dir;
insertvertexflags ivf;
int success;
int loc;
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) {
assert(0);
}
} else {
// This edge is not a segment (due to a Steiner point).
// Check whether it exists or not.
success = 0;
startpt = sorg(searchsh);
endpt = sdest(searchsh);
point2tetorg(startpt, searchtet);
assert(org(searchtet) == startpt); // SELF_CHECK
dir = finddirection(&searchtet, endpt, 1);
if (dir == ACROSSVERT) {
if (dest(searchtet) == endpt) {
success = 1;
} else {
//assert(0); // A PLC problem.
terminatetetgen(3);
}
} else {
// The edge is missing. Try to recover it.
if (recoveredgebyflips(startpt, endpt, &searchtet, 0)) {
success = 1;
} else {
if (recoveredgebyflips(endpt, startpt, &searchtet, 0)) {
success = 1;
}
}
}
if (success) {
// Insert a temporary segment to protect this edge.
if (b->verbose > 2) {
printf(" Insert a temp segment to protect edge [%d, %d].\n",
pointmark(startpt), pointmark(endpt));
}
makeshellface(subsegs, &(bdsegs[i]));
setshvertices(bdsegs[i], startpt, endpt, NULL);
//setshellmark(bdsegs[i], -2); // It's a temporary segment.
smarktest2(bdsegs[i]); // It's a temporary segment.
// Insert this segment into surface mesh.
ssbond(searchsh, bdsegs[i]);
spivot(searchsh, neighsh);
if (neighsh.sh != NULL) {
ssbond(neighsh, bdsegs[i]);
}
// Insert this segment into tetrahedralization.
tsspivot1(searchtet, checkseg); // SELF_CHECK
assert(checkseg.sh == NULL);
sstbond1(bdsegs[i], searchtet);
// Bond the segment to all tets containing it.
spintet = searchtet;
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])) { // if (shellmark(bdsegs[j]) == -2) {
if (b->verbose > 2) {
printf(" Remove a temp segment (%d, %d).\n",
pointmark(sorg(bdsegs[j])), pointmark(sdest(bdsegs[j])));
}
spivot(bdsegs[j], neineish);
assert(neineish.sh != NULL);
//if (neineish.sh != NULL) {
ssdissolve(neineish);
spivot(neineish, neighsh);
if (neighsh.sh != NULL) {
ssdissolve(neighsh);
// There should be only two subfaces at this segment.
spivotself(neighsh); // SELF_CHECK
assert(neighsh.sh == neineish.sh);
}
//}
sstpivot1(bdsegs[j], searchtet);
assert(searchtet.tet != NULL);
//if (searchtet.tet != NULL) {
spintet = searchtet;
while (1) {
tssdissolve1(spintet);
fnextself(spintet);
if (spintet.tet == searchtet.tet) break;
}
//}
shellfacedealloc(subsegs, bdsegs[j].sh);
}
} // j
if (steinerflag) {
// Add a Steiner point at the midpoint of this edge.
if (b->verbose > 2) {
printf(" Add a Steiner point in subedge (%d, %d).\n",
pointmark(startpt), pointmark(endpt));
}
makepoint(&steinerpt, FREEFACETVERTEX);
for (j = 0; j < 3; j++) {
steinerpt[j] = 0.5 * (startpt[j] + endpt[j]);
}
point2tetorg(startpt, searchtet); // Start from 'searchtet'.
ivf.iloc = (int) OUTSIDE; // Need point location.
ivf.bowywat = 1;
ivf.lawson = 0;
ivf.rejflag = 0;
ivf.chkencflag = 0;
ivf.sloc = (int) ONEDGE;
ivf.sbowywat = 1; // Allow flips in facet.
ivf.splitbdflag = 0;
ivf.validflag = 1;
ivf.respectbdflag = 1;
ivf.assignmeshsize = 0;
loc = insertvertex(steinerpt, &searchtet, &searchsh, NULL, &ivf);
assert(loc != (int) OUTSIDE);
// Save this Steiner point (for removal).
// Re-use the array 'subvertstack'.
subvertstack->newindex((void **) &parypt);
*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])) { //if (shellmark(bdsegs[j]) == -2) {
if (b->verbose > 2) {
printf(" Remove a temp segment (%d, %d).\n",
pointmark(sorg(bdsegs[j])), pointmark(sdest(bdsegs[j])));
}
spivot(bdsegs[j], neineish);
assert(neineish.sh != NULL);
//if (neineish.sh != NULL) {
ssdissolve(neineish);
spivot(neineish, neighsh);
if (neighsh.sh != NULL) {
ssdissolve(neighsh);
// There should be only two subfaces at this segment.
spivotself(neighsh); // SELF_CHECK
assert(neighsh.sh == neineish.sh);
}
//}
sstpivot1(bdsegs[j], neightet);
assert(neightet.tet != NULL);
//if (neightet.tet != NULL) {
spintet = neightet;
while (1) {
tssdissolve1(spintet);
fnextself(spintet);
if (spintet.tet == neightet.tet) break;
}
//}
shellfacedealloc(subsegs, bdsegs[j].sh);
}
} // j
if (success) {
if (searchsh.sh != NULL) {
// Face is recovered. Insert it.
tsbond(searchtet, searchsh);
fsymself(searchtet);
sesymself(searchsh);
tsbond(searchtet, searchsh);
}
} else {
if (steinerflag) {
// Add a Steiner point at the barycenter of this subface.
if (b->verbose > 2) {
printf(" Add a Steiner point in subface (%d, %d, %d).\n",
pointmark(startpt), pointmark(endpt), pointmark(apexpt));
}
makepoint(&steinerpt, FREEFACETVERTEX);
for (j = 0; j < 3; j++) {
steinerpt[j] = (startpt[j] + endpt[j] + apexpt[j]) / 3.0;
}
point2tetorg(startpt, searchtet); // Start from 'searchtet'.
ivf.iloc = (int) OUTSIDE; // Need point location.
ivf.bowywat = 1;
ivf.lawson = 0;
ivf.rejflag = 0;
ivf.chkencflag = 0;
ivf.sloc = (int) ONFACE;
ivf.sbowywat = 1; // Allow flips in facet.
ivf.splitbdflag = 0;
ivf.validflag = 1;
ivf.respectbdflag = 1;
ivf.assignmeshsize = 0;
loc = insertvertex(steinerpt, &searchtet, &searchsh, NULL, &ivf);
assert(loc != (int) OUTSIDE);
// Save this Steiner point (for removal).
// Re-use the array 'subvertstack'.
subvertstack->newindex((void **) &parypt);
*parypt = steinerpt;
st_facref_count++;
if (steinerleft > 0) steinerleft--;
} // if (steinerflag)
}
} else {
success = 0;
}
if (!success) {
if (misshlist != NULL) {
if (b->verbose > 2) {
printf(" Subface (%d, %d, %d) is missing.\n",
pointmark(sorg(searchsh)), pointmark(sdest(searchsh)),
pointmark(sapex(searchsh)));
}
// Save this subface.
misshlist->newindex((void **) &parysh);
*parysh = searchsh;
}
}
} // 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 oppsiting 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;
//face checkseg;
point pt, *parypt;
int collectflag;
int i, j;
if (b->verbose > 2) {
printf(" Form the star of vertex %d.\n", pointmark(searchpt));
}
point2tetorg(searchpt, searchtet);
// Go to the opposite face (the link face) of the vertex.
enextself(searchtet);
esymself(searchtet);
//assert(oppo(searchtet) == searchpt);
infect(searchtet); // Collect this tet (link face).
tetlist->newindex((void **) &parytet);
*parytet = searchtet;
if (vertlist != NULL) {
// Collect three (link) vertices.
for (i = 0; i < 3; i++) {
pt = org(searchtet);
pinfect(pt);
vertlist->newindex((void **) &parypt);
*parypt = pt;
enextself(searchtet);
}
}
collectflag = 1;
esym(searchtet, neightet);
tspivot(neightet, checksh);
if (checksh.sh != NULL) {
if (shlist != NULL) {
if (!sinfected(checksh)) {
// Collect this subface (link edge).
sinfected(checksh);
shlist->newindex((void **) &parysh);
*parysh = checksh;
}
} // if (checksh.sh != NULL)
if (!fullstar) {
collectflag = 0;
}
}
if (collectflag) {
fsymself(neightet); // Goto the adj tet of this face.
assert(neightet.tet != NULL);
esymself(neightet); // Goto the oppo face of this vertex.
// assert(oppo(neightet) == searchpt);
infect(neightet); // Collect this tet (link face).
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 neighors at the other two edges of this face.
for (j = 0; j < 2; j++) {
collectflag = 1;
enextself(searchtet);
//fnext(searchtet, neightet);
esym(searchtet, neightet);
tspivot(neightet, checksh);
if (checksh.sh != NULL) {
if (shlist != NULL) {
if (!sinfected(checksh)) {
// Collect this subface (link edge).
sinfected(checksh);
shlist->newindex((void **) &parysh);
*parysh = checksh;
}
}
if (!fullstar) {
collectflag = 0;
}
}
if (collectflag) {
fsymself(neightet);
assert(neightet.tet != NULL);
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
if (b->verbose > 2) {
printf(" Collected %ld tets", tetlist->objects);
if (vertlist != NULL) {
printf(", %ld vertices", vertlist->objects);
}
if (shlist != NULL) {
printf(", %ld subfaces", shlist->objects);
}
printf(".\n");
}
// Uninfect the list of tets and vertices.
for (i = 0; i < tetlist->objects; i++) {
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, 1);
if (dest(*tedge) == e2) {
return 1;
} else {
// Search for the edge [e2, e1].
point2tetorg(e2, *tedge);
finddirection(tedge, e1, 1);
if (dest(*tedge) == e1) {
esymself(*tedge);
return 1;
}
}
// Go to the link face of e1.
point2tetorg(e1, searchtet);
enextself(searchtet);
esymself(searchtet);
//assert(oppo(searchtet) == e1);
assert(cavetetlist->objects == 0l); // It will re-use this list.
// Search e2.
for (i = 0; i < 3; i++) {
pt = apex(searchtet);
if (pt == e2) {
// Found. 'searchtet' is [#,#,e2,e1].
enext(searchtet, *tedge);
esymself(*tedge);
eprevself(*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].
enext(neightet, *tedge);
esymself(*tedge);
eprevself(*tedge); // [e1,e2,#,#].
return 1;
}
// Continue searching in the link face of e1.
infect(searchtet);
cavetetlist->newindex((void **) &parytet);
*parytet = searchtet;
infect(neightet);
cavetetlist->newindex((void **) &parytet);
*parytet = neightet;
done = 0;
for (i = 0; (i < cavetetlist->objects) && !done; i++) {
parytet = (triface *) fastlookup(cavetetlist, 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].
enext(neightet, *tedge);
esymself(*tedge);
eprevself(*tedge); // [e1,e2,#,#].
done = 1;
} else {
infect(neightet);
cavetetlist->newindex((void **) &parytet);
*parytet = neightet;
}
}
} // j
} // i
// Uninfect the list of visited tets.
for (i = 0; i < cavetetlist->objects; i++) {
parytet = (triface *) fastlookup(cavetetlist, i);
uninfect(*parytet);
}
cavetetlist->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;
face checkseg;
point *pendpt, *parypt;
enum interresult dir;
flipconstraints fc;
int reduceflag;
int count;
int n, i, j;
if (b->verbose > 2) {
printf(" Initial edge degree = %ld.\n", endptlist->objects);
}
assert(endptlist->objects >= 4l);
// Reduce the number of edges.
fc.remvert = startpt;
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, 1);
}
if (dir == ACROSSVERT) {
if (dest(searchtet) == *pendpt) {
// Do not flip a segment.
tsspivot1(searchtet, checkseg);
if (checkseg.sh == NULL) {
n = removeedgebyflips(&searchtet, &fc);
if (n == 2) {
reduceflag = 1;
}
}
} else {
assert(0); // A plc problem.
}
} 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)
if (b->verbose > 2) {
printf(" Final edge degree = %ld.\n", endptlist->objects);
}
return (int) endptlist->objects;
}
///////////////////////////////////////////////////////////////////////////////
// //
// removevertexbyflips() Remove a vertex by flips. //
// //
// This routine attempts to remove the given vertex 'rempt' (p) from the cur-//
// rent tetrahedralization (T) by a sequence of elementary flips. //
// //
// The algorithm used here is a simple edge reduce method. Suppose there are //
// n edges connected at p. We try to reduce the number of edges by flipping //
// any edge (not a segment) that is connecting at p. //
// //
// The original location of 'p' in the tetrahedralization without 'p' is ind-//
// icated by 'iloc'. 'searchtet' (t) is a tet in the current tetrahedralizat-//
// ion which contains 'p'. Depending on 'iloc', it means the followig: //
// - INTET: the origin of 't' is 'p'; //
// - ONFACE: the origin of 't' is 'p', the face of 't' was split by 'p'; //
// - ONEDGE: the origin of 't' is 'p', the edge of 't' was split by 'p'; //
// //
// If 'parentsh' (s) is given (not NULL), it indicates that 'p' is a Steiner //
// point on a facet, and 's' was a subface created by the insertion of 'p'. //
// 'iloc' must be either ONFACE or 'OEDGE'. The origin of 's' is 'p'. //
// //
// If 'parentseg' (seg) is given (not NULL), it indicated that 'p' is a //
// Steiner point on a segment, and 'seg' was a subsegment created by 'p'. //
// 'iloc' must be ONEDGE. The original of 'seg' is 'p'. //
// //
// Unless T is a Delaunay tetrahedralization, there is no guarantee that 'p' //
// can be successfully removed. //
// //
///////////////////////////////////////////////////////////////////////////////
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;
enum verttype vt;
enum locateresult loc;
int valence, removeflag;
int slawson;
int n, i;
vt = pointtype(steinerpt);
if (vt == FREESEGVERTEX) {
sdecode(point2sh(steinerpt), leftseg);
assert(leftseg.sh != NULL);
leftseg.shver = 0;
if (sdest(leftseg) == steinerpt) {
senext(leftseg, rightseg);
spivotself(rightseg);
assert(rightseg.sh != NULL);
rightseg.shver = 0;
assert(sorg(rightseg) == steinerpt);
} else {
assert(sorg(leftseg) == steinerpt);
rightseg = leftseg;
senext2(rightseg, leftseg);
spivotself(leftseg);
assert(leftseg.sh != NULL);
leftseg.shver = 0;
assert(sdest(leftseg) == steinerpt);
}
lpt = sorg(leftseg);
rpt = sdest(rightseg);
if (b->verbose > 2) {
printf(" Removing Steiner point %d in segment (%d, %d).\n",
pointmark(steinerpt), pointmark(lpt), pointmark(rpt));
}
} else if (vt == FREEFACETVERTEX) {
if (b->verbose > 2) {
printf(" Removing Steiner point %d in facet.\n",
pointmark(steinerpt));
}
} else if (vt == FREEVOLVERTEX) {
if (b->verbose > 2) {
printf(" Removing Steiner point %d in volume.\n",
pointmark(steinerpt));
}
} else {
// It is not a Steiner point.
return 0;
}
// Try to reduce the number of edges at 'p' by flips.
getvertexstar(1, steinerpt, cavetetlist, cavetetvertlist, NULL);
cavetetlist->restart(); // This list may be re-used.
if (cavetetvertlist->objects > 3l) {
valence = reduceedgesatvertex(steinerpt, cavetetvertlist);
} else {
valence = cavetetvertlist->objects;
}
assert(cavetetlist->objects == 0l);
cavetetvertlist->restart();
removeflag = 0;
if (valence < 3) {
assert(0); // Unknown cases.
}
if (valence == 3) {
// Only three edges at this vertex. This is only possible when there are
// Inverted elements.
getvertexstar(1, steinerpt, cavetetlist, NULL, NULL);
if (cavetetlist->objects == 2) {
printf("to be continued...");
assert(0);
} else {
assert(0); // Unknown cases.
}
cavetetlist->restart();
loc = OUTSIDE;
removeflag = 1;
} else if (valence == 4) {
// Only 4 vertices (4 tets) left! 'p' is inside the convex hull of the 4
// vertices. This case is due to that 'p' is not exactly on the segment.
point2tetorg(steinerpt, searchtet);
loc = INTETRAHEDRON;
removeflag = 1;
} else if (valence == 5) {
// There are 5 edges.
if (vt == FREESEGVERTEX) {
sstpivot1(leftseg, searchtet);
if (org(searchtet) != steinerpt) {
esymself(searchtet);
}
assert(org(searchtet) == steinerpt);
assert(dest(searchtet) == lpt);
i = 0; // Count the numbe of tet at the edge [p,lpt].
neightet.tet = NULL; // Init the face.
spintet = searchtet;
while (1) {
i++;
if (apex(spintet) == rpt) {
// Remember the face containing the edge [lpt, rpt].
neightet = spintet;
}
fnextself(spintet);
if (spintet.tet == searchtet.tet) break;
}
if (i == 3) {
// This case has been checked below.
} else if (i == 4) {
// There are 4 tets sharing at [p,lpt]. There must be 4 tets sharing
// at [p,rpt]. There must be a face [p, lpt, rpt].
if (apex(neightet) == rpt) {
// The edge (segment) has been already recovered! At first, this is
// due to the same reason as the case 'valence == 4'. Second,
// there are 4 vertices (including p, lpt, rpt) exactly coplanar.
// We can do a 6-to-2 flip to remove p and recover a face
// [lpt, rpt, c] = [a,b,c].
// Let 'searchtet' be [p,d,a,b]
esym(neightet, searchtet);
enextself(searchtet);
loc = ONFACE;
removeflag = 1;
}
}
} else if (vt == FREEFACETVERTEX) {
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]);
// Get the one which has a subface (should be only 1).
n = -1;
valence = 0; // Re-use it as a counter.
for (i = 0; i < 3; i++) {
tspivot(wrktets[i], checksh);
if (checksh.sh != NULL) {
n = i;
valence++;
}
}
assert(valence == 1);
searchtet = wrktets[n];
esymself(searchtet);
enextself(searchtet);
loc = ONFACE;
removeflag = 1;
} else {
// assert(0); DEBUG IT
}
//removeflag = 1;
} else { // valence > 5.
} // if (valence > 5)
if (!removeflag) {
if (vt == FREESEGVERTEX) {
// Check is it possible to recover the edge [lpt,rpt].
// The condition to check is: Whether each tet containing 'leftseg' is
// adjacent to a tet containing 'rightseg'.
sstpivot1(leftseg, searchtet);
if (org(searchtet) != steinerpt) {
esymself(searchtet);
}
assert(org(searchtet) == steinerpt);
assert(dest(searchtet) == lpt);
spintet = searchtet;
while (1) {
// Go to the bottom face of this tet.
eprev(spintet, neightet);
esymself(neightet); // [steinerpt, p1, p2, lpt]
// Get the adjacent tet.
fsymself(neightet); // [p1, steinerpt, p2, rpt]
if (oppo(neightet) != rpt) {
// Found a non-matching adjacent tet.
break;
}
fnextself(spintet);
if (spintet.tet == searchtet.tet) {
// 'searchtet' is [p,d,p1,p2].
loc = ONEDGE;
removeflag = 1;
break;
}
}
} // if (vt == FREESEGVERTEX)
}
if (!removeflag) {
if (vt == FREESEGVERTEX) {
// Check if the edge [lpr, rpt] exists.
if (getedge(lpt, rpt, &searchtet)) {
// We have recovered this edge. Shift the vertex into the volume.
// We can recover this edge if the subfaces are not recovered yet.
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 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
slawson = 1; // Do lawson flip after removal.
spivot(rightseg, parentsh); // 'rightseg' has p as its origin.
sremovevertex(steinerpt, &parentsh, &rightseg, slawson);
// Clear the list for new subfaces.
caveshbdlist->restart();
// Insert the new segment.
assert(org(searchtet) == lpt);
assert(dest(searchtet) == rpt);
sstbond1(rightseg, searchtet);
spintet = searchtet;
while (1) {
tsspivot1(spintet, checkseg); // FOR DEBUG ONLY
assert(checkseg.sh == NULL); // FOR DEBUG ONLY
tssbond1(spintet, rightseg);
fnextself(spintet);
if (spintet.tet == searchtet.tet) break;
}
// The Steiner point has been shifted into the volume.
setpointtype(steinerpt, FREEVOLVERTEX);
st_segref_count--;
st_volref_count++;
// Save this Steiner points in (global) list.
suppsteinerptlist->newindex((void **) &parypt);
*parypt = steinerpt;
return 1;
} // if (!checksubfaceflag)
} // if (getedge(...))
} // if (vt == FREESEGVERTEX)
} // if (!removeflag)
if (!removeflag) {
if (b->verbose > 2) {
printf(" Unable to remove Steiner point %d val(%d).\n",
pointmark(steinerpt), valence);
}
return 0;
}
assert(org(searchtet) == steinerpt);
if (vt == FREESEGVERTEX) {
// Detach the subsegments from their surronding tets.
for (i = 0; i < 2; i++) {
checkseg = (i == 0) ? leftseg : rightseg;
sstpivot1(checkseg, neightet);
spintet = neightet;
while (1) {
tssdissolve1(spintet);
fnextself(spintet);
if (spintet.tet == neightet.tet) break;
}
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);
//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]
}
// 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);
// DEBUG BEGIN
// fliptets[3] is [a,b,c,p], check it.
assert(org(fliptets[3]) == apex(fliptets[0])); // a
assert(dest(fliptets[3]) == apex(fliptets[1])); // b
assert(apex(fliptets[3]) == apex(fliptets[2])); // c
assert(oppo(fliptets[3]) == steinerpt);
// fliptets[4] is [b,a,c,e].
// DEBUG END
// Second do a 4-to-1 flip on [p,d,a,b],[p,d,b,c],[p,d,c,a],[a,b,c,p].
// This creates a new tet [a,b,c,d].
flip41(fliptets, 1, 0, 0);
//recenttet = fliptets[0];
} else if (loc == ONEDGE) {
// Let the original edge be [e,d] and p is in [e,d]. Assume there are n
// tets sharing at edge [e,d] originally. We number the link vertices
// of [e,d]: p_0, p_1, ..., p_n-1. 'searchtet' is [p,d,p_0,p_1].
// Count the number of tets at edge [e,p] and [p,d] (this is n).
n = 0;
spintet = searchtet;
while (1) {
n++;
fnextself(spintet);
if (spintet.tet == searchtet.tet) break;
}
assert(n >= 3);
// Collect the 2n tets containing 'p'.
fliptets = new triface[2 * n];
fliptets[0] = searchtet; // [p,b,p_0,p_1]
for (i = 0; i < (n - 1); i++) {
fnext(fliptets[i], fliptets[i+1]); // [p,d,p_i,p_i+1].
}
eprev(fliptets[0], fliptets[n]);
fnextself(fliptets[n]); // [p_0,p,p_1,e]
esymself(fliptets[n]); // [p,p_0,e,p_1]
eprevself(fliptets[n]); // [e,p,p_0,p_1]
for (i = n; i < (2 * n - 1); i++) {
fnext(fliptets[i], fliptets[i+1]); // [e,p,p_i,p_i+1].
}
// Remove p by a 2n-to-n flip, it is a sequence of n flips:
// - Do a 2-to-3 flip on
// [p_0,p_1,p,d] and
// [p,p_1,p_0,e].
// This produces:
// [e,d,p_0,p_1],
// [e,d,p_1,p] (degenerated), and
// [e,d,p,p_0] (degenerated).
wrktets[0] = fliptets[0]; // [p,d,p_0,p_1]
eprevself(wrktets[0]); // [p_0,p,d,p_1]
esymself(wrktets[0]); // [p,p_0,p_1,d]
enextself(wrktets[0]); // [p_0,p_1,p,d] [0]
wrktets[1] = fliptets[n]; // [e,p,p_0,p_1]
enextself(wrktets[1]); // [p,p_0,e,p_1]
esymself(wrktets[1]); // [p_0,p,p_1,e]
eprevself(wrktets[1]); // [p_1,p_0,p,e] [1]
flip23(wrktets, 1, 0, 0);
// 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);
// 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);
// Save the new tet [e,d,p_n-1,p_0] // FOR DEBUG ONLY
fliptets[n-1] = wrktets[0]; // [e,d,p_n-1,p_0] // FOR DEBUG ONLY
//recenttet = fliptets[0];
} else {
assert(0); // Unknown location.
} // if (iloc == ...)
delete [] fliptets;
if (vt == FREESEGVERTEX) {
// Remove the vertex from the surface mesh.
// This will re-create the segment [lpt, rpt] and re-triangulate
// all the facets at the segment.
// Only do lawson flip when subfaces are not recovery yet.
slawson = (checksubfaceflag ? 0 : 1);
spivot(rightseg, parentsh); // 'rightseg' has p as its origin.
sremovevertex(steinerpt, &parentsh, &rightseg, slawson);
// The original segment is returned in 'rightseg'.
rightseg.shver = 0;
assert(sorg(rightseg) == lpt);
assert(sdest(rightseg) == rpt);
// Insert the new segment.
point2tetorg(lpt, searchtet);
finddirection(&searchtet, rpt, 1);
assert(dest(searchtet) == rpt);
sstbond1(rightseg, searchtet);
spintet = searchtet;
while (1) {
tsspivot1(spintet, checkseg); // FOR DEBUG ONLY
assert(checkseg.sh == NULL); // FOR DEBUG ONLY
tssbond1(spintet, rightseg);
fnextself(spintet);
if (spintet.tet == searchtet.tet) break;
}
if (checksubfaceflag) {
// Insert subfaces at segment [lpt,rpt] into the tetrahedralization.
spivot(rightseg, parentsh);
if (parentsh.sh != NULL) {
spinsh = parentsh;
while (1) {
if (sorg(spinsh) != lpt) {
sesymself(spinsh);
assert(sorg(spinsh) == lpt);
}
assert(sdest(spinsh) == rpt);
apexpt = sapex(spinsh);
// Find the adjacent tet of [lpt,rpt,apexpt];
spintet = searchtet;
while (1) {
if (apex(spintet) == apexpt) {
tsbond(spintet, spinsh);
sesymself(spinsh); // Get to another side of this face.
fsym(spintet, neightet);
tsbond(neightet, spinsh);
sesymself(spinsh); // Get back to the original side.
break;
}
fnextself(spintet);
assert(spintet.tet != searchtet.tet);
//if (spintet.tet == searchtet.tet) break;
}
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.
setpointtype(steinerpt, UNUSEDVERTEX);
unuverts++;
// 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;
}
///////////////////////////////////////////////////////////////////////////////
// //
// suppresssteinerpoint() Suppress a Steiner point. //
// //
// Remove a Steiner point 'p' from the segment it lies on. It is replaced by //
// a set of volume Steiner points in each sector at the segment. //
// //
// The list of volume Steiner points is returned in 'suppsteinerptlist'. //
// //
///////////////////////////////////////////////////////////////////////////////
int tetgenmesh::suppressssteinerpoint(point steinerpt)
{
triface searchtet, neightet, spintet, *parytet;
triface newtet, newface;
face parentsh, spinsh, *parysh;
face newsh, neighsh;
face leftseg, rightseg, checkseg, *splitseg;
point lpt = NULL, rpt = NULL, newpt, *parypt;
point pa, pb, pc;
verttype vt;
long bak_supp_steiners;
int slawson;
int i, j, k;
vt = pointtype(steinerpt);
if (vt == FREESEGVERTEX) {
sdecode(point2sh(steinerpt), leftseg);
assert(leftseg.sh != NULL);
leftseg.shver = 0;
if (sdest(leftseg) == steinerpt) {
senext(leftseg, rightseg);
spivotself(rightseg);
assert(rightseg.sh != NULL);
rightseg.shver = 0;
assert(sorg(rightseg) == steinerpt);
} else {
assert(sorg(leftseg) == steinerpt);
rightseg = leftseg;
senext2(rightseg, leftseg);
spivotself(leftseg);
assert(leftseg.sh != NULL);
leftseg.shver = 0;
assert(sdest(leftseg) == steinerpt);
}
lpt = sorg(leftseg);
rpt = sdest(rightseg);
if (b->verbose > 2) {
printf(" Suppressing point %d from segment (%d, %d).\n",
pointmark(steinerpt), pointmark(lpt), pointmark(rpt));
}
} else if (vt == FREEFACETVERTEX) {
if (b->verbose > 2) {
printf(" Suppressing point %d from facet.\n",
pointmark(steinerpt));
}
//point2shorg(steinerpt, parentsh);
getvertexstar(0, steinerpt, cavetetlist, NULL, caveshlist);
parysh = (face *) fastlookup(caveshlist, 0);
parentsh = *parysh;
//assert(sapex(parentsh) == steinerpt);
senext2self(parentsh);
assert(sorg(parentsh) == steinerpt);
cavetetlist->restart();
caveshlist->restart();
} else {
// Do nothing.
return 0;
}
if (vt == FREESEGVERTEX) {
// Check if this edge [lpt, rpt] already exists.
if (getedge(lpt, rpt, &searchtet)) {
tsspivot1(searchtet, checkseg); // SELF_CHECK
assert(checkseg.sh == NULL);
return 0;
}
}
bak_supp_steiners = suppsteinerptlist->objects;
if (vt == FREESEGVERTEX) {
// Get all subfaces at the left segment [lpt, steinerpt].
spivot(leftseg, parentsh);
spinsh = parentsh;
while (1) {
cavesegshlist->newindex((void **) &parysh);
*parysh = spinsh;
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;
}
// 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;
}
} else { // vt == FREEFACETVERTEX
// A facet Steiner point. There are exactly two sectors.
for (i = 0; i < 2; i++) {
cavesegshlist->newindex((void **) &parysh);
*parysh = parentsh;
sesymself(parentsh);
}
}
// 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);
assert(caveshlist->objects > 0);
// Create a new vertex 'np'.
makepoint(&newpt, FREEVOLVERTEX);
st_volref_count++;
// Init 'np' at the same location of 'p'.
for (j = 0; j < 3; j++) newpt[j] = steinerpt[j];
// Within the tet, replace 'p' by 'np'.
for (j = 0; j < cavetetlist->objects; j++) {
parytet = (triface *) fastlookup(cavetetlist, j);
setoppo(*parytet, newpt);
} // j
// Save the new Steiner point in list.
suppsteinerptlist->newindex((void **) &parypt);
*parypt = newpt;
// 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();
// Remove p from the segment.
slawson = 0; // Do not do flip afterword.
if (vt == FREESEGVERTEX) {
spivot(rightseg, parentsh); // 'rightseg' has p as its origin.
splitseg = &rightseg;
} else {
assert(sorg(parentsh) == steinerpt);
splitseg = NULL;
}
sremovevertex(steinerpt, &parentsh, splitseg, slawson);
if (vt == FREESEGVERTEX) {
// The original segment is returned in 'rightseg'.
rightseg.shver = 0;
assert(sorg(rightseg) == lpt);
assert(sdest(rightseg) == rpt);
}
// The set of new subfaces are found in 'caveshbdlist'.
assert(caveshbdlist->objects > 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);
}
if (vt == FREESEGVERTEX) {
// Connecting new tets at the recovered segment.
spivot(rightseg, parentsh);
assert(parentsh.sh != NULL);
spinsh = parentsh;
while (1) {
assert(sinfected(spinsh));
if (sorg(spinsh) != lpt) sesymself(spinsh);
assert(sorg(spinsh) == lpt);
assert(sdest(spinsh) == rpt);
// Get the new tet at this subface.
stpivot(spinsh, newtet);
assert(oppo(newtet) == dummypoint);
tssbond1(newtet, rightseg);
// Go to the other face at this segment.
esymself(newtet);
assert(org(newtet) == rpt);
assert(newtet.tet[newtet.ver & 3] == NULL);
// Get the adjacent tet at this segment.
spivot(spinsh, neighsh);
if (sorg(neighsh) != lpt) sesymself(neighsh);
sesymself(neighsh);
stpivot(neighsh, neightet);
assert(oppo(neightet) == dummypoint);
tssbond1(neightet, rightseg);
sstbond1(rightseg, neightet);
// Go to the other face at this segment.
esymself(neightet);
assert(org(neightet) == lpt);
assert(neightet.tet[neightet.ver & 3] == NULL);
// 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.
assert(checkseg.sh != rightseg.sh);
tssbond1(newtet, checkseg);
//sstbond1(checkseg, newtet);
}
spivot(newsh, neighsh);
if (neighsh.sh != NULL) {
// The adjacent subface exists. It's not a dangling segment.
if (sorg(neighsh) != sdest(newsh)) sesymself(neighsh);
stpivot(neighsh, neightet);
if (sinfected(neighsh)) {
esymself(neightet);
assert(neightet.tet[neightet.ver & 3] == NULL);
} else {
// Search for an open face at this edge.
spintet = neightet;
while (1) {
esym(spintet, searchtet);
fsym(searchtet, spintet);
if (spintet.tet == NULL) break;
assert(spintet.tet != neightet.tet);
}
// Found an open face at 'searchtet'.
neightet = searchtet;
}
} else {
// The edge (at 'newsh') is a dangling segment.
assert(checkseg.sh != NULL);
// Get an adjacent tet at this segment.
sstpivot1(checkseg, neightet);
assert(!isdeadtet(neightet));
if (org(neightet) != sdest(newsh)) esymself(neightet);
assert((org(neightet) == sdest(newsh)) &&
(dest(neightet) == sorg(newsh)));
// Search for an open face at this edge.
spintet = neightet;
while (1) {
esym(spintet, searchtet);
fsym(searchtet, spintet);
if (spintet.tet == NULL) break;
assert(spintet.tet != neightet.tet);
}
// Found an open face at 'searchtet'.
neightet = searchtet;
}
pc = apex(newface);
if (pc == dummypoint) {
setapex(newface, apex(neightet));
} else {
assert(pc == apex(neightet));
}
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();
setpointtype(steinerpt, UNUSEDVERTEX);
unuverts++;
if (vt == FREESEGVERTEX) {
st_segref_count--;
} else { // vt == FREEFACETVERTEX
st_facref_count--;
}
if (steinerleft > 0) steinerleft++;
if (b->verbose > 2) {
printf(" Duplicated %ld Steiner points.\n",
suppsteinerptlist->objects - bak_supp_steiners);
}
return 1;
}
///////////////////////////////////////////////////////////////////////////////
// //
// suppresssteinerpoints() Suppress Steiner points. //
// //
// Each Steiner point is either removed or shifted into the interior. //
// //
///////////////////////////////////////////////////////////////////////////////
int tetgenmesh::suppresssteinerpoints()
{
triface *parytet;
point rempt, *parypt, *plastpt, *ppt;
optparameters opm;
REAL ori;
int bak_fliplinklevel;
int remcount, smtcount;
int count, nt;
int i, j;
if (!b->quiet) {
printf("Suppressing Steiner points ...\n");
}
bak_fliplinklevel = b->fliplinklevel;
b->fliplinklevel = 100000; // Unlimited flip level.
remcount = 0;
if (b->nobisect_param > 1) { // -Y2
// Try to remove all the Steiner points.
for (i = 0; i < subvertstack->objects; i++) {
parypt = (point *) fastlookup(subvertstack, i);
rempt = *parypt;
if (pointtype(rempt) != UNUSEDVERTEX) {
if (removevertexbyflips(rempt)) {
remcount++;
}
}
}
if (b->verbose) {
if (remcount > 0) {
printf(" Removed %d Steiner points.\n", remcount);
}
}
subvertstack->restart();
}
remcount = smtcount = 0;
// Try to remove the suppressed Steiner points.
for (i = 0; i < suppsteinerptlist->objects; i++) {
// Get the Steiner point.
parypt = (point *) fastlookup(suppsteinerptlist, i);
rempt = *parypt;
if (pointtype(rempt) != UNUSEDVERTEX) {
assert((pointtype(rempt) == FREESEGVERTEX) ||
(pointtype(rempt) == FREEFACETVERTEX) ||
(pointtype(rempt) == FREEVOLVERTEX));
if (removevertexbyflips(rempt)) {
// Move the last entry to fill the current one.
j = (int) (suppsteinerptlist->objects - 1);
plastpt = (point *) fastlookup(suppsteinerptlist, j);
*parypt = *plastpt;
suppsteinerptlist->objects--;
i--;
remcount++;
}
} else {
// The point has been removed.
// Move the last entry to fill the current one.
j = (int) (suppsteinerptlist->objects - 1);
plastpt = (point *) fastlookup(suppsteinerptlist, j);
*parypt = *plastpt;
suppsteinerptlist->objects--;
i--;
}
} // i
if (b->verbose) {
if (remcount > 0) {
printf(" Removed %d suppressed Steiner points.\n", remcount);
}
}
if (suppsteinerptlist->objects == 0l) {
b->fliplinklevel = bak_fliplinklevel;
return remcount;
}
// Point smooth options.
opm.max_min_volume = 1;
opm.numofsearchdirs = 20;
opm.searchstep = 0.001;
nt = 0;
while (1) {
// Try to smooth volume Steiner points.
count = 0;
for (i = 0; i < suppsteinerptlist->objects; i++) {
parypt = (point *) fastlookup(suppsteinerptlist, i);
rempt = *parypt;
if (pointtype(rempt) == FREEVOLVERTEX) {
getvertexstar(1, rempt, cavetetlist, NULL, NULL);
// Calculate the initial smallest volume (maybe zero or negative).
for (j = 0; j < cavetetlist->objects; j++) {
parytet = (triface *) fastlookup(cavetetlist, j);
ppt = (point *) &(parytet->tet[4]);
ori = orient3d(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++;
}
cavetetlist->restart();
}
} // i
smtcount += count;
if (count == 0) {
// No point has been smoothed.
break;
}
nt++;
if (nt > 2) {
break; // Already three iterations.
}
} // while
// The mesh should not contain inverted (or degenrrated) tets now.
checkinverttetflag = 0;
if (b->verbose) {
if (smtcount > 0) {
printf(" Smoothed %d Steiner points.\n", smtcount);
}
}
b->fliplinklevel = bak_fliplinklevel;
return smtcount;
}
///////////////////////////////////////////////////////////////////////////////
// //
// recoverboundary() Recover segments and facets. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::recoverboundary(clock_t& tv)
{
arraypool *misseglist, *misshlist;
arraypool *bdrysteinerptlist;
face searchsh, *parysh;
face searchseg, *paryseg;
point rempt, *parypt;
long ms; // The number of missing segments/subfaces.
int nit; // The number of iterations.
int s, i;
// Counters.
long bak_segref_count, bak_facref_count, bak_volref_count;
long bak_supp_count;
if (!b->quiet) {
printf("Recovering boundaries...\n");
}
if (b->verbose) {
printf(" Flip link level = %d\n", b->fliplinklevel);
}
//markacutevertices();
if (b->verbose) {
printf(" Recovering segments.\n");
}
// Segments will be introduced.
checksubsegflag = 1;
misseglist = new arraypool(sizeof(face), 8);
bdrysteinerptlist = new arraypool(sizeof(point), 8);
if (0) { //if (b->order == 4) { // '-o4' option (for debug)
// In sequential order.
subsegs->traversalinit();
for (i = 0; i < subsegs->items; i++) {
searchseg.sh = shellfacetraverse(subsegs);
//sinfect(searchseg); // Only save it once.
subsegstack->newindex((void **) &paryseg);
*paryseg = searchseg;
}
} else {
// In random order.
subsegs->traversalinit();
for (i = 0; i < subsegs->items; i++) {
s = randomnation(i + 1);
// Move the s-th seg to the i-th.
subsegstack->newindex((void **) &paryseg);
*paryseg = * (face *) fastlookup(subsegstack, s);
// Put i-th seg to be the s-th.
searchseg.sh = shellfacetraverse(subsegs);
//sinfect(searchseg); // Only save it once.
paryseg = (face *) fastlookup(subsegstack, s);
*paryseg = searchseg;
}
}
// The init number of missing segments.
ms = subsegs->items;
nit = 0;
if (b->fliplinklevel < 0) {
autofliplinklevel = 1; // Init value.
}
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;
}
} // while (1)
if (b->verbose) {
printf(" %ld (%ld) segments are recovered (missing).\n",
subsegs->items - misseglist->objects, misseglist->objects);
}
if (misseglist->objects > 0) {
// There are missing segments. Increase the fliplevel.
nit = 0;
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();
// Recover the missing segments by doing more flips.
recoversegments(misseglist, 1, 0);
if (misseglist->objects < ms) {
// The number of missing segments is reduced.
continue;
} else {
nit++;
if (nit >= 3) {
break;
}
}
}
if (b->verbose) {
printf(" %ld (%ld) segments are recovered (missing).\n",
subsegs->items - misseglist->objects, misseglist->objects);
}
}
if (misseglist->objects > 0) {
// There are missing segments. Add Steiner points in volume.
nit = 0;
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();
// Recover the missing segments (with Steiner points).
recoversegments(misseglist, 1, 1);
if (misseglist->objects < ms) {
// The number of missing segments is reduced.
continue;
} else {
nit++;
if (nit >= 3) {
break;
}
}
}
if (b->verbose) {
printf(" Added %ld Steiner points in volume.\n", st_volref_count);
}
}
if (misseglist->objects > 0) {
// There are missing segments. Add Steiner points to split them.
long bak_inpoly_count = st_volref_count; //st_inpoly_count;
for (i = 0; i < misseglist->objects; i++) {
subsegstack->newindex((void **) &paryseg);
*paryseg = * (face *) fastlookup(misseglist, i);
}
misseglist->restart();
// Recover the missing segments (with Steiner points).
recoversegments(misseglist, 1, 2);
if (b->verbose) {
printf(" Added %ld Steiner points in segments.\n", st_segref_count);
if (st_volref_count > bak_inpoly_count) {
printf(" Added another %ld Steiner points in volume.\n",
st_volref_count - bak_inpoly_count);
}
}
assert(misseglist->objects == 0l);
}
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");
}
// Subfaces will be introduced.
checksubfaceflag = 1;
misshlist = new arraypool(sizeof(face), 8);
// Randomly order the subfaces.
subfaces->traversalinit();
for (i = 0; i < subfaces->items; i++) {
s = randomnation(i + 1);
// Move the s-th subface to the i-th.
subfacstack->newindex((void **) &parysh);
*parysh = * (face *) fastlookup(subfacstack, s);
// Put i-th subface to be the s-th.
searchsh.sh = shellfacetraverse(subfaces);
parysh = (face *) fastlookup(subfacstack, s);
*parysh = searchsh;
}
ms = subfaces->items;
nit = 0;
b->fliplinklevel = -1; // Init.
if (b->fliplinklevel < 0) {
autofliplinklevel = 1; // Init value.
}
while (1) {
recoversubfaces(misshlist, 0);
if (misshlist->objects > 0) {
if (b->fliplinklevel >= 0) {
break;
} else {
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;
}
} // while (1)
if (b->verbose) {
printf(" %ld (%ld) subfaces are recovered (missing).\n",
subfaces->items - misshlist->objects, misshlist->objects);
}
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();
recoversubfaces(NULL, 1);
if (b->verbose) {
printf(" Added %ld Steiner points in facets.\n", st_facref_count);
}
}
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();
}
if ((bdrysteinerptlist->objects > 0) && (b->nobisect_param > 0)) { // -Y1
bak_supp_count = 0;
b->fliplinklevel = 100000; // Unlimited flip levels.
do {
// Suppress boundary Steiner points.
for (i = 0; i < bdrysteinerptlist->objects; i++) {
parypt = (point *) fastlookup(bdrysteinerptlist, i);
rempt = *parypt;
suppressssteinerpoint(rempt);
bak_supp_count++;
}
bdrysteinerptlist->restart();
// There may be subfaces need to be recover.
if (subfacstack->objects > 0l) {
recoversubfaces(NULL, 1);
}
} while (bdrysteinerptlist->objects > 0);
if (b->verbose) {
printf(" Suppressed %ld Steiner points from boundary.\n",
bak_supp_count);
}
// The mesh contains inverted (or degenrrated) tets now.
checkinverttetflag = 1;
} // if
delete bdrysteinerptlist;
delete misseglist;
delete misshlist;
}
//// ////
//// ////
//// steiner_cxx //////////////////////////////////////////////////////////////
//// reconstruct_cxx //////////////////////////////////////////////////////////
//// ////
//// ////
///////////////////////////////////////////////////////////////////////////////
// //
// carveholes() Remove tetrahedra not in the mesh domain. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::carveholes()
{
arraypool *tetarray;
triface tetloop, neightet, hulltet, *parytet;
triface openface, casface;
triface *regiontets;
face checksh, casingout, casingin, *parysh;
face checkseg;
point *ppt, pa, pb, pc, *parypt;
enum locateresult loc;
REAL volume;
long delsegcount, delvertcount, delsteinercount;
int regioncount;
int attrnum, attr, maxattr;
int remflag;
int i, j;
tetrahedron ptr;
if (!b->quiet) {
printf("Removing exterior tetrahedra ...\n");
}
// Initialize the pool of exterior tets.
tetarray = new arraypool(sizeof(triface), 10);
regiontets = NULL;
maxattr = 0; // Choose a small number here.
attrnum = in->numberoftetrahedronattributes;
// Mark as infected any unprotected hull tets.
tetrahedrons->traversalinit();
tetloop.ver = 11; // The face opposite to dummypoint.
tetloop.tet = alltetrahedrontraverse();
while (tetloop.tet != (tetrahedron *) NULL) {
if ((point) tetloop.tet[7] == dummypoint) {
// Is this side protected by a subface?
tspivot(tetloop, checksh);
if (checksh.sh == NULL) {
infect(tetloop);
tetarray->newindex((void **) &parytet);
*parytet = tetloop;
}
}
tetloop.tet = alltetrahedrontraverse();
}
hullsize -= tetarray->objects;
if (in->numberofholes > 0) {
// Mark as infected any tets inside volume holes.
for (i = 0; i < 3 * in->numberofholes; i += 3) {
// Search a tet containing the i-th hole point.
neightet.tet = NULL;
randomsample(&(in->holelist[i]), &neightet);
loc = locate(&(in->holelist[i]), &neightet, 0, 1); // randflag = 1;
if (loc != OUTSIDE) {
infect(neightet);
tetarray->newindex((void **) &parytet);
*parytet = 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");
}
}
}
}
if (b->regionattrib && (in->numberofregions > 0)) { // If has -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);
loc = locate(&(in->regionlist[i]), &neightet, 0, 1); // randflag = 1;
if (loc != OUTSIDE) {
regiontets[i/5] = neightet;
if ((int) in->regionlist[i + 3] > maxattr) {
maxattr = (int) in->regionlist[i + 3];
}
} 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;
}
}
}
// Find and infect all exterior tets (in concave place and in holes).
for (i = 0; i < tetarray->objects; i++) {
parytet = (triface *) fastlookup(tetarray, i);
tetloop = *parytet;
for (tetloop.ver = 0; tetloop.ver < 4; tetloop.ver++) {
fsym(tetloop, neightet);
// Is this side protected by a subface?
tspivot(tetloop, checksh);
if (checksh.sh == NULL) {
// Not protected. Infect it if it is not a hull tet.
if ((point) neightet.tet[7] != dummypoint) {
if (!infected(neightet)) {
infect(neightet);
tetarray->newindex((void **) &parytet);
*parytet = neightet;
}
}
} else {
// Its adjacent tet is protected.
if ((point) neightet.tet[7] == dummypoint) {
// A hull tet. It is dead.
assert(!infected(neightet));
infect(neightet);
tetarray->newindex((void **) &parytet);
*parytet = neightet;
// Both sides of this subface are exterior.
stdissolve(checksh);
// Queue this subface (to be deleted later).
if (!sinfected(checksh)) {
sinfect(checksh); // Only queue it once.
subfacstack->newindex((void **) &parysh);
*parysh = checksh;
}
hullsize--;
} else {
if (!infected(neightet)) {
// The subface is still connect to a "live" tet - it survived.
// tsbond(neightet, checksh);
} else {
// Both sides of this subface are exterior.
stdissolve(checksh);
// Queue this subface (to be deleted later).
if (!sinfected(checksh)) {
sinfect(checksh); // Only queue it once.
subfacstack->newindex((void **) &parysh);
*parysh = checksh;
}
}
}
}
}
}
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;
}
}
}
// Remove all exterior tetrahedra (including infected hull tets).
for (i = 0; i < tetarray->objects; i++) {
parytet = (triface *) fastlookup(tetarray, i);
tetloop = *parytet;
for (tetloop.ver = 0; tetloop.ver < 4; tetloop.ver++) {
fsym(tetloop, neightet);
if (!infected(neightet)) {
// A "live" tet (may be a hull tet). Clear its adjacent tet.
neightet.tet[neightet.ver & 3] = NULL;
}
}
tetrahedrondealloc(parytet->tet);
} // i
tetarray->restart(); // Re-use it for new hull tets.
// Create new hull tets.
// Update point-to-tet map, segment-to-tet map, and subface-to-tet map.
tetrahedrons->traversalinit();
tetloop.tet = tetrahedrontraverse();
while (tetloop.tet != (tetrahedron *) NULL) {
for (tetloop.ver = 0; tetloop.ver < 4; tetloop.ver++) {
if (tetloop.tet[tetloop.ver] == NULL) {
tspivot(tetloop, checksh);
assert(checksh.sh != NULL); // SELF_CHECK
// Create a new hull tet.
maketetrahedron(&hulltet);
pa = org(tetloop);
pb = dest(tetloop);
pc = apex(tetloop);
setvertices(hulltet, pb, pa, pc, dummypoint);
bond(tetloop, hulltet);
// Update the subface-to-tet map.
sesymself(checksh);
tsbond(hulltet, checksh);
// Update the segment-to-tet map.
for (i = 0; i < 3; i++) {
tsspivot1(tetloop, checkseg);
if (checkseg.sh != NULL) {
tssbond1(hulltet, checkseg);
sstbond1(checkseg, hulltet);
}
enextself(tetloop);
eprevself(hulltet);
}
// Save this hull tet in list.
tetarray->newindex((void **) &parytet);
*parytet = hulltet;
}
}
// Update the point-to-tet map.
tetloop.ver = 0;
ptr = encode(tetloop);
ppt = (point *) tetloop.tet;
for (i = 4; i < 8; i++) {
setpoint2tet(ppt[i], ptr);
}
tetloop.tet = tetrahedrontraverse();
}
if (subfacstack->objects > 0) {
// Remove all subfaces which do not attach to any tetrahedron.
// Segments which are not attached to any subfaces and tets
// are deleted too.
delsegcount = 0;
for (i = 0; i < subfacstack->objects; i++) {
parysh = (face *) fastlookup(subfacstack, i);
if (i == 0) {
if (b->verbose) {
printf("Warning: Removing an open face (%d, %d, %d)\n",
pointmark(sorg(*parysh)), pointmark(sdest(*parysh)),
pointmark(sapex(*parysh)));
}
}
// Dissolve this subface from face links.
for (j = 0; j < 3; j++) {
spivot(*parysh, casingout);
sspivot(*parysh, checkseg);
if (casingout.sh != NULL) {
casingin = casingout;
while (1) {
spivot(casingin, checksh);
if (checksh.sh == parysh->sh) break;
casingin = checksh;
}
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) {
// The segment is also dead.
if (delsegcount == 0) {
if (b->verbose) {
printf("Warning: Removing a dangling segment (%d, %d)\n",
pointmark(sorg(checkseg)), pointmark(sdest(checkseg)));
}
}
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);
if (delsegcount > 0) {
printf(" Deleted %ld segments.\n", delsegcount);
}
}
subfacstack->restart();
}
// Some vertices may be not belong to any tet. Mark them.
delvertcount = unuverts;
delsteinercount = 0l;
points->traversalinit();
pa = pointtraverse();
while (pa != NULL) {
if (pointtype(pa) != UNUSEDVERTEX) {
remflag = 0;
decode(point2tet(pa), neightet);
if ((neightet.tet == NULL) || (neightet.tet[4] == NULL)) {
remflag = 1; // It's a dead tet.
} else {
// Check if this tet contains pa.
ppt = (point *) &(neightet.tet[4]);
if (!((ppt[0] == pa) || (ppt[1] == pa) ||
(ppt[2] == pa) || (ppt[3] == pa))) {
remflag = 1; // It's a wrong pointer.
}
}
if (remflag) {
// Found an exterior vertex.
if (pointmark(pa) >
(in->numberofpoints - (in->firstnumber ? 0 : 1))) {
if (pointtype(pa) == FREESEGVERTEX) {
st_segref_count--;
} else if (pointtype(pa) == FREEFACETVERTEX) {
st_facref_count--;
} else {
assert(pointtype(pa) == FREEVOLVERTEX);
st_volref_count--; //st_inpoly_count--;
}
delsteinercount++; // A Steiner point.
if (steinerleft > 0) steinerleft++;
}
setpointtype(pa, UNUSEDVERTEX);
unuverts++;
} else {
// This vertex survived.
if (b->nobisect && (b->nobisect_param > 1)) { // -Y2
// Queue it if it is a Steiner point.
if ((pointtype(pa) == FREESEGVERTEX) ||
(pointtype(pa) == FREEFACETVERTEX) ||
(pointtype(pa) == FREEVOLVERTEX)) {
subvertstack->newindex((void **) &parypt);
*parypt = pa;
}
}
}
}
pa = pointtraverse();
}
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);
}
}
}
// Update the hull size.
hullsize += tetarray->objects;
// Connect new hull tets.
for (i = 0; i < tetarray->objects; i++) {
parytet = (triface *) fastlookup(tetarray, i);
hulltet = *parytet;
for (j = 0; j < 3; j++) {
esym(hulltet, neightet);
if (neightet.tet[neightet.ver & 3] == NULL) {
tspivot(hulltet, checksh);
assert(checksh.sh != NULL);
// Get the next subface in the same face ring of checksh. It must
// exist, otherwise, checksh is either a dangling subface (which
// should be removed already), or it is not a hull face.
sfnext(checksh, casingout);
assert(casingout.sh != NULL);
// Go to the hull side.
sesymself(casingout);
stpivot(casingout, casface);
assert(ishulltet(casface));
esymself(casface);
assert(casface.tet[casface.ver & 3] == NULL);
// Bond the two hull tets together.
bond(neightet, casface);
}
enextself(hulltet);
}
}
// Set region attributes (when has -A and -AA options).
if (b->regionattrib) {
if (!b->quiet) {
printf("Spreading region attributes.\n");
}
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];
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 (tetloop.ver = 0; tetloop.ver < 4; tetloop.ver++) {
fsym(tetloop, neightet);
// Is this side protected by a subface?
tspivot(tetloop, checksh);
if (checksh.sh == NULL) {
// Not protected. It must not be a hull tet.
// assert((point) neightet.tet[7] != dummypoint);
if ((point) neightet.tet[7] == dummypoint) {
assert(0);
}
if (!infected(neightet)) {
infect(neightet);
tetarray->newindex((void **) &parytet);
*parytet = neightet;
}
} else {
// Protected. Set attribute for hull tet as well.
if ((point) neightet.tet[7] == dummypoint) {
setelemattribute(neightet.tet, attrnum, attr);
if (b->varvolume) { // If has -a option.
setvolumebound(neightet.tet, volume);
}
}
}
} // ver
} // j
regioncount++;
} // if (regiontets[i/5].tet != NULL)
} // i
}
if (b->regionattrib > 1) { // If has -AA option.
// Set attributes for all tetrahedra.
attr = maxattr + 1;
tetrahedrons->traversalinit();
tetloop.tet = tetrahedrontraverse();
while (tetloop.tet != (tetrahedron *) NULL) {
if (!infected(tetloop)) {
// An unmarked region.
tetarray->restart(); // Re-use this array.
infect(tetloop);
tetarray->newindex((void **) &parytet);
*parytet = tetloop;
// Find and mark all tets.
for (j = 0; j < tetarray->objects; j++) {
parytet = (triface *) fastlookup(tetarray, j);
tetloop = *parytet;
setelemattribute(tetloop.tet, attrnum, attr);
for (tetloop.ver = 0; tetloop.ver < 4; tetloop.ver++) {
fsym(tetloop, neightet);
// Is this side protected by a subface?
tspivot(tetloop, checksh);
if (checksh.sh == NULL) {
// Not protected. It must not be a hull tet.
assert((point) neightet.tet[7] != dummypoint);
if (!infected(neightet)) {
infect(neightet);
tetarray->newindex((void **) &parytet);
*parytet = neightet;
}
} else {
// Protected. Set attribute for hull tet as well.
if ((point) neightet.tet[7] == dummypoint) {
setelemattribute(neightet.tet, attrnum, attr);
}
}
} // loc
}
attr++; // Increase the attribute.
regioncount++;
} // if (!infected(tetloop))
tetloop.tet = tetrahedrontraverse();
}
// Until here, every tet has a region attribute.
}
// Uninfect processed tets.
tetrahedrons->traversalinit();
tetloop.tet = tetrahedrontraverse();
while (tetloop.tet != (tetrahedron *) NULL) {
uninfect(tetloop);
tetloop.tet = tetrahedrontraverse();
}
// Mesh elements contain region attributes now.
in->numberoftetrahedronattributes++;
if (b->verbose) {
assert(regioncount > 0);
if (regioncount > 1) {
printf(" Found %d subdomains.\n", regioncount);
} else {
printf(" Found 1 domain.\n");
}
}
} // if (b->regionattrib)
if (b->regionattrib && (in->numberofregions > 0)) { // If has -A option.
delete [] regiontets;
}
delete tetarray;
// The mesh is non-convex now.
nonconvex = 1;
// Push all hull tets into 'flipstack'.
tetrahedrons->traversalinit();
tetloop.ver = 11; // The face opposite to dummypoint.
tetloop.tet = alltetrahedrontraverse();
while (tetloop.tet != (tetrahedron *) NULL) {
if ((point) tetloop.tet[7] == dummypoint) {
flippush(flipstack, &tetloop);
}
tetloop.tet = alltetrahedrontraverse();
}
// Peel "slivers" off the hull.
lawsonflip3d(NULL, 4, 1, 0, 0);
if (b->verbose && (opt_sliver_peels > 0l)) {
printf(" Peeled %ld hull slivers.\n", opt_sliver_peels);
}
}
///////////////////////////////////////////////////////////////////////////////
// //
// reconstructmesh() Reconstruct a tetrahedral mesh. //
// //
///////////////////////////////////////////////////////////////////////////////
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 angtol, ang;
int eextras, marker = 0;
int bondflag;
int idx, i, j, k;
if (!b->quiet) {
printf("Reconstructing mesh ...\n");
}
// Default assume the mesh is non-convex.
nonconvex = 1;
// Create a map from indices to vertices.
makeindex2pointmap(idx2verlist);
// Allocate an array that maps each vertex to its adjacent tets.
ver2tetarray = new tetrahedron[in->numberofpoints + 1];
for (i = 0; i < in->numberofpoints; 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++]];
setpointtype(p[j], VOLVERTEX); // initial type.
}
// 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 {
assert(checktet.ver < 4); // SELF_CHECK
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.
assert(face2.tet[face2.ver & 3] == NULL);
esym(hulltet, face1);
bond(face1, face2);
}
enextself(hulltet);
}
//hullsize++;
}
// Create the point-to-tet map.
setpoint2tet((point) (tetloop.tet[4 + tetloop.ver]), tptr);
// Clean the temporary used space.
tetloop.tet[8 + tetloop.ver] = NULL;
}
tetloop.tet = tetrahedrontraverse();
}
hullsize = tetrahedrons->items - hullsize;
// Subfaces will be inserted into the mesh.
if (in->trifacelist != NULL) {
// A .face file is given. It may contain boundary faces. Insert them.
for (i = 0; i < in->numberoftrifaces; i++) {
// Is it a subface?
if (in->trifacemarkerlist != NULL) {
marker = in->trifacemarkerlist[i];
} else {
// Face markers are not available. Assume all of them are subfaces.
marker = 1;
}
if (marker > 0) {
idx = i * 3;
for (j = 0; j < 3; j++) {
p[j] = idx2verlist[in->trifacelist[idx++]];
}
// Search the subface.
bondflag = 0;
// Make sure all vertices are in the mesh. Avoid crash.
for (j = 0; j < 3; j++) {
decode(point2tet(p[j]), checktet);
if (checktet.tet == NULL) break;
}
if ((j == 3) && getedge(p[0], p[1], &checktet)) {
tetloop = checktet;
q[2] = apex(checktet);
while (1) {
if (apex(tetloop) == p[2]) {
// Found the face.
// Check if there exist a subface already?
tspivot(tetloop, neighsh);
if (neighsh.sh != NULL) {
// Found a duplicated subface.
// This happens when the mesh was generated by other mesher.
bondflag = 0;
} else {
bondflag = 1;
}
break;
}
fnextself(tetloop);
if (apex(tetloop) == q[2]) break;
}
}
if (bondflag) {
// Create a new subface.
makeshellface(subfaces, &subloop);
setshvertices(subloop, p[0], p[1], p[2]);
// Create the point-to-subface map.
sptr = sencode(subloop);
for (j = 0; j < 3; j++) {
setpointtype(p[j], FACETVERTEX); // initial type.
setpoint2sh(p[j], sptr);
}
if (in->trifacemarkerlist != NULL) {
setshellmark(subloop, in->trifacemarkerlist[i]);
}
// Insert the subface into the mesh.
tsbond(tetloop, subloop);
fsymself(tetloop);
sesymself(subloop);
tsbond(tetloop, subloop);
} else {
if (!b->quiet) {
if (neighsh.sh == NULL) {
printf("Warning: Subface #%d [%d,%d,%d] is missing.\n",
i + in->firstnumber, pointmark(p[0]), pointmark(p[1]),
pointmark(p[2]));
} else {
printf("Warning: Ignore a dunplicated subface #%d [%d,%d,%d].\n",
i + in->firstnumber, pointmark(p[0]), pointmark(p[1]),
pointmark(p[2]));
}
}
} // if (bondflag)
} // if (marker > 0)
} // i
} // if (in->trifacelist)
// Indentify subfaces from the mesh.
// Create subfaces for hull faces (if they're not subface yet) and
// interior faces which separate two different materials.
eextras = in->numberoftetrahedronattributes;
tetrahedrons->traversalinit();
tetloop.tet = tetrahedrontraverse();
while (tetloop.tet != (tetrahedron *) NULL) {
for (tetloop.ver = 0; tetloop.ver < 4; tetloop.ver++) {
tspivot(tetloop, neighsh);
if (neighsh.sh == NULL) {
bondflag = 0;
fsym(tetloop, checktet);
if (ishulltet(checktet)) {
bondflag = 1; // A hull face.
} else {
if (eextras > 0) {
if (elemattribute(tetloop.tet, eextras - 1) !=
elemattribute(checktet.tet, eextras - 1)) {
bondflag = 1; // An interior interface.
}
}
}
if (bondflag) {
// Create a new subface.
makeshellface(subfaces, &subloop);
p[0] = org(tetloop);
p[1] = dest(tetloop);
p[2] = apex(tetloop);
setshvertices(subloop, p[0], p[1], p[2]);
// Create the point-to-subface map.
sptr = sencode(subloop);
for (j = 0; j < 3; j++) {
setpointtype(p[j], FACETVERTEX); // initial type.
setpoint2sh(p[j], sptr);
}
setshellmark(subloop, 0); // Default marker.
// Insert the subface into the mesh.
tsbond(tetloop, subloop);
sesymself(subloop);
tsbond(checktet, subloop);
} // if (bondflag)
} // if (neighsh.sh == NULL)
}
tetloop.tet = tetrahedrontraverse();
}
// Connect subfaces together.
subfaces->traversalinit();
subloop.shver = 0;
subloop.sh = shellfacetraverse(subfaces);
while (subloop.sh != (shellface *) NULL) {
for (i = 0; i < 3; i++) {
spivot(subloop, neighsh);
if (neighsh.sh == NULL) {
// Form a subface ring by linking all subfaces at this edge.
// Traversing all faces of the tets at this edge.
stpivot(subloop, tetloop);
q[2] = apex(tetloop);
neighsh = subloop;
while (1) {
fnextself(tetloop);
tspivot(tetloop, nextsh);
if (nextsh.sh != NULL) {
// Link neighsh <= nextsh.
sbond1(neighsh, nextsh);
neighsh = nextsh;
}
if (apex(tetloop) == q[2]) {
assert(nextsh.sh == subloop.sh); // It's a ring.
break;
}
} // while (1)
} // if (neighsh.sh == NULL)
senextself(subloop);
}
subloop.sh = shellfacetraverse(subfaces);
}
//if (b->verbose) {
// printf(" Created %ld subfaces.\n", subfaces->items);
//}
// Segments will be introudced.
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;
}
if (marker != 0) {
// Insert a segment.
idx = i * 2;
for (j = 0; j < 2; j++) {
p[j] = idx2verlist[in->edgelist[idx++]];
}
// Make sure all vertices are in the mesh. Avoid crash.
for (j = 0; j < 2; j++) {
decode(point2tet(p[j]), checktet);
if (checktet.tet == NULL) break;
}
// Search the segment.
if ((j == 2) && getedge(p[0], p[1], &checktet)) {
// Create a new subface.
makeshellface(subsegs, &segloop);
setshvertices(segloop, p[0], p[1], NULL);
// Create the point-to-segment map.
sptr = sencode(segloop);
for (j = 0; j < 2; j++) {
setpointtype(p[j], RIDGEVERTEX); // initial type.
setpoint2sh(p[j], sptr);
}
if (in->edgemarkerlist != NULL) {
setshellmark(segloop, marker);
}
// Insert the segment into the mesh.
tetloop = checktet;
q[2] = apex(checktet);
subloop.sh = NULL;
while (1) {
tssbond1(tetloop, segloop);
tspivot(tetloop, subloop);
if (subloop.sh != NULL) {
ssbond1(subloop, segloop);
sbond1(segloop, subloop);
}
fnextself(tetloop);
if (apex(tetloop) == q[2]) break;
} // while (1)
// Remember an adjacent tet for this segment.
sstbond1(segloop, tetloop);
} else {
if (!b->quiet) {
printf("Warning: Segment #%d [%d,%d] is missing.\n",
i + in->firstnumber, pointmark(p[0]), pointmark(p[1]));
}
}
} // if (marker != 0)
} // i
} // if (in->edgelist)
// Identify segments from the mesh.
// Create segments for non-manifold edges (which are shared by more
// than two subfaces), and for non-coplanar edges, i.e., two subfaces
// form an dihedral angle > 'b->facet_ang_tol' (degree).
angtol = b->facet_ang_tol / 180.0 * PI;
subfaces->traversalinit();
subloop.shver = 0;
subloop.sh = shellfacetraverse(subfaces);
while (subloop.sh != (shellface *) NULL) {
for (i = 0; i < 3; i++) {
sspivot(subloop, segloop);
if (segloop.sh == NULL) {
// Check if this edge is a segment.
bondflag = 0;
// Counter the number of subfaces at this edge.
idx = 0;
nextsh = subloop;
while (1) {
idx++;
spivotself(nextsh);
if (nextsh.sh == subloop.sh) break;
}
if (idx != 2) {
// It's a non-manifold edge. Insert a segment.
p[0] = sorg(subloop);
p[1] = sdest(subloop);
bondflag = 1;
} else {
// Check the dihedral angle formed by the two subfaces.
spivot(subloop, neighsh);
p[0] = sorg(subloop);
p[1] = sdest(subloop);
p[2] = sapex(subloop);
p[3] = sapex(neighsh);
ang = facedihedral(p[0], p[1], p[2], p[3]);
if (ang > PI) ang = 2 * PI - ang;
if (ang < angtol) {
bondflag = 1;
}
}
if (bondflag) {
// Create a new subface.
makeshellface(subsegs, &segloop);
setshvertices(segloop, p[0], p[1], NULL);
// Create the point-to-segment map.
sptr = sencode(segloop);
for (j = 0; j < 2; j++) {
setpointtype(p[j], RIDGEVERTEX); // initial type.
setpoint2sh(p[j], sptr);
}
setshellmark(segloop, marker);
// Insert the subface into the mesh.
stpivot(subloop, tetloop);
q[2] = apex(tetloop);
while (1) {
tssbond1(tetloop, segloop);
tspivot(tetloop, neighsh);
if (neighsh.sh != NULL) {
ssbond1(neighsh, segloop);
}
fnextself(tetloop);
if (apex(tetloop) == q[2]) break;
} // while (1)
// Remember an adjacent tet for this segment.
sstbond1(segloop, tetloop);
sbond1(segloop, subloop);
} // if (bondflag)
} // if (neighsh.sh == NULL)
senextself(subloop);
}
subloop.sh = shellfacetraverse(subfaces);
}
// Remember the number of input segments.
insegments = subsegs->items;
//if (b->verbose) {
// printf(" Created %ld segments.\n", subsegs->items);
//}
// Set global flags.
checksubsegflag = 1;
checksubfaceflag = 1;
//nonconvex = 1;
delete [] idx2verlist;
delete [] ver2tetarray;
}
///////////////////////////////////////////////////////////////////////////////
// //
// 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'. //
// //
///////////////////////////////////////////////////////////////////////////////
int tetgenmesh::scoutpoint(point searchpt, triface *searchtet, int randflag)
{
point pa, pb, pc, pd;
enum locateresult loc = OUTSIDE;
REAL vol, ori1, ori2, ori3, ori4;
int iter;
if (searchtet->tet == NULL) {
*searchtet = recenttet;
}
iter = 0;
while (1) {
// Randonmly select a good starting tet.
if (randflag) {
randomsample(searchpt, searchtet);
}
loc = locate(searchpt, searchtet, 0, 1);
if (loc == OUTSIDE) {
// Not found. This happens when the mesh is not convex.
if (!randflag) break;
iter++;
if (iter > 3) {
searchtet->tet = NULL;
break;
}
} else {
// Found the point.
break;
}
} // while (1)
if (loc != OUTSIDE) {
// Round the result of location.
pa = org(*searchtet);
pb = dest(*searchtet);
pc = apex(*searchtet);
pd = oppo(*searchtet);
vol = orient3d(pa, pb, pc, pd);
ori1 = orient3d(pa, pb, pc, searchpt);
ori2 = orient3d(pb, pa, pd, searchpt);
ori3 = orient3d(pc, pb, pd, searchpt);
ori4 = orient3d(pa, pc, pd, searchpt);
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 = orient3d(pa, pb, pc, pd);
assert(vol < 0); // vol != 0
ori1 = orient3d(pa, pb, pc, searchpt);
if (fabs(ori1 / vol) < b->epsilon) ori1 = 0; // Rounding.
if (ori1 <= 0) {
ori2 = orient3d(pb, pa, pd, searchpt);
if (fabs(ori2 / vol) < b->epsilon) ori2 = 0;
if (ori2 <= 0) {
ori3 = orient3d(pc, pb, pd, searchpt);
if (fabs(ori3 / vol) < b->epsilon) ori3 = 0;
if (ori3 <= 0) {
ori4 = orient3d(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 = bgm->tetrahedrontraverse();
} // while (searchtet->tet != NULL)
}
if (searchtet->tet != NULL) {
// Return the point location.
if (ori1 == 0) { // on face [a,b,c]
if (ori2 == 0) { // on edge [a,b].
if (ori3 == 0) { // on vertex [b].
assert(ori4 != 0);
enextself(*searchtet); // [b,c,a,d]
loc = ONVERTEX;
} else {
if (ori4 == 0) { // on vertex [a]
loc = ONVERTEX; // [a,b,c,d]
} else {
loc = ONEDGE; // [a,b,c,d]
}
}
} else { // ori2 != 0
if (ori3 == 0) { // on edge [b,c]
if (ori4 == 0) { // on vertex [c]
eprevself(*searchtet); // [c,a,b,d]
loc = ONVERTEX;
} else {
enextself(*searchtet); // [b,c,a,d]
loc = ONEDGE;
}
} else { // ori3 != 0
if (ori4 == 0) { // on edge [c,a]
eprevself(*searchtet); // [c,a,b,d]
loc = ONEDGE;
} else {
loc = ONFACE;
}
}
}
} else { // ori1 != 0
if (ori2 == 0) { // on face [b,a,d]
esymself(*searchtet); // [b,a,d,c]
if (ori3 == 0) { // on edge [b,d]
eprevself(*searchtet); // [d,b,a,c]
if (ori4 == 0) { // on vertex [d]
loc = ONVERTEX;
} else {
loc = ONEDGE;
}
} else { // ori3 != 0
if (ori4 == 0) { // on edge [a,d]
enextself(*searchtet); // [a,d,b,c]
loc = ONEDGE;
} else {
loc = ONFACE;
}
}
} else { // ori2 != 0
if (ori3 == 0) { // on face [c,b,d]
enextself(*searchtet);
esymself(*searchtet);
if (ori4 == 0) { // on edge [c,d]
eprevself(*searchtet);
loc = ONEDGE;
} else {
loc = ONFACE;
}
} else {
if (ori4 == 0) { // on face [a,c,d]
eprevself(*searchtet);
esymself(*searchtet);
loc = ONFACE;
} else { // inside tet [a,b,c,d]
loc = INTETRAHEDRON;
} // ori4
} // ori3
} // ori2
} // ori1
} else {
loc = OUTSIDE;
}
return (int) loc;
}
///////////////////////////////////////////////////////////////////////////////
// //
// 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. //
// //
// If 'posflag' is set, only do interpolation when all vertices have a posi- //
// tive value. //
// //
///////////////////////////////////////////////////////////////////////////////
REAL tetgenmesh::getpointmeshsize(point searchpt, triface *searchtet, int iloc,
int posflag)
{
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]);
assert(pts[3] != dummypoint);
if (!posflag ||
((pts[0][pointmtrindex] > 0) && (pts[1][pointmtrindex] > 0) &&
(pts[2][pointmtrindex] > 0) && (pts[3][pointmtrindex] > 0))) {
// P1 interpolation.
volume = orient3d(pts[0], pts[1], pts[2], pts[3]);
vol[0] = orient3d(searchpt, pts[1], pts[2], pts[3]);
vol[1] = orient3d(pts[0], searchpt, pts[2], pts[3]);
vol[2] = orient3d(pts[0], pts[1], searchpt, pts[3]);
vol[3] = orient3d(pts[0], pts[1], pts[2], searchpt);
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 (!posflag ||
((pa[pointmtrindex] > 0) && (pb[pointmtrindex] > 0) &&
(pc[pointmtrindex] > 0))) {
volume = triarea(pa, pb, pc);
vol[0] = triarea(searchpt, pb, pc);
vol[1] = triarea(pa, searchpt, pc);
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 (!posflag || ((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 (!posflag || (pa[pointmtrindex] > 0)) {
size = pa[pointmtrindex];
}
}
return size;
}
///////////////////////////////////////////////////////////////////////////////
// //
// interpolatemeshsize() Interpolate the mesh size from a background mesh //
// (source) to the current mesh (destination). //
// //
///////////////////////////////////////////////////////////////////////////////
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");
}
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 (posflag = 0)
ploop[pointmtrindex] = bgm->getpointmeshsize(ploop, &searchtet, iloc, 0);
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);
printf(" Size rangle [%.17g, %.17g].\n", minval, maxval);
}
}
///////////////////////////////////////////////////////////////////////////////
// //
// insertconstrainedpoints() Insert a list of points into the mesh. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::insertconstrainedpoints(tetgenio *addio)
{
triface searchtet, spintet;
face checksh, *splitsh;
face checkseg, *splitseg;
point newpt;
insertvertexflags ivf;
REAL *attr, x, y, z, w;
int randflag;
int count, index;
int loc;
int i, j;
if (!b->quiet) {
printf("Inserting constrained points ...\n");
}
randflag = 1; // Randomly select start tet for point location.
count = 0;
index = 0;
for (i = 0; i < addio->numberofpoints; i++) {
makepoint(&newpt, VOLVERTEX);
x = newpt[0] = addio->pointlist[index++];
y = newpt[1] = addio->pointlist[index++];
z = newpt[2] = addio->pointlist[index++];
if (b->weighted) { // -w option
if (addio->numberofpointattributes > 0) {
// The first point attribute is weight.
w = addio->pointattributelist[addio->numberofpointattributes * i];
} else {
// No given weight available.
w = 0;
}
if (b->weighted_param == 0) {
newpt[3] = x * x + y * y + z * z - w; // Weighted DT.
} else { // -w1 option
newpt[3] = w; // Regular tetrahedralization.
}
} else {
newpt[3] = 0;
}
// Read the add point attributes if current points have attributes.
if ((addio->numberofpointattributes > 0) &&
(in->numberofpointattributes > 0)) {
attr = addio->pointattributelist + addio->numberofpointattributes * i;
for (j = 0; j < in->numberofpointattributes; j++) {
if (j < addio->numberofpointattributes) {
newpt[4 + j] = attr[j];
}
}
}
// Read the point metric tensor.
//for (j = 0; j < in->numberofpointmtrs; j++) {
// pointloop[pointmtrindex + j] = in->pointmtrlist[mtrindex++];
//}
// Find the location of the inserted point.
searchtet.tet = NULL;
ivf.iloc = scoutpoint(newpt, &searchtet, randflag);
if (ivf.iloc != (int) OUTSIDE) {
// Found the point.
// Initialize the insertion parameters.
if (b->psc) {
ivf.bowywat = 0; // Do not enlarge the initial cavity.
ivf.validflag = 0; // Do not validate the initial cavity.
} else {
ivf.bowywat = 3; // Use the "Bowyer-Watson" algorithm to form cavity.
ivf.validflag = 1; // Validate the B-W cavity.
}
ivf.lawson = 3;
ivf.rejflag = 0;
ivf.chkencflag = 0;
ivf.sloc = ivf.iloc;
ivf.sbowywat = ivf.bowywat; // Surface mesh options.
ivf.splitbdflag = 1;
ivf.respectbdflag = 1;
ivf.assignmeshsize = 1;
splitsh = NULL;
splitseg = NULL;
// Set the right point type.
if (ivf.iloc == (int) ONEDGE) {
tsspivot1(searchtet, checkseg);
if (checkseg.sh != NULL) {
setpointtype(newpt, RIDGEVERTEX);
spivot(checkseg, checksh);
splitsh = &checksh;
splitseg = &checkseg;
} else {
// Check if it is a subface edge.
spintet = searchtet;
while (1) {
tspivot(spintet, checksh);
if (checksh.sh != NULL) {
setpointtype(newpt, FACETVERTEX);
splitsh = &checksh;
break;
}
fnextself(spintet);
if (spintet.tet == searchtet.tet) break;
}
}
} else if (ivf.iloc == (int) ONFACE) {
tspivot(searchtet, checksh);
if (checksh.sh != NULL) {
setpointtype(newpt, FACETVERTEX);
splitsh = &checksh;
}
}
// Insert the vertex.
loc = insertvertex(newpt, &searchtet, splitsh, splitseg, &ivf);
if (loc == ivf.iloc) {
// The point has been inserted.
lawsonflip3d(newpt, 4, 0, ivf.chkencflag, 0);
count++;
} else {
if (!b->quiet) {
printf("Warning: Failed to insert point #%d. Ignored.\n", i);
}
pointdealloc(newpt);
}
} else {
if (!b->quiet) {
printf("Warning: Can't locate add point #%d. Ignored.\n", i);
}
pointdealloc(newpt);
}
} // i
if (b->verbose) {
printf(" Inserted %d of %d vertices.\n", count, addio->numberofpoints);
}
}
//// ////
//// ////
//// reconstruct_cxx //////////////////////////////////////////////////////////
//// refine_cxx ///////////////////////////////////////////////////////////////
//// ////
//// ////
///////////////////////////////////////////////////////////////////////////////
// //
// marksharpsegments() Mark sharp segments. //
// //
// All segments are initialized as type NSHARP. //
// //
// A segment is SHARP if there are two facets intersecting at it with an //
// internal dihedral angle (*) less than an angle \theta. //
// //
// A theoretical value of \theta is arccos(1/3) \approx 70.54 degree. It is //
// possible to relax it in practice. Here we choose \theta = 65 degree. //
// //
// The minimum dihedral angle between facets (minfacetdihed) is calulcated. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::marksharpsegments()
{
triface adjtet;
face startsh, spinsh, neighsh;
face segloop, nextseg, prevseg;
point eorg, edest;
REAL ang, smallang;
bool issharp;
int sharpcount;
// For storing extremely small dihedral angle.
face *parysh, *parysh1;
REAL exsmallang;
int exsharpcount;
int i, j, k;
if (b->verbose > 0) {
printf(" Marking sharp segments.\n");
}
minfacetdihed = PI;
smallang = 65.0 * PI / 180.0; // 65 degree.
exsmallang = 15.0 * PI / 180.0; // 15 degree.
sharpcount = exsharpcount = 0;
// A segment s may have been split into many subsegments. Operate the one
// which contains the origin of s. Then mark the rest of subsegments.
subsegs->traversalinit();
segloop.sh = shellfacetraverse(subsegs);
while (segloop.sh != (shellface *) NULL) {
segloop.shver = 0;
senext2(segloop, prevseg);
spivotself(prevseg);
if (prevseg.sh == NULL) {
// Operate on this seg s.
issharp = false;
spivot(segloop, startsh);
if (startsh.sh != NULL) {
// First check if two facets form an acute dihedral angle at s.
eorg = sorg(segloop);
edest = sdest(segloop);
spinsh = startsh;
while (1) {
if (sorg(spinsh) != eorg) sesymself(spinsh);
// Only do test when the spinsh is faceing inward.
stpivot(spinsh, adjtet);
if (adjtet.tet != NULL) {
if (!ishulltet(adjtet)) {
// Get the subface on the adjacent facet.
spivot(spinsh, neighsh);
// Do not calculate if it is self-bonded.
if ((neighsh.sh != NULL) && (neighsh.sh != spinsh.sh)) {
// Calculate the dihedral angle between the two subfaces.
ang = facedihedral(eorg, edest, sapex(spinsh), sapex(neighsh));
// Only do check if a sharp angle has not been found.
if (!issharp) issharp = (ang < smallang);
// Remember the smallest facet dihedral angle.
minfacetdihed = minfacetdihed < ang ? minfacetdihed : ang;
if (ang < exsmallang) {
// It's an extremely small dihedral angle.
// Mark the two facets.
// To avoid too many Steiner points, do not refine them.
if (shelltype(spinsh) != SHARP) {
setshelltype(spinsh, SHARP);
cavesegshlist->newindex((void **) &parysh);
*parysh = spinsh;
}
if (shelltype(neighsh) != SHARP) {
setshelltype(neighsh, SHARP);
cavesegshlist->newindex((void **) &parysh);
*parysh = neighsh;
}
exsharpcount++;
}
}
}
}
// Go to the next facet.
spivotself(spinsh);
if (spinsh.sh == NULL) break; // A single subface case.
if (spinsh.sh == startsh.sh) break;
}
} // if (startsh.sh != NULL)
if (issharp) {
if (b->verbose > 2) {
printf(" Mark a sharp segment (%d, %d).\n",
pointmark(eorg), pointmark(edest));
}
setshelltype(segloop, SHARP);
// The endpoint of this segment is acute.
if (pointtype(eorg) == RIDGEVERTEX) {
setpointtype(eorg, ACUTEVERTEX);
} else {
assert(pointtype(eorg) == ACUTEVERTEX); // SELF_CHECK
}
// Set the type for all subsegments at forwards.
edest = sdest(segloop);
senext(segloop, nextseg);
spivotself(nextseg);
while (nextseg.sh != NULL) {
setshelltype(nextseg, SHARP);
// Adjust the direction of nextseg.
nextseg.shver = 0;
if (sorg(nextseg) != edest) {
sesymself(nextseg);
}
assert(sorg(nextseg) == edest);
edest = sdest(nextseg);
// Go the next connected subsegment at edest.
senextself(nextseg);
spivotself(nextseg);
}
// The endpoint of this segment is acute.
if (pointtype(edest) == RIDGEVERTEX) {
setpointtype(edest, ACUTEVERTEX);
} else {
assert(pointtype(edest) == ACUTEVERTEX); // SELF_CHECK
}
sharpcount++;
} // if (issharp)
} // if (prevseg.sh == NULL)
segloop.sh = shellfacetraverse(subsegs);
}
// Mark all facets at extremely small dihedral angles.
if (cavesegshlist->objects > 0) {
for (i = 0; i < cavesegshlist->objects; i++) {
parysh = (face *) fastlookup(cavesegshlist, i);
caveshlist->newindex((void **) &parysh1);
*parysh1 = *parysh;
for (j = 0; j < caveshlist->objects; j++) {
parysh1 = (face *) fastlookup(caveshlist, j);
spinsh = *parysh1;
for (k = 0; k < 3; k++) {
sspivot(spinsh, nextseg);
if (nextseg.sh == NULL) {
spivot(spinsh, neighsh);
if (shelltype(neighsh) != SHARP) {
setshelltype(neighsh, SHARP);
caveshlist->newindex((void **) &parysh1);
*parysh1 = neighsh;
}
}
senextself(spinsh);
} // k
} // j
caveshlist->restart();
} // i
cavesegshlist->restart();
} // if (cavesegshlist->objects > 0)
if (b->verbose) {
if (sharpcount > 0) {
printf(" Found %d (%d) sharp segments.\n", sharpcount, exsharpcount);
}
printf(" Minimum fac-fac angle = %g.\n", minfacetdihed / PI * 180.0);
}
}
///////////////////////////////////////////////////////////////////////////////
// //
// decidefeaturepointsizes() Calculate sizes for all feature points. //
// //
// A feature point is either an acute vertex or a Steiner point on a sharp //
// segment. Each feature point p will be protected by a ball whose radius //
// is called its "feature size". //
// //
// NOTE: we should have already marked all features points in the two func- //
// tions: markacutevertices() and marksharpsegments(). Each feature point //
// has the type ACUTEVERTEX or FREESEGVERTEX. //
// //
// The feature size of a vertex is the minimum of the following sizes: //
// (0) the (approximated) local feature size (the distance to the second //
// nearest boundary) of the vertex;
// (1) the value specified in .mtr file (-m option); //
// (2) the cubic root of a fixed maximal volume constraint ('-a__'); //
// (3) the cubic root of a maximal volume constraint in a region ('-a'); //
// (4) the square root of a maximal area constraint in a .var file; //
// (5) a maximal length constraint in a .var file; //
// //
// If 'b->nobisect' ('-Y' option) is set, every input vertex has a feature //
// size. //
// //
// The feature size of a Steiner point is linearly interpolated from its adj-//
// acent vertices which belong to the "carrier" (the boundary of the lowrest //
// dimension) of this Steiner point. For example, a Steiner point on a seg- //
// ment gets its size from the two endpoints of the segment. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::decidefeaturepointsizes()
{
arraypool *tetlist, *verlist;
triface starttet, *parytet;
face checksh, parentsh, shloop;
face checkseg, prevseg, nextseg, testseg;
point ploop, adjpt, e1, e2, *parypt;
REAL lfs_0, lfs_1, lfs_2;
REAL len, vol, maxlen = 0.0, varlen;
REAL ang, a, a1, a2, a3, prjpt[3], n[3];
int featureflag, featurecount;
int i, j;
if (b->verbose > 0) {
printf(" Deciding feature-point sizes.\n");
}
// Initialize working lists.
tetlist = cavetetlist;
verlist = cavetetvertlist;
if (b->fixedvolume) {
// A fixed volume constraint is imposed. This gives an upper bound of
// the maximal radius of the protect ball of a vertex.
maxlen = pow(6.0 * b->maxvolume, 1.0 / 3.0);
}
// First only assign a size of p if p is not a Steiner point. The size of
// a Steiner point will be interpolated later from the endpoints of the
// segment on which it lies.
featurecount = 0;
points->traversalinit();
ploop = pointtraverse();
while (ploop != (point) NULL) {
// Check if it is a feature point.
featureflag = 0;
// Only calculate the size if it has a size zero.
// The point may already has a positive size (-m option).
if (ploop[pointmtrindex] == 0) {
if (pointtype(ploop) == ACUTEVERTEX) {
featureflag = 1;
} else {
if (b->nobisect) { // '-Y' option
if ((pointtype(ploop) == RIDGEVERTEX) ||
(pointtype(ploop) == FACETVERTEX) ||
(pointtype(ploop) == VOLVERTEX)) {
featureflag = 1; // It is an input vertex.
}
}
}
}
if (featureflag) {
// Form star(p).
getvertexstar(1, ploop, tetlist, verlist, NULL);
// Calculate lfs_0(p), i.e., the smallest distance from p to a vertex.
// We approximate it by taking the distance of p to its nearest
// vertex in Link(p).
lfs_0 = longest;
for (i = 0; i < verlist->objects; i++) {
parypt = (point *) fastlookup(verlist, i);
adjpt = * parypt;
if (adjpt == dummypoint) {
continue; // Skip a dummypoint.
}
if (pointtype(adjpt) == FREESEGVERTEX) {
// A Steiner point. Get the subsegment.
sdecode(point2sh(adjpt), checkseg);
assert(checkseg.sh != NULL);
checkseg.shver = 0;
if (sdest(checkseg) != adjpt) {
sesymself(checkseg);
}
assert(sdest(checkseg) == adjpt);
// It is possible that the original segment of 'adjpt' does not
// have 'ploop' as an endpoint.
if (sorg(checkseg) == ploop) {
// Find the other end point of the original segment.
nextseg = checkseg;
while (1) {
senext(nextseg, testseg);
spivotself(testseg);
if (testseg.sh == NULL) break;
// Go to the next subseg.
nextseg = testseg;
// Adjust the direction of the nextseg.
nextseg.shver = 0;
if (sorg(nextseg) != adjpt) {
sesymself(nextseg);
}
assert(sorg(nextseg) == adjpt);
adjpt = sdest(nextseg);
}
}
} else if (pointtype(adjpt) == FREEFACETVERTEX) {
// Ignore a Steiner point on facet.
continue;
} else if (pointtype(adjpt) == FREEVOLVERTEX) {
// Ignore a Steiner point in volume.
continue;
}
len = distance(ploop, adjpt);
if (lfs_0 > len) lfs_0 = len;
} // i
assert(lfs_0 < longest); // SELF_CHECK
ploop[pointmtrindex] = lfs_0;
// Calculate lfs_1(p), i.e., the smallest distance from p to a segment.
// We approximate it by restricting the segments in Link(p).
lfs_1 = lfs_0;
for (i = 0; i < tetlist->objects; i++) {
parytet = (triface *) fastlookup(tetlist, i);
for (j = 0; j < 3; j++) {
tsspivot1(*parytet, checkseg);
if (checkseg.sh != NULL) {
e1 = sorg(checkseg);
e2 = sdest(checkseg);
// Only do calculation if the projeciton of 'p' lies inside the
// segment [e1, e2].
ang = interiorangle(ploop, e1, e2, NULL);
ang *= 2.0;
if (ang > PI) {
len = shortdistance(ploop, e1, e2);
if (lfs_1 > len) {
lfs_1 = len;
}
}
}
enextself(*parytet);
} // j
} // i
if (ploop[pointmtrindex] > lfs_1) {
ploop[pointmtrindex] = lfs_1;
}
// Calculate lfs_2(p), i.e., the smallest distance from p to a facet.
// We approximate it by restricting the facets in Link(p).
lfs_2 = lfs_0;
for (i = 0; i < tetlist->objects; i++) {
parytet = (triface *) fastlookup(tetlist, i);
tspivot(*parytet, checksh);
if (checksh.sh != NULL) {
adjpt = sorg(checksh);
e1 = sdest(checksh);
e2 = sapex(checksh);
// Only do calculation if the projeciton of 'p' lies inside the
// subface [adjpt, e1, e2].
projpt2face(ploop, adjpt, e1, e2, prjpt);
facenormal(adjpt, e1, e2, n, 1, NULL);
a = sqrt(dot(n, n)); // area of [adjpt, e1, e2].
if (a > 0) {
facenormal(adjpt, e1, prjpt, n, 1, NULL);
a1 = sqrt(dot(n, n));
facenormal(e1, e2, prjpt, n, 1, NULL);
a2 = sqrt(dot(n, n));
facenormal(e2, adjpt, prjpt, n, 1, NULL);
a3 = sqrt(dot(n, n));
if ((fabs(a1 + a2 + a3 - a) / a) < b->epsilon) {
len = distance(ploop, prjpt);
if (lfs_2 > len) {
lfs_2 = len;
}
}
} else {
assert(0); // a degenerate triangle.
} // if (a > 0)
}
}
if (ploop[pointmtrindex] > lfs_2) {
ploop[pointmtrindex] = lfs_2;
}
if (b->fixedvolume) {
// A fixed volume constraint is imposed. Adjust H(p) <= maxlen.
if (ploop[pointmtrindex] > maxlen) {
ploop[pointmtrindex] = maxlen;
}
}
if (b->varvolume) {
// Variant volume constraints are imposed. Adjust H(p) <= varlen.
for (i = 0; i < tetlist->objects; i++) {
parytet = (triface *) fastlookup(tetlist, i);
starttet = *parytet;
vol = volumebound(starttet.tet);
if (vol > 0.0) {
varlen = pow(6 * vol, 1.0 / 3.0);
if (ploop[pointmtrindex] > varlen) {
ploop[pointmtrindex] = varlen;
}
}
}
}
// The size is calculated.
assert(ploop[pointmtrindex] > 0); // SELF_CHECK
// Clear working lists.
tetlist->restart();
verlist->restart();
featurecount++;
} // if (featureflag)
ploop = pointtraverse();
}
if (b->verbose) {
printf(" %d feature points.\n", featurecount);
}
// Second only assign sizes for all Steiner points. A Steiner point p
// inserted on a sharp segment s is assigned a size by interpolating
// the sizes of the original endpoints of s.
featurecount = 0;
points->traversalinit();
ploop = pointtraverse();
while (ploop != (point) NULL) {
if (ploop[pointmtrindex] == 0.0) {
if (pointtype(ploop) == FREESEGVERTEX) {
// A Steiner point on segment.
featureflag = 0;
sdecode(point2sh(ploop), checkseg);
assert(checkseg.sh != NULL);
checkseg.shver = 0;
e1 = farsorg(checkseg); // The origin of this seg.
e2 = farsdest(checkseg); // The dest of this seg.
if (b->nobisect) { // '-Y' option.
assert(e1[pointmtrindex] > 0); // SELF_CHECK
assert(e2[pointmtrindex] > 0); // SELF_CHECK
featureflag = 1;
} else {
if ((e1[pointmtrindex] > 0) && (e2[pointmtrindex] > 0)) {
featureflag = 1;
}
}
if (featureflag) {
len = distance(e1, e2);
lfs_0 = distance(e1, ploop); // Re-use lfs_0.
ploop[pointmtrindex] = e1[pointmtrindex]
+ (lfs_0 / len) * (e2[pointmtrindex] - e1[pointmtrindex]);
featurecount++;
} // if (featureflag)
} else if (pointtype(ploop) == FREEFACETVERTEX) {
if (b->nobisect) { // -Y option.
// Collect vertices in the Star(p) which are also in the facet
// containing p.
point2shorg(ploop, parentsh);
checksh = parentsh;
while (1) {
assert(sorg(checksh) == ploop);
adjpt = sdest(checksh);
// Collect this vertex.
verlist->newindex((void **) &parypt);
*parypt = adjpt;
// Go to the next subface at p. (counterclockwise)
senext2self(checksh);
spivotself(checksh);
assert(checksh.sh != NULL);
if (checksh.sh == parentsh.sh) break;
if (sorg(checksh) != ploop) sesymself(checksh);
}
assert(verlist->objects > 0);
// Using Shepard interpolation (p=1) to interpolate the size for 'p'.
// Re-use len, lfs_0, lfs_1, lfs_2;
lfs_1 = lfs_2 = 0;
for (i = 0; i < verlist->objects; i++) {
parypt = (point *) fastlookup(verlist, i);
adjpt = *parypt;
if (adjpt[pointmtrindex] > 0) {
len = distance(adjpt, ploop);
lfs_0 = 1.0 / len;
lfs_1 += lfs_0 * adjpt[pointmtrindex];
lfs_2 += lfs_0;
}
}
assert(lfs_2 > 0);
ploop[pointmtrindex] = lfs_1 / lfs_2;
verlist->restart();
featurecount++;
} // if (b->nobisect)
} else if (pointtype(ploop) == FREEVOLVERTEX) {
if (b->nobisect) { // -Y option.
getvertexstar(1, ploop, tetlist, verlist, NULL);
// Using Shepard interpolation to interpolate the size for 'p'.
// Re-use len, lfs_0, lfs_1, lfs_2;
lfs_1 = lfs_2 = 0;
for (i = 0; i < verlist->objects; i++) {
parypt = (point *) fastlookup(verlist, i);
adjpt = *parypt;
if (adjpt[pointmtrindex] > 0) {
len = distance(adjpt, ploop);
lfs_0 = 1.0 / len;
lfs_1 += lfs_0 * adjpt[pointmtrindex];
lfs_2 += lfs_0;
}
}
assert(lfs_2 > 0);
ploop[pointmtrindex] = lfs_1 / lfs_2;
tetlist->restart();
verlist->restart();
featurecount++;
} // if (b->nobisect)
}
} // if (ploop[pointmtrindex] == 0.0)
ploop = pointtraverse();
}
if (b->verbose && (featurecount > 0)) {
printf(" %d Steiner feature points.\n", featurecount);
}
if (checkconstraints) {
// A .var file exists. Adjust feature sizes.
if (in->facetconstraintlist) {
// Have facet area constrains.
subfaces->traversalinit();
shloop.sh = shellfacetraverse(subfaces);
while (shloop.sh != (shellface *) NULL) {
varlen = areabound(shloop);
if (varlen > 0.0) {
// Check if the three corners are feature points.
varlen = sqrt(varlen);
for (j = 0; j < 3; j++) {
ploop = (point) shloop.sh[3 + j];
if (ploop[pointmtrindex] > 0) {
if (ploop[pointmtrindex] > varlen) {
ploop[pointmtrindex] = varlen;
}
}
} // j
}
shloop.sh = shellfacetraverse(subfaces);
}
}
if (in->segmentconstraintlist) {
// Have facet area constrains.
subsegs->traversalinit();
shloop.sh = shellfacetraverse(subsegs);
while (shloop.sh != (shellface *) NULL) {
varlen = areabound(shloop);
if (varlen > 0.0) {
// Check if the two endpoints are feature points.
for (j = 0; j < 2; j++) {
ploop = (point) shloop.sh[3 + j];
if (ploop[pointmtrindex] > 0.0) {
if (ploop[pointmtrindex] > varlen) {
ploop[pointmtrindex] = varlen;
}
}
} // j
}
shloop.sh = shellfacetraverse(subsegs);
}
}
} // if (checkconstraints)
}
///////////////////////////////////////////////////////////////////////////////
// //
// checkseg4encroach() Check if an edge is encroached upon by a point. //
// //
///////////////////////////////////////////////////////////////////////////////
int tetgenmesh::checkseg4encroach(point pa, point pb, point checkpt)
{
REAL ang;
REAL prjpt[3], u, v, t;
// Check if the point lies inside the diametrical sphere of this seg.
ang = interiorangle(checkpt, pa, pb, NULL);
ang *= 2.0; // Compare it to PI/2 (90 degree).
if (ang > PI) {
// Inside.
if (b->metric || b->nobisect) { // -m or -Y option.
if ((pa[pointmtrindex] > 0) && (pb[pointmtrindex] > 0)) {
// In this case, we're sure that the projection of 'checkpt' lies
// inside the segment [a,b]. Check if 'checkpt' lies inside the
// protecting region of this seg.
projpt2edge(checkpt, pa, pb, prjpt);
// Get 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.
}
}
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). //
// //
// 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. //
// //
///////////////////////////////////////////////////////////////////////////////
int tetgenmesh::checkseg4split(face *chkseg, point& encpt, int& qflag)
{
triface searchtet, spintet;
point forg, fdest, eapex;
REAL ccent[3], len, r, d, diff;
int i;
REAL ti, tj, t, midpt[3];
REAL ang;
int eid;
forg = sorg(*chkseg);
fdest = sdest(*chkseg);
if (b->verbose > 2) {
printf(" Check segment (%d, %d)\n", pointmark(forg), pointmark(fdest));
}
// 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)) {
if (b->verbose > 2) {
printf(" has too large size, len = %g (> %g)\n", len,
areabound(*chkseg));
}
qflag = 1;
return 1;
}
}
if (b->fixedvolume) { // if (b->varvolume || b->fixedvolume) {
if ((len * len * len) > b->maxvolume) {
if (b->verbose > 2) {
printf(" has too large size, len^3 = %g (> %g)\n", len*len*len,
b->maxvolume);
}
qflag = 1;
return 1;
}
}
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;
}
}
if (b->psc) {
// Check if it satisfies the approximation requirement.
eid = shellmark(*chkseg);
if ((pointtype(forg) == ACUTEVERTEX)||(pointtype(forg) == RIDGEVERTEX)) {
ti = in->getvertexparamonedge(in->geomhandle, pointmark(forg), eid);
} else {
ti = pointgeomuv(forg, 0);
}
if ((pointtype(fdest) == ACUTEVERTEX)||(pointtype(fdest) == RIDGEVERTEX)) {
tj = in->getvertexparamonedge(in->geomhandle, pointmark(fdest), eid);
} else {
tj = pointgeomuv(fdest, 0);
}
t = 0.5 * (ti + tj);
in->getsteineronedge(in->geomhandle, eid, t, midpt);
ang = interiorangle(midpt, forg, fdest, NULL) / PI * 180.0;
if (ang < b->facet_ang_tol) {
// Refine this segment.
if (b->verbose > 2) {
printf(" has bad approx, ang = %g\n", ang);
}
qflag = 1;
return 1;
}
} // if (b->psc)
// Second check if it is encroached.
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.
encpt = eapex;
break;
}
}
fnextself(spintet);
if (spintet.tet == searchtet.tet) break;
} // while (1)
if (encpt != NULL) {
if (b->verbose > 2) {
printf(" is encroached by %d\n", pointmark(encpt));
}
return 1;
}
return 0; // No need to split it.
}
///////////////////////////////////////////////////////////////////////////////
// //
// 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. //
// //
///////////////////////////////////////////////////////////////////////////////
int tetgenmesh::splitsegment(face *splitseg, point encpt, int qflag,
int chkencflag)
{
triface searchtet;
face searchsh;
point newpt, pa, pb;
insertvertexflags ivf;
REAL len; //, len1;
int loc;
//int i;
pa = sorg(*splitseg);
pb = sdest(*splitseg);
len = distance(pa, pb);
if (b->verbose > 2) {
printf(" Split segment (%d, %d).\n", pointmark(pa), pointmark(pb));
}
if (qflag == 0) {
if (shelltype(*splitseg) == SHARP) {
// Do not split it (due to a very small angle) even it is encroached.
// Avoid creating too many Steiner points.
return 0;
}
}
// Quickly check if we CAN split this segment.
if ((encpt == NULL) && (qflag == 0)) {
// Do not split this segment if the length is smaller than the mesh
// size at one of its endpoints.
if ((len < pa[pointmtrindex]) || (len < pb[pointmtrindex])) {
return 0;
}
}
makepoint(&newpt, FREESEGVERTEX);
getsteinerptonsegment(splitseg, encpt, newpt);
// Split the segment by the "Bowyer-Watson" algorithm.
// Parameters are chosen as follows:
// - bowywat = 3, preserve subsegments and subfaces;
// - flipflag = 3, check star & link facets for flipping;
// - rejflag = 0, do insertion even if it encoraches upon
// other subsegments or subfaces.
sstpivot1(*splitseg, searchtet);
ivf.iloc = (int) ONEDGE;
if (b->psc) {
ivf.bowywat = 0; // Do not enlarge the initial cavity.
ivf.validflag = 0; // Do not validate the initial cavity.
} else {
ivf.bowywat = 3; // Use the "Bowyer-Watson" algorithm to form cavity.
ivf.validflag = 1; // Validate the B-W cavity.
}
ivf.lawson = 3;
ivf.rejflag = 0;
if ((encpt == NULL) && (qflag == 0)) {
// Do not insert the point if it lies inside some protecting balls.
ivf.rejflag |= 4;
}
ivf.chkencflag = chkencflag;
ivf.sloc = ivf.iloc;
ivf.sbowywat = ivf.bowywat; // Surface mesh options.
ivf.splitbdflag = 1;
ivf.respectbdflag = 1;
ivf.assignmeshsize = 1;
loc = insertvertex(newpt, &searchtet, &searchsh, splitseg, &ivf);
// The new vertex should not too close to an existing point.
if (loc == (int) NEARVERTEX) {
outnodes(0);
outsubfaces(0);
outsubsegments(0);
assert(0);
} else if (loc == ENCVERTEX) {
// The point lies in some protecting balls. Rejected.
pointdealloc(newpt);
} else if (loc == (int) BADELEMENT) {
// Failed to create a valid sub-cavity in surface mesh.
pointdealloc(newpt);
//prob_subseg_count++;
} else if (loc == (int) ONEDGE) {
// Flip not locally Delaunay link facets by the 'Lawson's algo'.
lawsonflip3d(newpt, 4, 0, chkencflag, 0);
st_segref_count++;
if (steinerleft > 0) steinerleft--;
return 1;
} else {
// The vertex was not inserted. For unknown reasons.
//pointdealloc(newpt);
assert(0);
}
// Should not be here.
return 0;
}
///////////////////////////////////////////////////////////////////////////////
// //
// repairencsegs() Repair encroached (sub) segments. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::repairencsegs(int chkencflag)
{
badface *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 = badfacetraverse(badsubsegs);
while ((bface != NULL) && (steinerleft != 0)) {
// A queued segment may have been deleted (split).
if (bface->ss.sh[3] != NULL) {
// A queued segment may have been processed.
if (smarktest2ed(bface->ss)) {
sunmarktest2(bface->ss);
if (checkseg4split(&(bface->ss), encpt, qflag)) {
splitsegment(&(bface->ss), encpt, qflag, chkencflag);
}
}
}
badfacedealloc(badsubsegs, bface); // Remove this entry from list.
bface = badfacetraverse(badsubsegs);
}
}
if (badsubsegs->items > 0) {
if (steinerleft == 0) {
if (b->verbose) {
printf("The desired number of Steiner points is reached.\n");
}
} else {
assert(0); // Unknown case.
}
badsubsegs->traversalinit();
bface = badfacetraverse(badsubsegs);
while (bface != NULL) {
if (bface->ss.sh[3] != NULL) {
if (smarktest2ed(bface->ss)) {
sunmarktest2(bface->ss);
}
}
bface = badfacetraverse(badsubsegs);
}
badsubsegs->restart();
}
}
///////////////////////////////////////////////////////////////////////////////
// //
// checkfac4encroach() Check if a subface is encroached by a point. //
// //
///////////////////////////////////////////////////////////////////////////////
int tetgenmesh::checkfac4encroach(point pa, point pb, point pc, point checkpt,
REAL* cent, REAL* r)
{
REAL rd, len;
REAL prjpt[3], n[3];
REAL a, a1, a2, a3;
circumsphere(pa, pb, pc, NULL, cent, &rd);
assert(rd != 0);
len = distance(cent, checkpt);
if ((fabs(len - rd) / rd) < b->epsilon) len = rd; // Rounding.
if (len < rd) {
// The point lies inside the circumsphere of this face.
if (b->metric || b->nobisect) { // -m or -Y option.
if ((pa[pointmtrindex] > 0) && (pb[pointmtrindex] > 0) &&
(pc[pointmtrindex] > 0)) {
// Get the projection of 'checkpt' in the plane of pa, pb, and pc.
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 {
// The projection lies outside the face.
// In this case, 'p' must close to another face or a segment than
// to this one. We ignore this boundary face.
}
} else {
return 1; // No protecting ball. Encroached.
}
} else {
*r = rd;
return 1; // Encroached.
}
}
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)
{
triface searchtet;
face checksh; // *parysh;
face checkseg;
point pa, pb, pc;
REAL area, rd, len, sintheta;
REAL A[4][4], rhs[4], D;
int indx[4];
REAL elen[3];
int i;
encpt = NULL;
qflag = 0;
pa = sorg(*chkfac);
pb = sdest(*chkfac);
pc = sapex(*chkfac);
if (b->verbose > 2) {
printf(" Check subface (%d, %d, %d)\n", pointmark(pa),
pointmark(pb), pointmark(pc));
}
// Compute the coefficient matrix A (3x3).
A[0][0] = pb[0] - pa[0];
A[0][1] = pb[1] - pa[1];
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).
elen[0] = dot(A[0], A[0]); // edge [a,b]
elen[1] = dot(A[1], A[1]); // edge [a,c]
rhs[0] = 0.5 * elen[0];
rhs[1] = 0.5 * elen[1];
rhs[2] = 0.0;
// Solve the 3 by 3 equations use LU decomposition with partial
// pivoting and backward and forward substitute..
if (lu_decmp(A, 3, indx, &D, 0)) {
lu_solve(A, 3, indx, rhs, 0);
cent[0] = pa[0] + rhs[0];
cent[1] = pa[1] + rhs[1];
cent[2] = pa[2] + rhs[2];
rd = sqrt(rhs[0] * rhs[0] + rhs[1] * rhs[1] + rhs[2] * rhs[2]);
if (b->verbose > 2) {
printf(" circent: (%g, %g, %g)\n", cent[0], cent[1], cent[2]);
printf(" cirradi: %g\n", rd);
}
// Check the quality (radius-edge ratio) of this subface.
// Re-use variables 'A', 'rhs', and 'D'.
A[2][0] = pb[0] - pc[0];
A[2][1] = pb[1] - pc[1];
A[2][2] = pb[2] - pc[2];
elen[2] = dot(A[2], A[2]); // edge [b,c]
// Get the shortest edge length in 'D'.
D = elen[0]; // edge [a,b]
for (i = 1; i < 3; i++) {
if (D > elen[i]) D = elen[i];
}
D = sqrt(D);
if (b->verbose > 2) {
printf(" shortest edge length = %g\n", D);
}
rhs[3] = rd / D; // The radius-edge ratio.
// Check if this subface is nearly degenerate.
sintheta = 1.0 / (2.0 * rhs[3]);
if (sintheta < sintheta_tol) {
// Do not split this subface. Save it in list.
if (b->verbose > 1) {
printf(" !! A degenerated subface, theta = %g (deg)\n",
asin(sintheta) / PI * 180.0);
}
return 0; // Do not split a degenerated subface.
}
if (checkconstraints && (areabound(*chkfac) > 0.0)) {
// Check if the subface has too big area.
if (area > areabound(*chkfac)) {
if (b->verbose > 2) {
printf(" has too big area: %g (> %g)\n", area,
areabound(*chkfac));
}
qflag = 1;
return 1;
}
}
if (b->metric) { // -m option. Check mesh size.
// Check if the ccent lies outside one of the prot.balls at vertices.
if (((pa[pointmtrindex] > 0) && (rd > pa[pointmtrindex])) ||
((pb[pointmtrindex] > 0) && (rd > pb[pointmtrindex])) ||
((pc[pointmtrindex] > 0) && (rd > pc[pointmtrindex]))) {
qflag = 1; // Enforce mesh size.
return 1;
}
}
// Check if this subface is locally encroached.
for (i = 0; i < 2; i++) {
stpivot(*chkfac, searchtet);
if (!ishulltet(searchtet)) {
len = distance(oppo(searchtet), cent);
if ((fabs(len - rd) / rd) < b->epsilon) len = rd;// Rounding.
if (len < rd) {
if (b->verbose > 2) {
printf(" is encroached by point %d\n",
pointmark(oppo(searchtet)));
}
encpt = oppo(searchtet);
return 1;
}
}
sesymself(*chkfac);
}
} else {
assert(0);
} // if (!lu_decomp)
return 0;
}
///////////////////////////////////////////////////////////////////////////////
// //
// 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- //
// ments. Instead, one of the encroached segments is split. It is possible //
// that none of the encorached segments can be split. //
// //
// The return value indicates whether a new point is inserted (> 0) or not //
// (= 0). Furthermore, it is inserted on an encorached segment (= 1) or in- //
// side the facet (= 2). //
// //
///////////////////////////////////////////////////////////////////////////////
int tetgenmesh::splitsubface(face *splitfac, point encpt, int qflag,
REAL *ccent, int chkencflag)
{
badface *bface;
triface searchtet;
face searchsh;
face checkseg, *paryseg;
point newpt, pa, pb, pc;
insertvertexflags ivf;
REAL rd;
int splitflag;
int loc;
int i;
pa = sorg(*splitfac);
pb = sdest(*splitfac);
pc = sapex(*splitfac);
if (b->verbose > 2) {
printf(" Split subface (%d, %d, %d).\n", pointmark(pa), pointmark(pb),
pointmark(pc));
}
// Quickly check if we CAN split this subface.
if (qflag == 0) {
// Do not split this subface if it forms a very small dihedral with
// another facet. Avoid creating too many Steiner points.
if (shelltype(*splitfac) == SHARP) {
return 0;
}
// Do not split this subface if the 'ccent' lies inside the protect balls
// of one of its vertices.
rd = distance(ccent, pa);
if ((rd <= pa[pointmtrindex]) || (rd <= pb[pointmtrindex]) ||
(rd <= pc[pointmtrindex])) {
if (b->verbose > 2) {
printf(" Encroaching a protecting ball. Rejected.\n");
}
return 0;
}
}
// Initialize the inserting point.
makepoint(&newpt, FREEFACETVERTEX);
if (0) {
} else {
// Split the subface at its circumcenter.
for (i = 0; i < 3; i++) newpt[i] = ccent[i];
// Search a subface which contains 'newpt'.
searchsh = *splitfac;
// Calculate an above point. It lies above the plane containing
// the subface [a,b,c], and save it in dummypoint. Moreover,
// the vector cent->dummypoint is the normal of the plane.
calculateabovepoint4(newpt, pa, pb, pc);
// Parameters: 'aflag' = 1, - above point exists.
// 'cflag' = 0, - non-convex, check co-planarity of the result.
// 'rflag' = 0, - no need to round the locating result.
ivf.iloc = (int) slocate(newpt, &searchsh, 1, 0, 0);
if ((ivf.iloc == (int) ONFACE) || (ivf.iloc == (int) ONEDGE)) {
// Insert this point.
} else {
pointdealloc(newpt);
return 0;
}
}
// Insert the point.
stpivot(searchsh, searchtet);
//assert((ivf.iloc == (int) ONFACE) || (ivf.iloc == (int) ONEDGE));
// Split the subface by the "Bowyer-Watson" algorithm.
ivf.bowywat = 3; // Form B-W cavity.
ivf.lawson = 3; // Queue faces of the cavity for flipping.
ivf.rejflag = 1; // Reject it if it encroached upon any segment.
if (qflag == 0) {
ivf.rejflag |= 4; // Reject it if it encroached upon any vertex.
}
ivf.chkencflag = chkencflag;
ivf.sloc = ivf.iloc;
ivf.sbowywat = ivf.bowywat;
ivf.splitbdflag = 1;
ivf.validflag = 1;
ivf.respectbdflag = 1;
ivf.assignmeshsize = 1;
ivf.refineflag = 2;
ivf.refinesh = searchsh;
loc = insertvertex(newpt, &searchtet, &searchsh, NULL, &ivf);
if (loc == (int) ENCSEGMENT) {
// The new point encroaches upon some segments.
pointdealloc(newpt);
assert(encseglist->objects > 0);
// Select an encroached segment and split it.
splitflag = 0;
for (i = 0; i < encseglist->objects; i++) {
paryseg = (face *) fastlookup(encseglist, i);
if (splitsegment(paryseg, NULL, qflag, chkencflag | 1)) {
splitflag = 1; // A point is inserted on a segment.
break;
}
}
encseglist->restart();
if (splitflag) {
// Some segments may need to be repaired.
repairencsegs(chkencflag | 1);
// Queue this subface if it is still alive and not queued.
if (splitfac->sh[3] != NULL) {
if (!smarktest2ed(*splitfac)) {
bface = (badface *) badsubfacs->alloc();
bface->ss = *splitfac;
smarktest2(bface->ss); // Only queue it once.
bface->forg = sorg(*splitfac); // An alive badface.
}
}
}
return splitflag;
} else if (loc == (int) ENCVERTEX) {
// The point lies inside some protecting balls. Rejected.
pointdealloc(newpt);
} else if (loc == (int) ONVERTEX) {
pointdealloc(newpt);
} else if (loc == (int) NEARVERTEX) {
pointdealloc(newpt);
} else if (loc == (int) BADELEMENT) {
// Failed to create a valid sub-cavity in surface mesh.
pointdealloc(newpt);
} else if (loc == (int) ivf.iloc) {
// Flip not locally Delaunay link facets.
lawsonflip3d(newpt, 4, 0, chkencflag, 0);
st_facref_count++;
if (steinerleft > 0) steinerleft--;
return 1; // A point is inserted on facet.
} else {
// Unknown error.
assert(0);
}
// Should not be here.
return 0;
}
///////////////////////////////////////////////////////////////////////////////
// //
// repairencfacs() Repair encroached subfaces. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::repairencfacs(int chkencflag)
{
badface *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 = badfacetraverse(badsubfacs);
while ((bface != NULL) && (steinerleft != 0)) {
// A queued subface may have been deleted (split).
if (bface->ss.sh[3] != NULL) {
// A queued subface may have been processed.
if (smarktest2ed(bface->ss)) {
sunmarktest2(bface->ss);
if (checkfac4split(&(bface->ss), encpt, qflag, ccent)) {
splitsubface(&(bface->ss), encpt, qflag, ccent, chkencflag);
}
}
}
badfacedealloc(badsubfacs, bface); // Remove this entry from list.
bface = badfacetraverse(badsubfacs);
}
}
if (badsubfacs->items > 0) {
if (steinerleft == 0) {
if (b->verbose) {
printf("The desired number of Steiner points is reached.\n");
}
} else {
assert(0); // Unknown case.
}
badsubfacs->traversalinit();
bface = badfacetraverse(badsubfacs);
while (bface != NULL) {
if (bface->ss.sh[3] != NULL) {
if (smarktest2ed(bface->ss)) {
sunmarktest2(bface->ss);
}
}
bface = badfacetraverse(badsubfacs);
}
badsubfacs->restart();
}
}
///////////////////////////////////////////////////////////////////////////////
// //
// checktet4split() Check if the tet needs to be split. //
// //
///////////////////////////////////////////////////////////////////////////////
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, rd;
REAL A[4][4], rhs[4], D;
int indx[4];
int i, j;
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];
if (b->verbose > 2) {
printf(" Check tet (%d, %d, %d, %d)\n", pointmark(pa),
pointmark(pb), pointmark(pc), pointmark(pd));
}
// Get the edge vectors vda: d->a, vdb: d->b, vdc: d->c.
// Set the matrix A = [vda, vdb, vdc]^T.
for (i = 0; i < 3; i++) A[0][i] = vda[i] = pa[i] - pd[i];
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).
if (b->verbose > 2) {
printf(" Min dihed = 0 (degree)\n");
}
// Return its barycenter.
for (i = 0; i < 3; i++) {
ccent[i] = 0.25 * (pa[i] + pb[i] + pc[i] + pd[i]);
}
return 1;
}
// Check volume if '-a#' and '-a' options are used.
if (b->varvolume || b->fixedvolume) {
vol = fabs(A[indx[0]][0] * A[indx[1]][1] * A[indx[2]][2]) / 6.0;
if (b->verbose > 2) {
printf(" volume = %g.\n", vol);
}
if (b->fixedvolume) {
if (vol > b->maxvolume) {
qflag = 1;
}
}
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 (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;
} else {
return 0; // Do not split this tet.
}
}
// 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));
// 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 (b->verbose > 2) {
printf(" Ratio-edge ratio = %g, smlen = %g\n", D, 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]));
assert(L[i] > 0);
//if (L[i] > 0.0) {
for (j = 0; j < 3; j++) N[i][j] /= L[i];
//}
}
// Calculate the six dihedral angles.
cosd[0] = -dot(N[0], N[1]); // Edge cd, bd, bc.
cosd[1] = -dot(N[0], N[2]);
cosd[2] = -dot(N[0], N[3]);
cosd[3] = -dot(N[1], N[2]); // Edge ad, ac
cosd[4] = -dot(N[1], N[3]);
cosd[5] = -dot(N[2], N[3]); // Edge ab
// Get the smallest diehedral 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 (b->verbose > 2) {
printf(" Min dihed = %g (degree)\n", acos(maxcosd) / PI * 180.0);
}
if (maxcosd > cosmindihed) {
// Calculate the circumcenter of this tet.
// A bad dihedral angle.
//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;
}
}
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;
}
}
}
}
return 0;
}
///////////////////////////////////////////////////////////////////////////////
// //
// splittetrahedron() Split a tetrahedron. //
// //
///////////////////////////////////////////////////////////////////////////////
int tetgenmesh::splittetrahedron(triface* splittet, int qflag, REAL *ccent,
int chkencflag)
{
badface *bface;
triface searchtet;
face checkseg, *paryseg;
point newpt, pa, *ppt = NULL;
insertvertexflags ivf;
REAL rd;
int splitflag;
int loc;
int i;
if (b->verbose > 2) {
ppt = (point *) &(splittet->tet[4]);
printf(" Split tet (%d, %d, %d, %d).\n", pointmark(ppt[0]),
pointmark(ppt[1]), pointmark(ppt[2]), pointmark(ppt[3]));
}
if (qflag == 0) {
// It is a bad quality tet (not due to mesh size).
// It can be split if 'ccent' does not encroach upon any prot. balls.
// Do a quick check if the 'ccent' lies inside the protect balls
// of one of the vertices of this tet.
ppt = (point *) &(splittet->tet[4]);
rd = distance(ccent, ppt[0]);
if ((rd <= ppt[0][pointmtrindex]) || (rd <= ppt[1][pointmtrindex]) ||
(rd <= ppt[2][pointmtrindex]) || (rd <= ppt[3][pointmtrindex])) {
if (b->verbose > 2) {
printf(" Encroaching a protecting ball. Rejected.\n");
}
return 0;
}
}
makepoint(&newpt, FREEVOLVERTEX);
for (i = 0; i < 3; i++) newpt[i] = ccent[i];
searchtet = *splittet;
ivf.iloc = (int) OUTSIDE;
// Parameters are chosen as follows:
// - bowywat = 3, preserve subsegments and subfaces;
// - flipflag = 3, check star & link facets for flipping;
// - rejflag = 3, do not insert the point if it encroaches upon
// any segment or subface.
// - chkencflag = 4, (as input), only check tetrahedra.
ivf.bowywat = 3;
ivf.lawson = 3;
ivf.rejflag = 3;
if (qflag == 0) {
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 = 1;
ivf.refineflag = 1;
ivf.refinetet = *splittet;
loc = insertvertex(newpt, &searchtet, NULL, NULL, &ivf);
if (loc == (int) ENCSEGMENT) {
// There are encroached segments.
pointdealloc(newpt);
assert(encseglist->objects > 0);
splitflag = 0;
if (!b->nobisect) { // not -Y option
// Select an encroached segment and split it.
for (i = 0; i < encseglist->objects; i++) {
paryseg = (face *) fastlookup(encseglist, i);
if (splitsegment(paryseg, NULL, qflag, chkencflag | 3)) {
splitflag = 1; // A point is inserted on a segment.
break;
}
}
} // if (!b->nobisect)
encseglist->restart();
if (splitflag) {
// Some segments may need to be repaired.
repairencsegs(chkencflag | 3);
// Some subfaces may need to be repaired.
repairencfacs(chkencflag | 2);
// Queue the tet if it is still alive and not queued.
if (splittet->tet[4] != NULL) {
if (!marktest2ed(*splittet)) {
bface = (badface *) badtetrahedrons->alloc();
bface->tt = *splittet;
marktest2(bface->tt); // Only queue it once.
bface->forg = org(*splittet); // An alive badface.
}
}
}
return splitflag;
} else if (loc == (int) ENCSUBFACE) {
// There are encroached subfaces.
pointdealloc(newpt);
assert(encshlist->objects > 0);
splitflag = 0;
if (!b->nobisect) { // not -Y option
// Select an encroached subface and split it.
for (i = 0; i < encshlist->objects; i++) {
bface = (badface *) fastlookup(encshlist, i);
if (splitsubface(&(bface->ss),NULL,qflag,bface->cent,chkencflag | 2)) {
splitflag = 1; // A point is inserted on a subface or a segment.
break;
}
}
} // if (!b->nobisect)
encshlist->restart();
if (splitflag) {
assert(badsubsegs->items == 0l); // repairencsegs(chkencflag | 3);
// Some subfaces may need to be repaired.
repairencfacs(chkencflag | 2);
// Queue the tet if it is still alive.
if (splittet->tet[4] != NULL) {
if (!marktest2ed(*splittet)) {
bface = (badface *) badtetrahedrons->alloc();
bface->tt = *splittet;
marktest2(bface->tt); // Only queue it once.
bface->forg = org(*splittet); // An alive badface.
}
}
}
return splitflag;
} else if (loc == (int) OUTSIDE) {
// There exists not boundary conforming segments/subfaces.
pointdealloc(newpt);
} else if (loc == (int) ONVERTEX) {
// Found a coincident vertex. It should be a Steiner point.
pa = org(searchtet);
assert(pointtype(pa) == FREEVOLVERTEX);
// Delete this new point.
pointdealloc(newpt);
} else if (loc == (int) NEARVERTEX) {
// The point lies very close to an existing point.
pa = point2ppt(newpt);
assert(pointtype(pa) == FREEVOLVERTEX);
// Delete this new point.
pointdealloc(newpt);
} else if (loc == (int) ENCVERTEX) {
// The new point encoraches upon some protecting balls. Rejected.
pointdealloc(newpt);
} else if (loc == (int) BADELEMENT) {
pointdealloc(newpt);
} else {
// Recover Delaunayness.
lawsonflip3d(newpt, 4, 0, chkencflag, 0);
// Vertex is inserted.
st_volref_count++;
if (steinerleft > 0) steinerleft--;
return 1;
}
return 0;
}
///////////////////////////////////////////////////////////////////////////////
// //
// repairbadtets() Repair bad quality tetrahedra. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::repairbadtets(int chkencflag)
{
badface *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 = badfacetraverse(badtetrahedrons);
while ((bface != NULL) && (steinerleft != 0)) {
// A queued tet may have been deleted.
if (!isdeadtet(bface->tt)) {
// A queued tet may have been processed.
if (marktest2ed(bface->tt)) {
unmarktest2(bface->tt);
if (checktet4split(&(bface->tt), qflag, ccent)) {
splittetrahedron(&(bface->tt), qflag, ccent, chkencflag);
}
}
}
badfacedealloc(badtetrahedrons, bface);
bface = badfacetraverse(badtetrahedrons);
}
}
if (badtetrahedrons->items > 0) {
if (steinerleft == 0) {
if (b->verbose) {
printf("The desired number of Steiner points is reached.\n");
}
} else {
assert(0); // Unknown case.
}
// Unmark all queued tet.
badtetrahedrons->traversalinit();
bface = badfacetraverse(badtetrahedrons);
while (bface != NULL) {
if (!isdeadtet(bface->tt)) {
if (marktest2ed(bface->tt)) {
unmarktest2(bface->tt);
}
}
bface = badfacetraverse(badtetrahedrons);
}
// Clear the pool.
badtetrahedrons->restart();
}
}
///////////////////////////////////////////////////////////////////////////////
// //
// enforcequality() Refine the mesh. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::delaunayrefinement()
{
badface *bface;
triface checktet;
face checksh;
face checkseg;
long steinercount;
int chkencflag;
long bak_segref_count, bak_facref_count, bak_volref_count;
if (!b->quiet) {
printf("Refining mesh...\n");
}
if (b->verbose) {
printf(" Edge length limit = %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) is reached.\n\n",
b->steinerleft);
}
return; // No more Steiner points.
}
}
if (b->refine || b->nobisect) { // '-r' or '-Y' option.
markacutevertices();
}
marksharpsegments();
decidefeaturepointsizes();
encseglist = new arraypool(sizeof(face), 8);
encshlist = new arraypool(sizeof(badface), 8);
if (!b->nobisect) { // if no '-Y' option
if (b->verbose) {
printf(" Splitting encroached subsegments.\n");
}
chkencflag = 1; // Only check encroaching subsegments.
steinercount = points->items;
// Initialize the pool of encroached subsegments.
badsubsegs = new memorypool(sizeof(badface), b->shellfaceperblock,
memorypool::POINTER, 0);
// Add all segments into the pool.
subsegs->traversalinit();
checkseg.sh = shellfacetraverse(subsegs);
while (checkseg.sh != (shellface *) NULL) {
bface = (badface *) badsubsegs->alloc();
bface->ss = checkseg;
smarktest2(bface->ss); // Only queue it once.
bface->forg = sorg(checkseg); // An alive badface.
checkseg.sh = shellfacetraverse(subsegs);
}
// 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(badface), b->shellfaceperblock,
memorypool::POINTER, 0);
// Add all subfaces into the pool.
subfaces->traversalinit();
checksh.sh = shellfacetraverse(subfaces);
while (checksh.sh != (shellface *) NULL) {
bface = (badface *) badsubfacs->alloc();
bface->ss = checksh;
smarktest2(bface->ss); // Only queue it once.
bface->forg = sorg(checksh); // An alive badface.
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");
}
chkencflag = 4; // Only check tetrahedra.
steinercount = points->items;
bak_segref_count = st_segref_count;
bak_facref_count = st_facref_count;
bak_volref_count = st_volref_count;
// The cosine value of the min dihedral angle (-qq) for tetrahedra.
cosmindihed = cos(b->mindihedral / 180.0 * PI);
// Initialize the pool of bad quality tetrahedra.
badtetrahedrons = new memorypool(sizeof(badface), b->tetrahedraperblock,
memorypool::POINTER, 0);
// Add all tetrahedra (no hull tets) into the pool.
tetrahedrons->traversalinit();
checktet.tet = tetrahedrontraverse();
while (checktet.tet != NULL) {
bface = (badface *) badtetrahedrons->alloc();
bface->tt = checktet;
marktest2(bface->tt); // Only queue it once.
bface->forg = org(checktet); // An alive badface.
checktet.tet = tetrahedrontraverse();
}
// 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 (steinerleft == 0) {
if (!b->quiet) {
printf("\nWarnning: ");
printf("The desired number of Steiner points (%d) is reached.\n\n",
b->steinerleft);
}
}
delete encseglist;
delete encshlist;
if (!b->nobisect) {
delete badsubsegs;
if (b->reflevel > 1) {
delete badsubfacs;
}
}
if (b->reflevel > 2) {
delete badtetrahedrons;
}
}
//// ////
//// ////
//// refine_cxx ///////////////////////////////////////////////////////////////
//// optimize_cxx /////////////////////////////////////////////////////////////
//// ////
//// ////
///////////////////////////////////////////////////////////////////////////////
// //
// recoverdelaunay() Recovery the locally Delaunay property. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::recoverdelaunay()
{
arraypool *flipqueue, *nextflipqueue, *swapqueue;
badface *bface, *parybface;
triface tetloop, neightet, *parytet;
point *ppt;
flipconstraints fc;
int i, j;
if (!b->quiet) {
printf("Recovering Delaunayness...\n");
}
if (b->verbose) {
printf(" max_flipstarsize = %d.\n", b->optmaxflipstarsize);
printf(" max_fliplinklevel = %d.\n", b->delmaxfliplevel);
}
calc_tetprism_vol = 1;
tetprism_vol_sum = 0.0; // Initialize it.
assert(flipstack == NULL);
assert(unflipqueue->objects == 0l);
// Put all interior faces of the mesh into 'flipstack'.
tetrahedrons->traversalinit();
tetloop.tet = tetrahedrontraverse();
while (tetloop.tet != NULL) {
for (tetloop.ver = 0; tetloop.ver < 4; tetloop.ver++) {
// Avoid queue a face twice.
fsym(tetloop, neightet);
if (!ishulltet(neightet)) {
if (!facemarked(neightet)) {
flippush(flipstack, &tetloop);
}
}
}
ppt = (point *) &(tetloop.tet[4]);
tetprism_vol_sum += tetprismvol(ppt[0], ppt[1], ppt[2], ppt[3]);
tetloop.tet = tetrahedrontraverse();
}
if (b->verbose) {
printf(" Initial obj = %.17g\n", tetprism_vol_sum);
}
if (b->verbose > 1) {
printf(" Recover Delaunay [Lawson] : %ld\n", flippool->items);
}
assert(unflipqueue->objects == 0l);
// First only use the basic Lawson's flip.
lawsonflip3d(NULL, 4, 0, 0, 1);
if (b->verbose > 1) {
printf(" New obj = %.17g\n", tetprism_vol_sum);
}
if (unflipqueue->objects == 0l) {
// The mesh is Delaunay.
return;
}
// Set the common options.
fc.remove_ndelaunay_edge = 1;
fc.unflip = 1; // Unflip if the edge is not flipped.
fc.collectnewtets = 1;
autofliplinklevel = 1; // Init value.
b->fliplinklevel = -1;
// For efficiency reason, we limit the maximium size of the edge star.
// 'b->optmaxflipstarsize' is set by -OOOOO (5 Os), default is 10.
int bakmaxflipstarsize = b->flipstarsize;
b->flipstarsize = b->optmaxflipstarsize;
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) {
while (flipqueue->objects > 0l) {
if (b->verbose > 1) {
printf(" Recover Delaunay [level = %2d] #: %ld.\n",
autofliplinklevel, flipqueue->objects);
}
for (i = 0; i < flipqueue->objects; i++) {
bface = (badface *) fastlookup(flipqueue, i);
if (getedge(bface->forg, bface->fdest, &bface->tt)) {
// Remember the the objective value (volume of all tetprisms).
fc.bak_tetprism_vol = tetprism_vol_sum;
if (removeedgebyflips(&(bface->tt), &fc) == 2) {
if (b->verbose > 2) {
printf(" Decreased quantity: %.17g.\n",
fc.bak_tetprism_vol - tetprism_vol_sum);
}
// Queue new faces for flips.
for (j = 0; j < cavetetlist->objects; j++) {
parytet = (triface *) fastlookup(cavetetlist, j);
// A queued new tet may be dead.
if (!isdeadtet(*parytet)) {
for (parytet->ver = 0; parytet->ver < 4; parytet->ver++) {
// Avoid queue a face twice.
fsym(*parytet, neightet);
if (!facemarked(neightet)) {
flippush(flipstack, parytet);
}
} // parytet->ver
}
} // j
cavetetlist->restart();
// Remove locally non-Delaunay faces. New non-Delaunay edges
// may be found. They are saved in 'unflipqueue'.
lawsonflip3d(NULL, 4, 0, 0, 1);
} else {
// Unable to remove this edge. Save it.
nextflipqueue->newindex((void **) &parybface);
*parybface = *bface;
}
}
} // i
flipqueue->restart();
// Swap the two flip queues.
swapqueue = flipqueue;
flipqueue = unflipqueue;
unflipqueue = swapqueue;
} // while (flipqueue->objects > 0l)
if (b->verbose > 1) {
printf(" New obj = %.17g.\n", tetprism_vol_sum);
}
// Swap the two flip queues.
swapqueue = flipqueue;
flipqueue = nextflipqueue;
nextflipqueue = swapqueue;
if (flipqueue->objects > 0l) {
// 'b->delmaxfliplevel' is set by -OOOO, default is 1.
if (autofliplinklevel >= b->delmaxfliplevel) {
// For efficiency reason, we do not search too far.
break;
}
autofliplinklevel+=b->fliplinklevelinc;
}
} // while (flipqueue->objects > 0l)
if (flipqueue->objects > 0l) {
if (b->verbose > 1) {
printf(" %ld non-Delaunay edges remained.\n", flipqueue->objects);
}
}
b->flipstarsize = bakmaxflipstarsize;
delete nextflipqueue;
delete flipqueue;
calc_tetprism_vol = 0;
if (b->verbose) {
printf(" Final obj = %.17g\n", tetprism_vol_sum);
}
}
///////////////////////////////////////////////////////////////////////////////
// //
// gettetrahedron() Get a tetrahedron which have the given vertices. //
// //
///////////////////////////////////////////////////////////////////////////////
int tetgenmesh::gettetrahedron(point pa, point pb, point pc, point pd,
triface *searchtet)
{
triface spintet;
if (b->verbose > 2) {
printf(" Get tet [%d,%d,%d,%d].\n", pointmark(pa), pointmark(pb),
pointmark(pc), pointmark(pd));
}
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;
}
///////////////////////////////////////////////////////////////////////////////
// //
// improvequalitybyflips() Improve the mesh quality by flips. //
// //
///////////////////////////////////////////////////////////////////////////////
long tetgenmesh::improvequalitybyflips()
{
arraypool *flipqueue, *nextflipqueue, *swapqueue;
badface *bface, *parybface;
triface *parytet;
point *ppt;
flipconstraints fc;
REAL *cosdd, ncosdd[6], maxdd;
long totalremcount, remcount;
int remflag;
int n, i, j, k;
//assert(unflipqueue->objects > 0l);
flipqueue = new arraypool(sizeof(badface), 10);
nextflipqueue = new arraypool(sizeof(badface), 10);
// Flip edge options.
b->fliplinklevel = -1;
autofliplinklevel = 1; // Init value.
// For efficiency reason, we limit the maximium size of the edge star.
// 'b->optmaxflipstarsize' is set by -OOOOO (5 Os), default is 10.
int bakmaxflipstarsize = b->flipstarsize;
b->flipstarsize = b->optmaxflipstarsize;
fc.remove_large_angle = 1;
fc.unflip = 1;
fc.collectnewtets = 1;
totalremcount = 0l;
// Swap the two flip queues.
swapqueue = flipqueue;
flipqueue = unflipqueue;
unflipqueue = swapqueue;
while (flipqueue->objects > 0l) {
remcount = 0l;
while (flipqueue->objects > 0l) {
if (b->verbose > 1) {
printf(" Improving mesh qualiy by flips [%d]#: %ld.\n",
autofliplinklevel, flipqueue->objects);
}
for (k = 0; k < flipqueue->objects; k++) {
bface = (badface *) fastlookup(flipqueue, k);
if (gettetrahedron(bface->forg, bface->fdest, bface->fapex,
bface->foppo, &bface->tt)) {
// There are bad dihedral angles in this tet.
if (bface->tt.ver != 11) {
// The dihedral angles are permuted.
// Here we simply re-compute them. Slow!!.
ppt = (point *) & (bface->tt.tet[4]);
tetalldihedral(ppt[0], ppt[1], ppt[2], ppt[3], bface->cent,
&maxdd, NULL);
bface->forg = ppt[0];
bface->fdest = ppt[1];
bface->fapex = ppt[2];
bface->foppo = ppt[3];
bface->tt.ver = 11;
}
cosdd = bface->cent;
remflag = 0;
for (i = 0; (i < 6) && !remflag; i++) {
if (cosdd[i] < cosmaxdihed) {
// Found a large dihedral angle.
bface->tt.ver = edge2ver[i]; // Go to the edge.
if (b->verbose > 2) {
printf(" Found a large angle [%d,%d,%d,%d] (%g).\n",
pointmark(org(bface->tt)), pointmark(dest(bface->tt)),
pointmark(apex(bface->tt)), pointmark(oppo(bface->tt)),
acos(cosdd[i]) / PI * 180.0);
}
fc.cosdihed_in = cosdd[i];
fc.cosdihed_out = 0.0; // 90 degree.
n = removeedgebyflips(&(bface->tt), &fc);
if (n == 2) {
// Edge is flipped.
if (b->verbose > 2) {
printf(" Reduced a large angle to %g degree.\n",
acos(fc.cosdihed_out) / PI * 180.0);
}
remflag = 1;
if (fc.cosdihed_out < cosmaxdihed) {
// Queue new bad tets for further improvements.
for (j = 0; j < cavetetlist->objects; j++) {
parytet = (triface *) fastlookup(cavetetlist, j);
if (!isdeadtet(*parytet)) {
ppt = (point *) & (parytet->tet[4]);
//if (!marktest2ed(*parytet)) {
assert(!marktest2ed(*parytet)); // SELF_CHECK
// Do not test a hull tet.
if (ppt[3] != dummypoint) {
tetalldihedral(ppt[0], ppt[1], ppt[2], ppt[3], ncosdd,
&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);
}
totalremcount += remcount;
if (unflipqueue->objects > 0l) {
// 'b->optmaxfliplevel' is set by -OOO, default is 2.
if (autofliplinklevel >= b->optmaxfliplevel) {
// For efficiency reason, we do not search too far.
break;
}
autofliplinklevel+=b->fliplinklevelinc;
}
// Swap the two flip queues.
swapqueue = flipqueue;
flipqueue = unflipqueue;
unflipqueue = swapqueue;
} // while (flipqueues->objects > 0)
b->flipstarsize = bakmaxflipstarsize;
delete flipqueue;
delete nextflipqueue;
return totalremcount;
}
///////////////////////////////////////////////////////////////////////////////
// //
// 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). //
// //
// 'of' is a structure contains the parameters of the objective function. It //
// is needed by the evaluation of the function value. //
// //
// The return value indicates weather the point is smoothed or not. //
// //
// ASSUMPTION: This routine assumes that all link faces are true faces, i.e, //
// no face has 'dummypoint' as its vertex. //
// //
///////////////////////////////////////////////////////////////////////////////
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->verbose > 2) {
printf(" Smooth a point: %ld faces.\n", linkfacelist->objects);
if (opm->min_max_dihedangle) {
printf(" Init value = %g (degree).\n",
acos(opm->initval - 1.0) / PI * 180.0);
} else {
printf(" Init value = %g.\n", opm->initval);
}
}
// Decide the number of moving directions.
numdirs = (int) linkfacelist->objects;
if (numdirs > opm->numofsearchdirs) {
numdirs = opm->numofsearchdirs; // Maximum search directions.
}
// Set the initial value.
if (!opm->max_min_volume) {
assert(opm->initval >= 0.0);
}
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;
} else if (opm->max_min_aspectratio) {
val = tetaspectratio(pa, pb, pc, nextpt);
} else if (opm->min_max_dihedangle) {
tetalldihedral(pa, pb, pc, nextpt, NULL, &maxcosd, NULL);
if (maxcosd < -1) maxcosd = -1.0; // Rounding.
val = maxcosd + 1.0; // Make it be positive.
} else {
// Unknown objective function.
val = 0.0;
}
} else { // ori >= 0.0;
// An invalid new tet.
if (opm->max_min_volume) {
val = -ori;
} 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.
assert(minval > opm->imprval);
opm->imprval = minval;
// Save the new location of the point.
for (j = 0; j < 3; j++) bestpt[j] = nextpt[j];
}
// Swap k-th and (object-i-1)-th entries.
j = linkfacelist->objects - i - 1;
parytet = (triface *) fastlookup(linkfacelist, k);
parytet1 = (triface *) fastlookup(linkfacelist, j);
swaptet = *parytet1;
*parytet1 = *parytet;
*parytet = swaptet;
} // i
diff = opm->imprval - oldval;
if (diff > 0.0) {
// Is the function value improved effectively?
if (opm->max_min_volume) {
//if ((diff / oldval) < b->epsilon) diff = 0.0;
} else if (opm->max_min_aspectratio) {
if ((diff / oldval) < 1e-3) diff = 0.0;
} else if (opm->min_max_dihedangle) {
//oldang = acos(oldval - 1.0);
//newang = acos(opm->imprval - 1.0);
//if ((oldang - newang) < 0.00174) diff = 0.0; // about 0.1 degree.
} else {
// Unknown objective function.
assert(0); // Not possible.
}
}
if (diff > 0.0) {
// Yes, move p to the new location and continue.
for (j = 0; j < 3; j++) startpt[j] = bestpt[j];
iter++;
if ((opm->maxiter > 0) && (iter >= opm->maxiter)) {
// Maximum smoothing iterations reached.
break;
}
} else {
break;
}
} // while (1)
if (iter > 0) {
// The point has been smooothed.
opm->smthiter = iter; // Remember the number of iterations.
if (b->verbose > 2) {
printf(" Smoothed: %d iterations.\n", iter);
if (opm->min_max_dihedangle) {
printf(" Fina value = %g (degree).\n",
acos(opm->imprval - 1.0) / PI * 180.0);
} else {
printf(" Fina value = %g.\n", opm->imprval);
}
}
// The point has been smoothed. Update it to its new position.
for (i = 0; i < 3; i++) smtpt[i] = startpt[i];
if (opm->flipflag) {
// Push all affected faces into 'flipstack'.
triface starttet, neightet;
for (i = 0; i < linkfacelist->objects; i++) {
parytet = (triface *) fastlookup(linkfacelist, i);
starttet = *parytet;
for (starttet.ver = 0; starttet.ver < 4; starttet.ver++) {
fsym(starttet, neightet);
if (!infected(neightet)) {
flippush(flipstack, &starttet);
}
}
infect(*parytet);
}
for (i = 0; i < linkfacelist->objects; i++) {
parytet = (triface *) fastlookup(linkfacelist, i);
uninfect(*parytet);
}
} else if (opm->checkencflag) {
// Push all affected tets into pool.
badface *bface;
for (i = 0; i < linkfacelist->objects; i++) {
parytet = (triface *) fastlookup(linkfacelist, i);
if (!marktest2ed(*parytet)) {
marktest2(*parytet); // Only queue it once.
bface = (badface *) badtetrahedrons->alloc();
bface->tt = *parytet;
bface->forg = org(bface->tt);
}
}
}
} else {
if (b->verbose > 2) {
printf(" Not smoothed.\n");
}
}
return iter;
}
///////////////////////////////////////////////////////////////////////////////
// //
// improvequalitysmoothing() Improve mesh quality by smoothing. //
// //
///////////////////////////////////////////////////////////////////////////////
long tetgenmesh::improvequalitybysmoothing(optparameters *opm)
{
arraypool *flipqueue, *swapqueue;
badface *bface, *parybface;
point *ppt;
long totalsmtcount, smtcount;
int smtflag;
int iter, i, k;
//assert(unflipqueue->objects > 0l);
flipqueue = new arraypool(sizeof(badface), 10);
totalsmtcount = 0l;
// Swap the two flip queues.
swapqueue = flipqueue;
flipqueue = unflipqueue;
unflipqueue = swapqueue;
iter = 0;
while (flipqueue->objects > 0l) {
smtcount = 0l;
//while (flipqueue->objects > 0l) {
if (b->verbose > 1) {
printf(" Improving mesh qualiy by smoothing [%d]#: %ld.\n",
iter, flipqueue->objects);
}
for (k = 0; k < flipqueue->objects; k++) {
bface = (badface *) fastlookup(flipqueue, k);
if (gettetrahedron(bface->forg, bface->fdest, bface->fapex,
bface->foppo, &bface->tt)) {
if (!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;
//if (opm->min_max_dihedangle) {
opm->initval = bface->key + 1.0;
//opm->checkencflag = 4; // Queue affected tets.
//}
for (i = 0; (i < 4) && !smtflag; i++) {
if (pointtype(ppt[i]) == FREEVOLVERTEX) {
getvertexstar(1, ppt[i], cavetetlist, NULL, NULL);
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);
}
smtcount++;
}
cavetetlist->restart();
}
} // i
if (smtflag) {
// This tet is modifed.
smtcount++;
if ((opm->imprval - 1.0) < cossmtdihed) {
// Queue new slivers.
badtetrahedrons->traversalinit();
bface = badfacetraverse(badtetrahedrons);
while (bface != NULL) {
assert(!isdeadtet(bface->tt));
assert(marktest2ed(bface->tt));
unmarktest2(bface->tt);
if (!marktested(bface->tt)) {
ppt = (point *) & (bface->tt.tet[4]);
tetalldihedral(ppt[0], ppt[1], ppt[2], ppt[3], bface->cent,
&(bface->key), NULL);
if (bface->key < cossmtdihed) {
// A new sliver. Queue it.
marktest(bface->tt); // It is in unflipqueue.
bface->forg = ppt[0];
bface->fdest = ppt[1];
bface->fapex = ppt[2];
bface->foppo = ppt[3];
bface->tt.ver = 11;
unflipqueue->newindex((void **) &parybface);
*parybface = *bface;
}
}
bface = badfacetraverse(badtetrahedrons);
}
} else {
// No new slivers. Only unmark the queued tets.
badtetrahedrons->traversalinit();
bface = badfacetraverse(badtetrahedrons);
while (bface != NULL) {
assert(!isdeadtet(bface->tt));
assert(marktest2ed(bface->tt));
unmarktest2(bface->tt);
bface = badfacetraverse(badtetrahedrons);
}
}
badtetrahedrons->restart();
} else {
// Didn't smooth. Queue it again.
// Adjust the vertices for flipping.
marktest(bface->tt); // It is in unflipqueue.
bface->forg = ppt[0];
bface->fdest = ppt[1];
bface->fapex = ppt[2];
bface->foppo = ppt[3];
bface->tt.ver = 11;
unflipqueue->newindex((void **) &parybface);
*parybface = *bface;
}
} // if (maxdd < cosslidihed)
} // if (!marktested(...))
} // gettetrahedron(...)
} // k
flipqueue->restart();
// } // while
// Unmark the tets in unflipqueue.
for (i = 0; i < unflipqueue->objects; i++) {
bface = (badface *) fastlookup(unflipqueue, i);
assert(!isdeadtet(bface->tt));
assert(marktested(bface->tt));
unmarktest(bface->tt);
}
if (b->verbose > 1) {
printf(" Smooth %ld points.\n", smtcount);
}
totalsmtcount += smtcount;
if (smtcount == 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
delete flipqueue;
return totalsmtcount;
}
///////////////////////////////////////////////////////////////////////////////
// //
// splitsliver() Split a sliver. //
// //
///////////////////////////////////////////////////////////////////////////////
int tetgenmesh::splitsliver(triface *slitet, REAL cosd, int chkencflag)
{
triface *abtets;
triface searchtet, spintet, *parytet;
face checkseg;
point pa, pb, steinerpt;
optparameters opm;
insertvertexflags ivf;
REAL smtpt[3], midpt[3];
int success;
int loc;
int n, i;
// 'slitet' is [c,d,a,b], where [c,d] has a big hihedral angle.
// Go to the opposite edge [a,b].
eprev(*slitet, searchtet);
esymself(searchtet);
enextself(searchtet); // [a,b,c,d].
// Do not split a segment.
tsspivot1(searchtet, checkseg);
if (checkseg.sh != NULL) {
return 0;
}
// Count the number of tets shared at [a,b].
spintet = searchtet;
n = 0;
while (1) {
n++;
fnextself(spintet);
if (spintet.tet == searchtet.tet) break;
}
assert(n >= 3);
// 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) {
if (b->verbose > 2) {
printf(" Unable to relocate the initial point.\n");
}
delete [] abtets;
return 0;
}
if (steinerleft == 0) {
// The desired number of Steiner points is reached.
return 0;
}
// Insert the Steiner point.
makepoint(&steinerpt, FREEVOLVERTEX);
for (i = 0; i < 3; i++) steinerpt[i] = smtpt[i];
// 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.
ivf.iloc = (int) INSTAR;
ivf.bowywat = 0; // Do not use Bowyer-Watson algorithm.
ivf.lawson = 0; // Do not flip.
ivf.rejflag = 0;
ivf.chkencflag = chkencflag;
ivf.sloc = 0;
ivf.sbowywat = 0;
ivf.splitbdflag = 0;
ivf.validflag = 0;
ivf.respectbdflag = 0;
ivf.assignmeshsize = 0;
loc = insertvertex(steinerpt, &searchtet, NULL, NULL, &ivf);
if (loc == (int) INSTAR) {
// The vertex has been inserted.
st_volref_count++; //st_inpoly_count++;
if (steinerleft > 0) steinerleft--;
return 1;
} else {
// The Steiner point is too close to an existing vertex. Reject it.
pointdealloc(steinerpt);
return 0;
}
delete [] abtets;
}
///////////////////////////////////////////////////////////////////////////////
// //
// removeslivers() Remove slivers by adding Steiner points. //
// //
///////////////////////////////////////////////////////////////////////////////
long tetgenmesh::removeslivers(int chkencflag)
{
arraypool *flipqueue, *swapqueue;
badface *bface, *parybface;
point *ppt;
REAL *cosdd;
long totalsptcount, sptcount;
int iter, j, k;
//assert(unflipqueue->objects > 0l);
flipqueue = new arraypool(sizeof(badface), 10);
totalsptcount = 0l;
// Swap the two flip queues.
swapqueue = flipqueue;
flipqueue = unflipqueue;
unflipqueue = swapqueue;
iter = 0;
while (flipqueue->objects > 0l) {
sptcount = 0l;
if (b->verbose > 1) {
printf(" Splitting bad quality tets [%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)) {
//if (!marktested(bface->tt)) {
// Here we simply re-compute the quality. Since other smoothing
// operation may have moved the vertices of this tet.
ppt = (point *) & (bface->tt.tet[4]);
tetalldihedral(ppt[0], ppt[1], ppt[2], ppt[3], bface->cent,
&bface->key, NULL);
if (bface->key < cosslidihed) {
// It is a sliver. Try to split it.
cosdd = bface->cent;
for (j = 0; j < 6; j++) {
if (cosdd[j] < cosslidihed) {
// Found a large dihedral angle.
bface->tt.ver = edge2ver[j]; // Go to the edge.
if (b->verbose > 2) {
printf(" Found a bad tet [%d,%d,%d,%d] (%g).\n",
pointmark(org(bface->tt)), pointmark(dest(bface->tt)),
pointmark(apex(bface->tt)), pointmark(oppo(bface->tt)),
acos(cosdd[j]) / PI * 180.0);
}
if (splitsliver(&(bface->tt), cosdd[j], chkencflag)) {
sptcount++;
break;
}
}
} // j
if (j < 6) {
// A sliver is split. Queue new slivers.
badtetrahedrons->traversalinit();
bface = badfacetraverse(badtetrahedrons);
while (bface != NULL) {
assert(!isdeadtet(bface->tt));
assert(marktest2ed(bface->tt));
unmarktest2(bface->tt);
ppt = (point *) & (bface->tt.tet[4]);
tetalldihedral(ppt[0], ppt[1], ppt[2], ppt[3], bface->cent,
&(bface->key), NULL);
if (bface->key < cosslidihed) {
// A new sliver. Queue it.
//marktest(bface->tt); // It is in unflipqueue.
bface->forg = ppt[0];
bface->fdest = ppt[1];
bface->fapex = ppt[2];
bface->foppo = ppt[3];
bface->tt.ver = 11;
unflipqueue->newindex((void **) &parybface);
*parybface = *bface;
}
bface = badfacetraverse(badtetrahedrons);
}
badtetrahedrons->restart();
} else {
// Didn't split. Queue it again.
// Adjust the vertices for flipping.
//marktest(bface->tt); // It is in unflipqueue.
bface->forg = ppt[0];
bface->fdest = ppt[1];
bface->fapex = ppt[2];
bface->foppo = ppt[3];
bface->tt.ver = 11;
unflipqueue->newindex((void **) &parybface);
*parybface = *bface;
} // if (j == 6)
} // if (bface->key < cosslidihed)
// } // if (!marktested(bface->tt))
} // if (gettetrahedron(...))
} // k
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
delete flipqueue;
return totalsptcount;
}
///////////////////////////////////////////////////////////////////////////////
// //
// optimizemesh() Optimize mesh for specified objective functions. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::optimizemesh(int optflag)
{
badface *parybface;
triface checktet;
point *ppt;
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");
}
if (b->verbose > 1) {
printf(" min_max_dihedral = %g.\n", b->optmaxdihedral);
printf(" max_flipstarsize = %d.\n", b->optmaxflipstarsize);
printf(" max_fliplinklevel = %d.\n", b->optmaxfliplevel);
printf(" number of passes = %d.\n", b->optpasses);
}
totalsmtcount = totalsptcount = totalremcount = 0l;
if (b->verbose > 1) {
printf(" Removing large angles (> %g degree).\n", b->optmaxdihedral);
}
cosmaxdihed = cos(b->optmaxdihedral / 180.0 * PI);
cossmtdihed = cos(b->optminsmtdihed / 180.0 * PI);
cosslidihed = cos(b->optminslidihed / 180.0 * PI);
// Put all bad tetrahedra into array.
tetrahedrons->traversalinit();
checktet.tet = tetrahedrontraverse();
while (checktet.tet != NULL) {
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->optlevel & 2) || (b->optlevel & 4))) { // -O2 | -O4
badtetrahedrons = new memorypool(sizeof(badface), b->tetrahedraperblock,
memorypool::POINTER, 0);
// Smoothing options.
opm.min_max_dihedangle = 1;
opm.numofsearchdirs = 10;
// opm.searchstep = 0.001;
opm.maxiter = 30; // Limit the maximum iterations.
opm.checkencflag = 4; // Queue affected tets after smoothing.
chkencflag = 4; // Queue affected tets after splitting a sliver.
iter = 0;
while (iter < b->optpasses) {
smtcount = sptcount = remcount = 0l;
if (b->optlevel & 2) {
smtcount += improvequalitybysmoothing(&opm);
totalsmtcount += smtcount;
if (smtcount > 0l) {
remcount = improvequalitybyflips();
totalremcount += remcount;
}
}
if (unflipqueue->objects > 0l) {
if (b->optlevel & 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;
} // // -O2 | -O4
if (unflipqueue->objects > 0l) {
if (b->verbose > 1) {
printf(" %ld bad tets remained.\n", unflipqueue->objects);
}
unflipqueue->restart();
}
if (b->verbose) {
if (totalremcount > 0l) {
printf(" Removed %ld bad tets.\n", totalremcount);
}
if (totalsmtcount > 0l) {
printf(" Smoothed %ld points.\n", totalsmtcount);
}
if (totalsptcount > 0l) {
printf(" Split %ld bad tets.\n", totalsptcount);
}
}
}
//// ////
//// ////
//// optimize_cxx /////////////////////////////////////////////////////////////
//// meshstat_cxx /////////////////////////////////////////////////////////////
//// ////
//// ////
///////////////////////////////////////////////////////////////////////////////
// //
// 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. //
// //
///////////////////////////////////////////////////////////////////////////////
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++;
}
}
}
if (infected(tetloop)) {
// This may be a bug. Report it.
printf(" !! (%d, %d, %d, %d) is infected.\n", pointmark(pa),
pointmark(pb), pointmark(pc), pointmark(pd));
horrors++;
}
if (marktested(tetloop)) {
// This may be a bug. Report it.
printf(" !! (%d, %d, %d, %d) is marked.\n", pointmark(pa),
pointmark(pb), pointmark(pc), pointmark(pd));
horrors++;
}
}
if (tetloop.tet[tetloop.ver] == NULL) {
printf(" !! !! No neighbor at face (%d, %d, %d).\n", pointmark(pa),
pointmark(pb), pointmark(pc));
horrors++;
} else {
// Find the neighboring tetrahedron on this face.
fsym(tetloop, neightet);
// Check that the tetrahedron's neighbor knows it's a neighbor.
fsym(neightet, symtet);
if ((tetloop.tet != symtet.tet) || (tetloop.ver != symtet.ver)) {
printf(" !! !! Asymmetric tetra-tetra bond:\n");
if (tetloop.tet == symtet.tet) {
printf(" (Right tetrahedron, wrong orientation)\n");
}
printf(" First: (%d, %d, %d, %d)\n", pointmark(pa),
pointmark(pb), pointmark(pc), pointmark(pd));
printf(" Second: (%d, %d, %d, %d)\n", pointmark(org(neightet)),
pointmark(dest(neightet)), pointmark(apex(neightet)),
pointmark(oppo(neightet)));
horrors++;
}
// Check if they have the same edge (the bond() operation).
if ((org(neightet) != pb) || (dest(neightet) != pa)) {
printf(" !! !! Wrong edge-edge bond:\n");
printf(" First: (%d, %d, %d, %d)\n", pointmark(pa),
pointmark(pb), pointmark(pc), pointmark(pd));
printf(" Second: (%d, %d, %d, %d)\n", pointmark(org(neightet)),
pointmark(dest(neightet)), pointmark(apex(neightet)),
pointmark(oppo(neightet)));
horrors++;
}
// Check if they have the same apex.
if (apex(neightet) != pc) {
printf(" !! !! Wrong face-face bond:\n");
printf(" First: (%d, %d, %d, %d)\n", pointmark(pa),
pointmark(pb), pointmark(pc), pointmark(pd));
printf(" Second: (%d, %d, %d, %d)\n", pointmark(org(neightet)),
pointmark(dest(neightet)), pointmark(apex(neightet)),
pointmark(oppo(neightet)));
horrors++;
}
// Check if they have the same opposite.
if (oppo(neightet) == pd) {
printf(" !! !! Two identical tetra:\n");
printf(" First: (%d, %d, %d, %d)\n", pointmark(pa),
pointmark(pb), pointmark(pc), pointmark(pd));
printf(" Second: (%d, %d, %d, %d)\n", pointmark(org(neightet)),
pointmark(dest(neightet)), pointmark(apex(neightet)),
pointmark(oppo(neightet)));
horrors++;
}
}
if (facemarked(tetloop)) {
// This may be a bug. Report it.
printf(" !! tetface (%d, %d, %d) %d is marked.\n", pointmark(pa),
pointmark(pb), pointmark(pc), pointmark(pd));
}
}
// 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();
}
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");
}
return horrors;
}
///////////////////////////////////////////////////////////////////////////////
// //
// checkshells() Test the boundary mesh for topological consistency. //
// //
///////////////////////////////////////////////////////////////////////////////
int tetgenmesh::checkshells(/*int sub2tet*/)
{
triface neightet, symtet;
face shloop, spinsh, nextsh;
face checkseg;
point pa, pb; //, *ppt;
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", (unsigned long) spinsh.sh,
pointmark(sorg(spinsh)), pointmark(sdest(spinsh)),
pointmark(sapex(spinsh)));
printf(" Second: x%lx (DEAD)\n", (unsigned long) 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", (unsigned long) spinsh.sh,
pointmark(sorg(spinsh)), pointmark(sdest(spinsh)),
pointmark(sapex(spinsh)));
printf(" Scond: x%lx (%d, %d, %d).\n", (unsigned long) 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", (unsigned long) spinsh.sh,
pointmark(sorg(spinsh)), pointmark(sdest(spinsh)),
pointmark(sapex(spinsh)));
printf(" Scond: x%lx (%d, %d, %d).\n", (unsigned long) 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", (unsigned long) shloop.sh,
pointmark(sorg(shloop)), pointmark(sdest(shloop)),
pointmark(sapex(shloop)));
printf(" Sub: x%lx (Dead)\n", (unsigned long) checkseg.sh);
horrors++;
} else {
if (!(((sorg(checkseg) == pa) && (sdest(checkseg) == pb)) ||
((sorg(checkseg) == pb) && (sdest(checkseg) == pa)))) {
printf(" !! !! Wrong subface-subseg connection.\n");
printf(" Sub: x%lx (%d, %d, %d).\n", (unsigned long) shloop.sh,
pointmark(sorg(shloop)), pointmark(sdest(shloop)),
pointmark(sapex(shloop)));
printf(" Seg: x%lx (%d, %d).\n", (unsigned long) 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", (unsigned long) shloop.sh,
pointmark(sorg(shloop)), pointmark(sdest(shloop)),
pointmark(sapex(shloop)));
printf(" Tet: x%lx (DEAD)\n", (unsigned long) neightet.tet);
horrors++;
} else {
if (!((sorg(shloop) == org(neightet)) &&
(sdest(shloop) == dest(neightet)))) {
printf(" !! !! Wrong sub-to-tet connection\n");
printf(" Sub: x%lx (%d, %d, %d).\n", (unsigned long) shloop.sh,
pointmark(sorg(shloop)), pointmark(sdest(shloop)),
pointmark(sapex(shloop)));
printf(" Tet: x%lx (%d, %d, %d, %d).\n",
(unsigned long) 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", (unsigned long) spinsh.sh,
pointmark(sorg(spinsh)), pointmark(sdest(spinsh)),
pointmark(sapex(spinsh)));
printf(" Tet: x%lx (%d, %d, %d, %d).\n",
(unsigned long) 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", (unsigned long) spinsh.sh,
pointmark(sorg(spinsh)), pointmark(sdest(spinsh)),
pointmark(sapex(spinsh)));
printf(" Tet: x%lx (%d, %d, %d, %d).\n",
(unsigned long) 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 (horrors == 0) {
if (!b->quiet) {
printf(" Mesh boundaries connected correctly.\n");
}
} else {
printf(" !! !! !! !! %d boundary connection viewed with horror.\n",
horrors);
}
subfaces->pathblock = bakpathblock;
subfaces->pathitem = bakpathitem;
subfaces->pathitemsleft = bakpathitemsleft;
subfaces->alignbytes = bakalignbytes;
return horrors;
}
///////////////////////////////////////////////////////////////////////////////
// //
// checksegments() Check the connections between tetrahedra and segments. //
// //
///////////////////////////////////////////////////////////////////////////////
int tetgenmesh::checksegments()
{
triface tetloop, neightet, spintet;
shellface *segs;
face neighsh, spinsh, checksh;
face sseg, checkseg;
point pa, pb;
int miscount;
int 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",
(unsigned long) tetloop.tet, pointmark(org(tetloop)),
pointmark(dest(tetloop)), pointmark(apex(tetloop)),
pointmark(oppo(tetloop)), (unsigned long) sseg.sh,
pointmark(pa), pointmark(pb));
horrors++;
} else {
// Loop all tets sharing at this edge.
neightet = tetloop;
do {
tsspivot1(neightet, checkseg);
if (checkseg.sh != sseg.sh) {
printf(" !! Wrong tet->seg connection.\n");
printf(" Tet: x%lx (%d, %d, %d, %d) - ",
(unsigned long) neightet.tet, pointmark(org(neightet)),
pointmark(dest(neightet)), pointmark(apex(neightet)),
pointmark(oppo(neightet)));
if (checkseg.sh != NULL) {
printf("Seg x%lx (%d, %d).\n", (unsigned long) 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",
(unsigned long) neightet.tet, pointmark(org(neightet)),
pointmark(dest(neightet)), pointmark(apex(neightet)),
pointmark(oppo(neightet)), (unsigned long) 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)),
(unsigned long) 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)),
(unsigned long) spintet.tet, spintet.ver);
horrors++;
}
if (spintet.tet == neightet.tet) break;
}
}
}
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)), (unsigned long) 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)),
(unsigned long) 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)), (unsigned long) 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) {
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 (sdest(checkseg) != pa) {
printf(" !! Wrong seg-seg connection at point %d.\n",
pointmark(pa));
horrors++;
}
}
}
}
}
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. //
// //
///////////////////////////////////////////////////////////////////////////////
int tetgenmesh::checkdelaunay()
{
triface tetloop;
triface symtet;
face checksh;
point pa, pb, pc, pd, pe;
REAL sign;
int ndcount; // Count the non-locally Delaunay faces.
int horrors;
if (!b->quiet) {
printf(" Checking Delaunay property of the mesh...\n");
}
ndcount = 0;
horrors = 0;
tetloop.ver = 0;
// Run through the list of triangles, checking each one.
tetrahedrons->traversalinit();
tetloop.tet = tetrahedrontraverse();
while (tetloop.tet != (tetrahedron *) NULL) {
// Check all four faces of the tetrahedron.
for (tetloop.ver = 0; tetloop.ver < 4; tetloop.ver++) {
fsym(tetloop, symtet);
// Only do test if its adjoining tet is not a hull tet or its pointer
// is larger (to ensure that each pair isn't tested twice).
if (((point) symtet.tet[7] != dummypoint)&&(tetloop.tet < symtet.tet)) {
pa = org(tetloop);
pb = dest(tetloop);
pc = apex(tetloop);
pd = oppo(tetloop);
pe = oppo(symtet);
sign = insphere_s(pa, pb, pc, pd, pe);
if (sign < 0.0) {
ndcount++;
if (checksubfaceflag) {
tspivot(tetloop, checksh);
}
if (checksh.sh == NULL) {
printf(" !! Non-locally Delaunay (%d, %d, %d) - %d, %d\n",
pointmark(pa), pointmark(pb), pointmark(pc), pointmark(pd),
pointmark(pe));
horrors++;
}
}
}
}
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. //
// //
// 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::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++;
}
}
}
}
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. //
// //
// If 'flag' is 1, only check subsegments. If 'flag' is 2, check subfaces. //
// If 'flag' is 3, check both subsegments and subfaces. //
// //
///////////////////////////////////////////////////////////////////////////////
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 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 {
printf(" !! !! %d subsegments are non-conforming.\n", encsubsegs);
}
} // if (flag & 1)
encsubfaces = 0;
if (flag & 2) {
if (!b->quiet) {
printf(" Checking conforming property of subfaces...\n");
}
// 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 (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 encsubsegs + encsubfaces;
}
///////////////////////////////////////////////////////////////////////////////
// //
// qualitystatistics() Print statistics about the quality of the mesh. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::qualitystatistics()
{
triface tetloop, neightet;
point p[4];
char sbuf[128];
REAL radiusratiotable[12];
REAL aspectratiotable[12];
REAL A[4][4], rhs[4], D;
REAL V[6][3], N[4][3], H[4]; // edge-vectors, face-normals, face-heights.
REAL edgelength[6], alldihed[6], faceangle[3];
REAL shortest, longest;
REAL smallestvolume, biggestvolume;
REAL smallestratio, biggestratio;
REAL smallestdiangle, biggestdiangle;
REAL smallestfaangle, biggestfaangle;
REAL 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;
int radiustable[12];
int aspecttable[16];
int dihedangletable[18];
int faceangletable[18];
int indx[4];
int radiusindex;
int aspectindex;
int tendegree;
int i, j;
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] = 0;
for (i = 0; i < 12; i++) aspecttable[i] = 0;
for (i = 0; i < 18; i++) dihedangletable[i] = 0;
for (i = 0; i < 18; i++) faceangletable[i] = 0;
minaltitude = xmax - xmin + ymax - ymin + zmax - zmin;
minaltitude = minaltitude * minaltitude;
shortest = minaltitude;
longest = 0.0;
smallestvolume = minaltitude;
biggestvolume = 0.0;
smallestratio = 1e+16; // minaltitude;
biggestratio = 0.0;
smallestdiangle = smallestfaangle = 180.0;
biggestdiangle = biggestfaangle = 0.0;
// Loop all elements, calculate quality parameters for each element.
tetrahedrons->traversalinit();
tetloop.tet = tetrahedrontraverse();
while (tetloop.tet != (tetrahedron *) NULL) {
// Get four vertices: p0, p1, p2, p3.
for (i = 0; i < 4; i++) p[i] = (point) tetloop.tet[4 + i];
// Get the tet volume.
tetvol = orient3d(p[1], p[0], p[2], p[3]) / 6.0;
total_tet_vol += tetvol;
total_tetprism_vol += tetprismvol(p[0], p[1], p[2], p[3]);
// Calculate the largest and smallest volume.
if (tetvol < smallestvolume) {
smallestvolume = tetvol;
}
if (tetvol > biggestvolume) {
biggestvolume = tetvol;
}
// 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 lengthes.
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];
}
// Decompose A just once.
if (lu_decmp(A, 3, indx, &D, 0)) {
// Get the three faces normals.
for (j = 0; j < 3; j++) {
for (i = 0; i < 3; i++) rhs[i] = 0.0;
rhs[j] = 1.0; // Positive means the inside direction
lu_solve(A, 3, indx, rhs, 0);
for (i = 0; i < 3; i++) N[j][i] = rhs[i];
}
// Get the fourth face normal by summing up the first three.
for (i = 0; i < 3; i++) N[3][i] = - N[0][i] - N[1][i] - N[2][i];
// Get the radius of the circumsphere.
for (i = 0; i < 3; i++) rhs[i] = 0.5 * dot(V[i], V[i]);
lu_solve(A, 3, indx, rhs, 0);
cirradius = sqrt(dot(rhs, rhs));
// Normalize the face normals.
for (i = 0; i < 4; i++) {
// H[i] is the inverse of height of its corresponding face.
H[i] = sqrt(dot(N[i], N[i]));
for (j = 0; j < 3; j++) N[i][j] /= H[i];
}
// Get the radius of the inscribed sphere.
// insradius = 1.0 / (H[0] + H[1] + H[2] + H[3]);
// Get the biggest H[i] (corresponding to the smallest height).
minheightinv = H[0];
for (i = 1; i < 3; i++) {
if (H[i] > minheightinv) minheightinv = H[i];
}
} else {
// A nearly degenerated tet.
if (tetvol <= 0.0) {
// assert(tetvol != 0.0);
printf(" !! Warning: A %s tet (%d,%d,%d,%d).\n",
tetvol < 0 ? "inverted" : "degenerated", pointmark(p[0]),
pointmark(p[1]), pointmark(p[2]), pointmark(p[3]));
// Skip it.
tetloop.tet = tetrahedrontraverse();
continue;
}
// Calculate the four face normals.
facenormal(p[2], p[1], p[3], N[0], 1, NULL);
facenormal(p[0], p[2], p[3], N[1], 1, NULL);
facenormal(p[1], p[0], p[3], N[2], 1, NULL);
facenormal(p[0], p[1], p[2], N[3], 1, NULL);
// Normalize the face normals.
for (i = 0; i < 4; i++) {
// H[i] is the twice of the area of the face.
H[i] = sqrt(dot(N[i], N[i]));
for (j = 0; j < 3; j++) N[i][j] /= H[i];
}
// Get the biggest H[i] / tetvol (corresponding to the smallest height).
minheightinv = (H[0] / tetvol);
for (i = 1; i < 3; i++) {
if ((H[i] / tetvol) > minheightinv) minheightinv = (H[i] / tetvol);
}
// Let the circumradius to be the half of its longest edge length.
cirradius = 0.5 * sqrt(longlen);
}
// 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.
} else {
tendegree--; // In the right column.
}
}
dihedangletable[tendegree]++;
}
// Calulate 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]++;
}
}
}
// Calculate aspect ratio and radius-edge ratio for this element.
tetradius = cirradius / sqrt(shortlen);
// tetaspect = sqrt(longlen) / (2.0 * insradius);
tetaspect = sqrt(longlen) * minheightinv;
// Remember the largest and smallest aspect ratio.
if (tetaspect < smallestratio) {
smallestratio = tetaspect;
}
if (tetaspect > biggestratio) {
biggestratio = tetaspect;
}
// Accumulate the corresponding number in the aspect ratio histogram.
aspectindex = 0;
while ((tetaspect > aspectratiotable[aspectindex]) && (aspectindex < 11)) {
aspectindex++;
}
aspecttable[aspectindex]++;
radiusindex = 0;
while ((tetradius > radiusratiotable[radiusindex]) && (radiusindex < 11)) {
radiusindex++;
}
radiustable[radiusindex]++;
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 : %8d | %6.6g - %-6.6g : %8d\n",
aspectratiotable[0], aspecttable[0], aspectratiotable[5],
aspectratiotable[6], aspecttable[6]);
for (i = 1; i < 5; i++) {
printf(" %6.6g - %-6.6g : %8d | %6.6g - %-6.6g : %8d\n",
aspectratiotable[i - 1], aspectratiotable[i], aspecttable[i],
aspectratiotable[i + 5], aspectratiotable[i + 6],
aspecttable[i + 6]);
}
printf(" %6.6g - %-6.6g : %8d | %6.6g - : %8d\n",
aspectratiotable[4], aspectratiotable[5], aspecttable[5],
aspectratiotable[10], aspecttable[11]);
printf(" (A tetrahedron's aspect ratio is its longest edge length");
printf(" divided by its\n");
printf(" smallest side height)\n\n");
printf(" Face angle histogram:\n");
for (i = 0; i < 9; i++) {
printf(" %3d - %3d degrees: %8d | %3d - %3d degrees: %8d\n",
i * 10, i * 10 + 10, faceangletable[i],
i * 10 + 90, i * 10 + 100, faceangletable[i + 9]);
}
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: %8d | %3d - %3d degrees: %8d\n",
0, 5, dihedangletable[0], 80, 110, dihedangletable[9]);
printf(" %3d - %2d degrees: %8d | %3d - %3d degrees: %8d\n",
5, 10, dihedangletable[1], 110, 120, dihedangletable[10]);
// Print the third to seventh rows.
for (i = 2; i < 7; i++) {
printf(" %3d - %2d degrees: %8d | %3d - %3d degrees: %8d\n",
(i - 1) * 10, (i - 1) * 10 + 10, dihedangletable[i],
(i - 1) * 10 + 110, (i - 1) * 10 + 120, dihedangletable[i + 9]);
}
// Print the last two rows.
printf(" %3d - %2d degrees: %8d | %3d - %3d degrees: %8d\n",
60, 70, dihedangletable[7], 170, 175, dihedangletable[16]);
printf(" %3d - %2d degrees: %8d | %3d - %3d degrees: %8d\n",
70, 80, dihedangletable[8], 175, 180, dihedangletable[17]);
if (minfacetdihed != PI) {
printf(" Minimum input dihedral angle is %g (degree).\n",
minfacetdihed / PI * 180.0);
}
printf("\n");
printf("\n");
}
///////////////////////////////////////////////////////////////////////////////
// //
// statistics() Print all sorts of cool facts. //
// //
///////////////////////////////////////////////////////////////////////////////
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 (b->plc) {
printf(" Input facets: %d\n", in->numberoffacets);
printf(" Input segments: %ld\n", insegments);
printf(" Input holes: %d\n", in->numberofholes);
printf(" Input regions: %d\n", in->numberofregions);
}
tetnumber = tetrahedrons->items - hullsize;
facenumber = (tetnumber * 4l + hullsize) / 2l;
printf("\n Mesh points: %ld\n", points->items);
printf(" Mesh tetrahedra: %ld\n", tetnumber);
printf(" Mesh faces: %ld\n", facenumber);
printf(" Mesh edges: %ld\n", meshedges);
if (b->plc || b->refine) {
printf(" Mesh boundary faces: %ld\n", subfaces->items);
printf(" Mesh boundary edges: %ld\n", subsegs->items);
if (st_segref_count > 0l) {
printf(" Steiner points in boundary edges: %ld\n", st_segref_count);
}
if (st_facref_count > 0l) {
printf(" Steiner points in boundary faces: %ld\n", st_facref_count);
}
if (st_volref_count > 0l) {
printf(" Steiner points in mesh domain: %ld\n", st_volref_count);
}
} else {
printf(" Convex hull faces: %ld\n", hullsize);
printf(" Convex hull edges: %ld\n", meshhulledges);
}
printf("\n");
if (b->verbose > 0) {
if (b->plc || b->refine) { // -p or -r
if (tetrahedrons->items > 0l) {
qualitystatistics();
}
}
}
}
//// ////
//// ////
//// meshstat_cxx /////////////////////////////////////////////////////////////
//// output_cxx ///////////////////////////////////////////////////////////////
//// ////
//// ////
///////////////////////////////////////////////////////////////////////////////
// //
// jettisonnodes() Jettison unused or duplicated vertices. //
// //
// Unused points are those input points which are outside the mesh domain or //
// have no connection (isolated) to the mesh. Duplicated points exist for //
// example if the input PLC is read from a .stl mesh file (marked during the //
// Delaunay tetrahedralization step. This routine remove these points from //
// points list. All existing points are reindexed. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::jettisonnodes()
{
point pointloop;
bool jetflag;
int oldidx, newidx;
int remcount;
if (!b->quiet) {
printf("Jettisoning redundants 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 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++;
}
oldidx++;
//if (oldidx == in->numberofpoints) {
// // Update the numbe of input points (Because some were removed).
// in->numberofpoints -= remcount;
// // Remember this number for output original input nodes.
// jettisoninverts = remcount;
//}
pointloop = pointtraverse();
}
if (b->verbose) {
printf(" %d duplicated vertices are removed.\n", dupverts);
printf(" %d unused vertices are removed.\n", unuverts);
}
dupverts = 0;
unuverts = 0;
// The following line ensures that dead items in the pool of nodes cannot
// be allocated for the new created nodes. This ensures that the input
// nodes will occur earlier in the output files, and have lower indices.
points->deaditemstack = (void *) NULL;
}
///////////////////////////////////////////////////////////////////////////////
// //
// numberedges() Count the number of edges, save in "meshedges". //
// //
// This routine is called when '-p' or '-r', and '-E' options are used. The //
// total number of edges depends on the genus of the input surface mesh. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::numberedges()
{
triface worktet, spintet;
int firstindex, eindex;
int ishulledge;
int i;
// Determine the first index (0 or 1).
firstindex = b->zeroindex ? 0 : in->firstnumber;
// First indexing all tetrahedra.
tetrahedrons->traversalinit();
eindex = firstindex;
worktet.tet = tetrahedrontraverse();
while (worktet.tet != NULL) {
setelemindex(worktet.tet, eindex);
eindex++;
worktet.tet = tetrahedrontraverse();
}
meshedges = meshhulledges = 0l;
tetrahedrons->traversalinit();
worktet.tet = tetrahedrontraverse();
while (worktet.tet != NULL) {
// Count the number of Voronoi faces. Look at the six edges of this
// tet. Count an edge only if this tet's index is smaller than
// those of other non-hull tets which share this edge.
for (i = 0; i < 6; i++) {
worktet.ver = edge2ver[i];
ishulledge = 0;
fnext(worktet, spintet);
do {
if (!ishulltet(spintet)) {
if (elemindex(spintet.tet) < elemindex(worktet.tet)) break;
} else {
ishulledge = 1;
}
fnextself(spintet);
} while (spintet.tet != worktet.tet);
// Count this edge if no adjacent tets are smaller than this tet.
if (spintet.tet == worktet.tet) {
meshedges++;
if (ishulledge) meshhulledges++;
}
}
worktet.tet = tetrahedrontraverse();
}
}
///////////////////////////////////////////////////////////////////////////////
// //
// outnodes() Output the points to a .node file or a tetgenio structure. //
// //
// Note: each point has already been numbered on input (the first index is //
// 'in->firstnumber'). //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::outnodes(tetgenio* out)
{
FILE *outfile = NULL;
char outnodefilename[FILENAMESIZE];
face parentsh;
point pointloop;
int nextras, bmark, marker = 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");
}
}
nextras = in->numberofpointattributes;
bmark = !b->nobound && in->pointmarkerlist;
if (out == (tetgenio *) NULL) {
outfile = fopen(outnodefilename, "w");
if (outfile == (FILE *) NULL) {
printf("File I/O Error: Cannot create file %s.\n", outnodefilename);
terminatetetgen(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(1);
}
// Allocate space for 'pointattributelist' if necessary;
if (nextras > 0) {
out->pointattributelist = new REAL[points->items * nextras];
if (out->pointattributelist == (REAL *) NULL) {
printf("Error: Out of memory.\n");
terminatetetgen(1);
}
}
// 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(1);
}
}
if (b->psc) {
out->pointparamlist = new tetgenio::pointparam[points->items];
if (out->pointparamlist == NULL) {
printf("Error: Out of memory.\n");
terminatetetgen(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(pointloop) == FREEFACETVERTEX) {
if (in->facetmarkerlist != NULL) {
marker = in->facetmarkerlist[marker - 1];
}
}
}
} // if (pointtype(...))
}
}
if (out == (tetgenio *) NULL) {
// Point number, x, y and z coordinates.
fprintf(outfile, "%4d %.17g %.17g %.17g", pointnumber,
pointloop[0], pointloop[1], pointloop[2]);
for (i = 0; i < nextras; i++) {
// Write an attribute.
fprintf(outfile, " %.17g", pointloop[4 + 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.
out->pointattributelist[attribindex++] = pointloop[4 + i];
}
if (bmark) {
// Output the boundary marker.
out->pointmarkerlist[index] = marker;
}
if (b->psc) {
out->pointparamlist[index].uv[0] = pointgeomuv(pointloop, 0);
out->pointparamlist[index].uv[1] = pointgeomuv(pointloop, 1);
out->pointparamlist[index].tag = pointgeomtag(pointloop);
if (pointtype(pointloop) == RIDGEVERTEX) {
out->pointparamlist[index].type = 0;
} else if (pointtype(pointloop) == ACUTEVERTEX) {
out->pointparamlist[index].type = 0;
} else if (pointtype(pointloop) == FREESEGVERTEX) {
out->pointparamlist[index].type = 1;
} else if (pointtype(pointloop) == FREEFACETVERTEX) {
out->pointparamlist[index].type = 2;
} else if (pointtype(pointloop) == FREEVOLVERTEX) {
out->pointparamlist[index].type = 3;
} else {
out->pointparamlist[index].type = -1; // Unknown type.
}
}
}
pointloop = pointtraverse();
pointnumber++;
index++;
}
if (out == (tetgenio *) NULL) {
fprintf(outfile, "# Generated by %s\n", b->commandline);
fclose(outfile);
}
}
///////////////////////////////////////////////////////////////////////////////
// //
// outmetrics() Output the metric to a file (*.mtr) or a tetgenio obj. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::outmetrics(tetgenio* out)
{
FILE *outfile = NULL;
char outmtrfilename[FILENAMESIZE];
point ptloop;
int mtrindex;
if (out == (tetgenio *) NULL) {
strcpy(outmtrfilename, b->outfilename);
strcat(outmtrfilename, ".mtr");
}
if (!b->quiet) {
if (out == (tetgenio *) NULL) {
printf("Writing %s.\n", outmtrfilename);
} else {
printf("Writing metrics.\n");
}
}
if (out == (tetgenio *) NULL) {
outfile = fopen(outmtrfilename, "w");
if (outfile == (FILE *) NULL) {
printf("File I/O Error: Cannot create file %s.\n", outmtrfilename);
terminatetetgen(3);
}
// Number of points, number of point metrices,
// fprintf(outfile, "%ld %d\n", points->items, sizeoftensor + 3);
fprintf(outfile, "%ld %d\n", points->items, 1);
} else {
// Allocate space for 'pointmtrlist' if necessary;
// out->pointmtrlist = new REAL[points->items * (sizeoftensor + 3)];
out->pointmtrlist = new REAL[points->items];
if (out->pointmtrlist == (REAL *) NULL) {
terminatetetgen(1);
}
out->numberofpointmtrs = 1; // (sizeoftensor + 3);
mtrindex = 0;
}
points->traversalinit();
ptloop = pointtraverse();
while (ptloop != (point) NULL) {
if (out == (tetgenio *) NULL) {
// for (i = 0; i < sizeoftensor; i++) {
// fprintf(outfile, "%-16.8e ", ptloop[pointmtrindex + i]);
// }
fprintf(outfile, "%-16.8e\n", ptloop[pointmtrindex]);
} else {
// for (i = 0; i < sizeoftensor; i++) {
// out->pointmtrlist[mtrindex++] = ptloop[pointmtrindex + i];
// }
out->pointmtrlist[mtrindex++] = ptloop[pointmtrindex];
}
ptloop = pointtraverse();
}
if (out == (tetgenio *) NULL) {
fprintf(outfile, "# Generated by %s\n", b->commandline);
fclose(outfile);
}
}
///////////////////////////////////////////////////////////////////////////////
// //
// outelements() Output the tetrahedra to an .ele file or a tetgenio //
// structure. //
// //
// This routine also indexes all tetrahedra (exclusing hull tets) (from in-> //
// firstnumber). The total number of mesh edges is counted in 'meshedges'. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::outelements(tetgenio* out)
{
FILE *outfile = NULL;
char outelefilename[FILENAMESIZE];
tetrahedron* tptr;
triface worktet, spintet;
point p1, p2, p3, p4;
point *extralist;
REAL *talist = NULL;
int *tlist = NULL;
long ntets;
int firstindex, shift;
int pointindex, attribindex;
int highorderindex = 10; // The reserved pointer.
int elementnumber;
int eextras;
int ishulledge;
int i;
if (out == (tetgenio *) NULL) {
strcpy(outelefilename, b->outfilename);
strcat(outelefilename, ".ele");
}
if (!b->quiet) {
if (out == (tetgenio *) NULL) {
printf("Writing %s.\n", outelefilename);
} else {
printf("Writing elements.\n");
}
}
// The number of tets excluding hull tets.
ntets = tetrahedrons->items - hullsize;
eextras = in->numberoftetrahedronattributes;
if (out == (tetgenio *) NULL) {
outfile = fopen(outelefilename, "w");
if (outfile == (FILE *) NULL) {
printf("File I/O Error: Cannot create file %s.\n", outelefilename);
terminatetetgen(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(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(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 shiftment.
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) {
p1 = (point) tptr[4];
p2 = (point) tptr[5];
p3 = (point) tptr[6];
p4 = (point) tptr[7];
if (out == (tetgenio *) NULL) {
// Tetrahedron number, indices for four points.
fprintf(outfile, "%5d %5d %5d %5d %5d", elementnumber,
pointmark(p1) - shift, pointmark(p2) - shift,
pointmark(p3) - shift, pointmark(p4) - shift);
if (0) { // if (b->order == 2) {
extralist = (point *) tptr[highorderindex];
// Tetrahedron number, indices for four points plus six extra points.
fprintf(outfile, " %5d %5d %5d %5d %5d %5d",
pointmark(extralist[0]) - shift, pointmark(extralist[1]) - shift,
pointmark(extralist[2]) - shift, pointmark(extralist[3]) - shift,
pointmark(extralist[4]) - shift, pointmark(extralist[5]) - shift);
}
for (i = 0; i < eextras; i++) {
fprintf(outfile, " %.17g", elemattribute(tptr, i));
}
fprintf(outfile, "\n");
} else {
tlist[pointindex++] = pointmark(p1) - shift;
tlist[pointindex++] = pointmark(p2) - shift;
tlist[pointindex++] = pointmark(p3) - shift;
tlist[pointindex++] = pointmark(p4) - shift;
if (0) { // if (b->order == 2) {
extralist = (point *) tptr[highorderindex];
tlist[pointindex++] = pointmark(extralist[0]) - shift;
tlist[pointindex++] = pointmark(extralist[1]) - shift;
tlist[pointindex++] = pointmark(extralist[2]) - shift;
tlist[pointindex++] = pointmark(extralist[3]) - shift;
tlist[pointindex++] = pointmark(extralist[4]) - shift;
tlist[pointindex++] = pointmark(extralist[5]) - shift;
}
for (i = 0; i < eextras; i++) {
talist[attribindex++] = elemattribute(tptr, i);
}
}
//if (b->neighout) {
// Remember the index of this element.
setelemindex(tptr, elementnumber);
//}
tptr = tetrahedrontraverse();
elementnumber++;
}
// Count the number of edges (# Voronoi faces).
meshedges = meshhulledges = 0l;
tetrahedrons->traversalinit();
tptr = tetrahedrontraverse();
while (tptr != (tetrahedron *) NULL) {
// Count the number of Voronoi faces. Look at the six edges of this
// tet. Count an edge only if this tet's pointer is smaller than
// those of other non-hull tets which share this edge.
worktet.tet = tptr;
for (i = 0; i < 6; i++) {
worktet.ver = edge2ver[i];
ishulledge = 0;
fnext(worktet, spintet);
do {
if (!ishulltet(spintet)) {
if (elemindex(spintet.tet) < elemindex(worktet.tet)) break;
} else {
ishulledge = 1;
}
fnextself(spintet);
} while (spintet.tet != worktet.tet);
// Count this edge if no adjacent tets are smaller than this tet.
if (spintet.tet == worktet.tet) {
meshedges++;
if (ishulledge) meshhulledges++;
}
}
tptr = tetrahedrontraverse();
}
if (out == (tetgenio *) NULL) {
fprintf(outfile, "# Generated by %s\n", b->commandline);
fclose(outfile);
}
}
///////////////////////////////////////////////////////////////////////////////
// //
// outfaces() Output all faces to a .face file or a tetgenio object. //
// //
// The total number of faces f can be calculated as following: Let t be the //
// total number of tets. Since each tet has 4 faces, the number t * 4 counts //
// each interior face twice and each hull face once. So f = (t * 4 + h) / 2, //
// where h is the total number of hull faces (which is known). //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::outfaces(tetgenio* out)
{
FILE *outfile = NULL;
char facefilename[FILENAMESIZE];
triface tface, tsymface;
face checkmark;
point torg, tdest, tapex;
long ntets, faces;
int *elist = NULL, *emlist = NULL;
int neigh1 = 0, neigh2 = 0;
int faceid, marker = 0;
int firstindex, shift;
int facenumber;
int index;
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;
//bmark = !b->nobound && in->facetmarkerlist;
if (out == (tetgenio *) NULL) {
outfile = fopen(facefilename, "w");
if (outfile == (FILE *) NULL) {
printf("File I/O Error: Cannot create file %s.\n", facefilename);
terminatetetgen(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(1);
}
// 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(1);
}
}
if (b->neighout > 1) {
// '-nn' switch.
out->adjtetlist = new int[faces * 2];
if (out->adjtetlist == (int *) NULL) {
printf("Error: Out of memory.\n");
terminatetetgen(1);
}
}
out->numberoftrifaces = faces;
elist = out->trifacelist;
emlist = out->trifacemarkerlist;
index = 0;
}
// Determine the first index (0 or 1).
firstindex = b->zeroindex ? 0 : in->firstnumber;
shift = 0; // Default no shiftment.
if ((in->firstnumber == 1) && (firstindex == 0)) {
shift = 1; // Shift the output indices by 1.
}
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->nobound) {
// Get the boundary marker of this face.
if (b->plc || b->refine) {
// Shell face is used.
tspivot(tface, checkmark);
if (checkmark.sh == NULL) {
marker = 0; // It is an inner face. It's marker is 0.
} else {
if (in->facetmarkerlist) {
// The facet marker is given, get it.
faceid = shellmark(checkmark) - 1;
marker = in->facetmarkerlist[faceid];
} else {
marker = 1; // The default marker for subface is 1.
}
}
} else {
// Shell face is not used, only distinguish outer and inner face.
marker = (int) ishulltet(tsymface);
}
}
if (b->neighout > 1) {
// '-nn' switch. Output adjacent tets indices.
neigh1 = elemindex(tface.tet);
if (!ishulltet(tsymface)) {
neigh2 = elemindex(tsymface.tet);
} else {
neigh2 = -1;
}
}
if (out == (tetgenio *) NULL) {
// Face number, indices of three vertices.
fprintf(outfile, "%5d %4d %4d %4d", facenumber,
pointmark(torg) - shift, pointmark(tdest) - shift,
pointmark(tapex) - shift);
if (!b->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->nobound) {
emlist[facenumber - in->firstnumber] = marker;
}
if (b->neighout > 1) {
out->adjtetlist[(facenumber - in->firstnumber) * 2] = neigh1;
out->adjtetlist[(facenumber - in->firstnumber) * 2 + 1] = neigh2;
}
}
facenumber++;
}
}
tface.tet = tetrahedrontraverse();
}
if (out == (tetgenio *) NULL) {
fprintf(outfile, "# Generated by %s\n", b->commandline);
fclose(outfile);
}
}
///////////////////////////////////////////////////////////////////////////////
// //
// outhullfaces() Output hull faces to a .face file or a tetgenio object. //
// //
// The normal of each face is pointing to the outside of the domain. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::outhullfaces(tetgenio* out)
{
FILE *outfile = NULL;
char facefilename[FILENAMESIZE];
triface hulltet;
point torg, tdest, tapex;
int *elist = NULL;
int firstindex, shift;
int facenumber;
int index;
if (out == (tetgenio *) NULL) {
strcpy(facefilename, b->outfilename);
strcat(facefilename, ".face");
}
if (!b->quiet) {
if (out == (tetgenio *) NULL) {
printf("Writing %s.\n", facefilename);
} else {
printf("Writing faces.\n");
}
}
if (out == (tetgenio *) NULL) {
outfile = fopen(facefilename, "w");
if (outfile == (FILE *) NULL) {
printf("File I/O Error: Cannot create file %s.\n", facefilename);
terminatetetgen(1);
}
fprintf(outfile, "%ld 0\n", hullsize);
} else {
// Allocate memory for 'trifacelist'.
out->trifacelist = new int[hullsize * 3];
if (out->trifacelist == (int *) NULL) {
printf("Error: Out of memory.\n");
terminatetetgen(1);
}
out->numberoftrifaces = hullsize;
elist = out->trifacelist;
index = 0;
}
// Determine the first index (0 or 1).
firstindex = b->zeroindex ? 0 : in->firstnumber;
shift = 0; // Default no shiftment.
if ((in->firstnumber == 1) && (firstindex == 0)) {
shift = 1; // Shift the output indices by 1.
}
tetrahedrons->traversalinit();
hulltet.tet = alltetrahedrontraverse();
facenumber = firstindex;
while (hulltet.tet != (tetrahedron *) NULL) {
if (ishulltet(hulltet)) {
torg = (point) hulltet.tet[4];
tdest = (point) hulltet.tet[5];
tapex = (point) hulltet.tet[6];
if (out == (tetgenio *) NULL) {
// Face number, indices of three vertices.
fprintf(outfile, "%5d %4d %4d %4d", facenumber,
pointmark(torg) - shift, pointmark(tdest) - shift,
pointmark(tapex) - shift);
fprintf(outfile, "\n");
} else {
// Output indices of three vertices.
elist[index++] = pointmark(torg) - shift;
elist[index++] = pointmark(tdest) - shift;
elist[index++] = pointmark(tapex) - shift;
}
facenumber++;
}
hulltet.tet = alltetrahedrontraverse();
}
if (out == (tetgenio *) NULL) {
fprintf(outfile, "# Generated by %s\n", b->commandline);
fclose(outfile);
}
}
///////////////////////////////////////////////////////////////////////////////
// //
// outsubfaces() Output subfaces (i.e. boundary faces) to a .face file or //
// a tetgenio structure. //
// //
// The boundary faces are found in 'subfaces'. For listing triangle vertices //
// in the same sense for all triangles in the mesh, the direction determined //
// by right-hand rule is pointer to the inside of the volume. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::outsubfaces(tetgenio* out)
{
FILE *outfile = NULL;
char facefilename[FILENAMESIZE];
int *elist = NULL;
int *emlist = NULL;
int index = 0, index1 = 0, index2 = 0;
triface abuttingtet;
face faceloop;
point torg, tdest, tapex;
int faceid = 0, marker = 0;
int firstindex, shift;
int neigh1 = 0, neigh2 = 0;
int facenumber;
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");
}
}
//bmark = !b->nobound && in->facetmarkerlist;
if (out == (tetgenio *) NULL) {
outfile = fopen(facefilename, "w");
if (outfile == (FILE *) NULL) {
printf("File I/O Error: Cannot create file %s.\n", facefilename);
terminatetetgen(3);
}
// Number of subfaces.
fprintf(outfile, "%ld %d\n", subfaces->items, !b->nobound);
} else {
// Allocate memory for 'trifacelist'.
out->trifacelist = new int[subfaces->items * 3];
if (out->trifacelist == (int *) NULL) {
terminatetetgen(1);
}
if (!b->nobound) {
// Allocate memory for 'trifacemarkerlist'.
out->trifacemarkerlist = new int[subfaces->items];
if (out->trifacemarkerlist == (int *) NULL) {
terminatetetgen(1);
}
}
if (b->neighout > 1) {
// '-nn' switch.
out->adjtetlist = new int[subfaces->items * 2];
if (out->adjtetlist == (int *) NULL) {
terminatetetgen(1);
}
}
out->numberoftrifaces = subfaces->items;
elist = out->trifacelist;
emlist = out->trifacemarkerlist;
}
// 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 (abuttingtet.tet != NULL) {
// If there is a tetrahedron containing this subface, orient it so
// that the normal of this face points to inside of the volume by
// right-hand rule.
torg = org(abuttingtet);
tdest = dest(abuttingtet);
tapex = apex(abuttingtet);
} else {
// This may happen when only a surface mesh be generated.
torg = sorg(faceloop);
tdest = sdest(faceloop);
tapex = sapex(faceloop);
}
if (!b->nobound) {
if (in->facetmarkerlist) {
faceid = shellmark(faceloop) - 1;
marker = in->facetmarkerlist[faceid];
} else {
marker = 1; // Default marker for a subface is 1.
}
}
if (b->neighout > 1) {
// '-nn' switch. Output adjacent tets indices.
neigh1 = -1;
neigh2 = -1;
stpivot(faceloop, abuttingtet);
if (abuttingtet.tet != NULL) {
neigh1 = elemindex(abuttingtet.tet);
fsymself(abuttingtet);
if (!ishulltet(abuttingtet)) {
neigh2 = elemindex(abuttingtet.tet);
}
}
}
if (out == (tetgenio *) NULL) {
fprintf(outfile, "%5d %4d %4d %4d", facenumber,
pointmark(torg) - shift, pointmark(tdest) - shift,
pointmark(tapex) - shift);
if (!b->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->nobound) {
emlist[index1++] = marker;
}
if (b->neighout > 1) {
out->adjtetlist[index2++] = neigh1;
out->adjtetlist[index2++] = neigh2;
}
}
facenumber++;
faceloop.sh = shellfacetraverse(subfaces);
}
if (out == (tetgenio *) NULL) {
fprintf(outfile, "# Generated by %s\n", b->commandline);
fclose(outfile);
}
}
///////////////////////////////////////////////////////////////////////////////
// //
// outedges() Output all edges to a .edge file or a tetgenio object. //
// //
// Note: This routine must be called after outelements(), so that the total //
// number of edges 'meshedges' has been counted. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::outedges(tetgenio* out)
{
FILE *outfile = NULL;
char edgefilename[FILENAMESIZE];
triface tetloop, worktet, spintet;
face checkseg;
point torg, tdest;
int *elist = NULL, *emlist = NULL;
int ishulledge;
int firstindex, shift;
int edgenumber, marker;
int index, index1;
int i;
if (out == (tetgenio *) NULL) {
strcpy(edgefilename, b->outfilename);
strcat(edgefilename, ".edge");
}
if (!b->quiet) {
if (out == (tetgenio *) NULL) {
printf("Writing %s.\n", edgefilename);
} else {
printf("Writing edges.\n");
}
}
if (out == (tetgenio *) NULL) {
outfile = fopen(edgefilename, "w");
if (outfile == (FILE *) NULL) {
printf("File I/O Error: Cannot create file %s.\n", edgefilename);
terminatetetgen(1);
}
// Write the number of edges, boundary markers (0 or 1).
fprintf(outfile, "%ld %d\n", meshedges, !b->nobound);
} else {
// Allocate memory for 'edgelist'.
out->edgelist = new int[meshedges * 2];
if (out->edgelist == (int *) NULL) {
printf("Error: Out of memory.\n");
terminatetetgen(1);
}
if (!b->nobound) {
out->edgemarkerlist = new int[meshedges];
}
out->numberofedges = meshedges;
elist = out->edgelist;
emlist = out->edgemarkerlist;
index = 0;
index1 = 0; // if (!b->nobound)
}
// Determine the first index (0 or 1).
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. Look at the six edges of this
// tet. Count an edge only if this tet's pointer is smaller than
// those of other non-hull tets which share this edge.
worktet.tet = tetloop.tet;
for (i = 0; i < 6; i++) {
worktet.ver = edge2ver[i];
ishulledge = 0;
fnext(worktet, spintet);
do {
if (!ishulltet(spintet)) {
if (elemindex(spintet.tet) < elemindex(worktet.tet)) break;
} else {
ishulledge = 1;
}
fnextself(spintet);
} while (spintet.tet != worktet.tet);
// Count this edge if no adjacent tets are smaller than this tet.
if (spintet.tet == worktet.tet) {
torg = org(worktet);
tdest = dest(worktet);
if (out == (tetgenio *) NULL) {
fprintf(outfile, "%5d %4d %4d", edgenumber,
pointmark(torg) - shift, pointmark(tdest) - shift);
} else {
// Output three vertices of this face;
elist[index++] = pointmark(torg) - shift;
elist[index++] = pointmark(tdest) - shift;
}
if (!b->nobound) {
if (b->plc || b->refine) {
// Check if the edge is a segment.
tsspivot1(worktet, checkseg);
if (checkseg.sh != NULL) {
marker = shellmark(checkseg);
if (marker == 0) { // Does it have no marker?
marker = 1; // Set the default marker for this segment.
}
} else {
marker = 0; // It's not a segment.
}
} else {
// Mark it if it is a hull edge.
marker = ishulledge ? 1 : 0;
}
if (out == (tetgenio *) NULL) {
fprintf(outfile, " %d", marker);
} else {
emlist[index1++] = marker;
}
}
if (out == (tetgenio *) NULL) {
fprintf(outfile, "\n");
}
edgenumber++;
}
}
tetloop.tet = tetrahedrontraverse();
}
if (out == (tetgenio *) NULL) {
fprintf(outfile, "# Generated by %s\n", b->commandline);
fclose(outfile);
}
}
///////////////////////////////////////////////////////////////////////////////
// //
// outsubsegments() Output segments to a .edge file or a structure. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::outsubsegments(tetgenio* out)
{
FILE *outfile = NULL;
char edgefilename[FILENAMESIZE];
int *elist = NULL;
int index, i;
face edgeloop;
point torg, tdest;
int firstindex, shift;
int marker;
int edgenumber;
if (out == (tetgenio *) NULL) {
strcpy(edgefilename, b->outfilename);
strcat(edgefilename, ".edge");
}
if (!b->quiet) {
if (out == (tetgenio *) NULL) {
printf("Writing %s.\n", edgefilename);
} else {
printf("Writing edges.\n");
}
}
if (out == (tetgenio *) NULL) {
outfile = fopen(edgefilename, "w");
if (outfile == (FILE *) NULL) {
printf("File I/O Error: Cannot create file %s.\n", edgefilename);
terminatetetgen(3);
}
// Number of subsegments.
fprintf(outfile, "%ld 1\n", subsegs->items);
} else {
// Allocate memory for 'edgelist'.
out->edgelist = new int[subsegs->items * 2];
if (out->edgelist == (int *) NULL) {
terminatetetgen(1);
}
out->edgemarkerlist = new int[subsegs->items];
if (out->edgemarkerlist == (int *) NULL) {
terminatetetgen(1);
}
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);
marker = shellmark(edgeloop);
if (marker == 0) {
marker = 1; // Default marker of a boundary edge is 1.
}
if (out == (tetgenio *) NULL) {
fprintf(outfile, "%5d %4d %4d %d\n", edgenumber,
pointmark(torg) - shift, pointmark(tdest) - shift, marker);
} else {
// Output three vertices of this face;
elist[index++] = pointmark(torg) - shift;
elist[index++] = pointmark(tdest) - shift;
out->edgemarkerlist[i++] = marker;
}
edgenumber++;
edgeloop.sh = shellfacetraverse(subsegs);
}
if (out == (tetgenio *) NULL) {
fprintf(outfile, "# Generated by %s\n", b->commandline);
fclose(outfile);
}
}
///////////////////////////////////////////////////////////////////////////////
// //
// outneighbors() Output tet neighbors to a .neigh file or a structure. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::outneighbors(tetgenio* out)
{
FILE *outfile = NULL;
char neighborfilename[FILENAMESIZE];
int *nlist = NULL;
int index = 0;
triface tetloop, tetsym;
int neighbori[4];
int firstindex;
int elementnumber;
long ntets;
if (out == (tetgenio *) NULL) {
strcpy(neighborfilename, b->outfilename);
strcat(neighborfilename, ".neigh");
}
if (!b->quiet) {
if (out == (tetgenio *) NULL) {
printf("Writing %s.\n", neighborfilename);
} else {
printf("Writing neighbors.\n");
}
}
ntets = tetrahedrons->items - hullsize;
if (out == (tetgenio *) NULL) {
outfile = fopen(neighborfilename, "w");
if (outfile == (FILE *) NULL) {
printf("File I/O Error: Cannot create file %s.\n", neighborfilename);
terminatetetgen(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(1);
}
nlist = out->neighborlist;
}
// Determine the first index (0 or 1).
firstindex = b->zeroindex ? 0 : in->firstnumber;
tetrahedrons->traversalinit();
tetloop.tet = tetrahedrontraverse();
elementnumber = firstindex; // in->firstnumber;
while (tetloop.tet != (tetrahedron *) NULL) {
for (tetloop.ver = 0; tetloop.ver < 4; tetloop.ver++) {
fsym(tetloop, tetsym);
if (!ishulltet(tetsym)) {
neighbori[tetloop.ver] = elemindex(tetsym.tet);
} else {
neighbori[tetloop.ver] = -1;
}
}
if (out == (tetgenio *) NULL) {
// Tetrahedra number, neighboring tetrahedron numbers.
fprintf(outfile, "%4d %4d %4d %4d %4d\n", elementnumber,
neighbori[0], neighbori[1], neighbori[2], neighbori[3]);
} else {
nlist[index++] = neighbori[0];
nlist[index++] = neighbori[1];
nlist[index++] = neighbori[2];
nlist[index++] = neighbori[3];
}
tetloop.tet = tetrahedrontraverse();
elementnumber++;
}
if (out == (tetgenio *) NULL) {
fprintf(outfile, "# Generated by %s\n", b->commandline);
fclose(outfile);
}
}
///////////////////////////////////////////////////////////////////////////////
// //
// outvoronoi() Output the Voronoi diagram to .v.node, .v.edge, v.face, //
// and .v.cell. //
// //
// The Voronoi diagram is the geometric dual of the Delaunay triangulation. //
// The Voronoi vertices are the circumcenters of Delaunay tetrahedra. Each //
// Voronoi edge connects two Voronoi vertices at two sides of a common Dela- //
// unay face. At a face of convex hull, it becomes a ray (goto the infinity).//
// A Voronoi face is the convex hull of all Voronoi vertices around a common //
// Delaunay edge. It is a closed polygon for any interal Delaunay edge. At a //
// ridge, it is unbounded. Each Voronoi cell is the convex hull of all Vor- //
// onoi vertices around a common Delaunay vertex. It is a polytope for any //
// internal Delaunay vertex. It is an unbounded polyhedron for a Delaunay //
// vertex belonging to the convex hull. //
// //
// Comment: Special thanks to Victor Liu for finding and fixing few bugs. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::outvoronoi(tetgenio* out)
{
FILE *outfile = NULL;
char outfilename[FILENAMESIZE];
tetgenio::voroedge *vedge = NULL;
tetgenio::vorofacet *vfacet;
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;
// 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);
// Each face and edge of the tetrahedral mesh will be indexed for indexing
// the Voronoi edges and facets. Indices of faces and edges are saved in
// each tetrahedron (including hull tets).
// Allocate the total space once.
indexarray = new int[tetrahedrons->items * 10];
// Allocate space (10 integers) into each tetrahedron. It re-uses the slot
// for element markers, flags.
i = 0;
tetrahedrons->traversalinit();
tetloop.tet = alltetrahedrontraverse();
while (tetloop.tet != NULL) {
tetloop.tet[11] = (tetrahedron) &(indexarray[i * 10]);
i++;
tetloop.tet = alltetrahedrontraverse();
}
// The number of tetrahedra (excluding hull tets) (Voronoi vertices).
ntets = tetrahedrons->items - hullsize;
// The number of Delaunay faces (Voronoi edges).
faces = (4l * ntets + hullsize) / 2l;
// The number of Delaunay edges (Voronoi faces).
// edges = points->items + faces - ntets - 1;
edges = meshedges; // Counted in outelements() or numberedges();
if (out == (tetgenio *) NULL) {
outfile = fopen(outfilename, "w");
if (outfile == (FILE *) NULL) {
printf("File I/O Error: Cannot create file %s.\n", outfilename);
terminatetetgen(3);
}
// Number of voronoi points, 3 dim, no attributes, no marker.
fprintf(outfile, "%ld 3 0 0\n", ntets);
} else {
// Allocate space for 'vpointlist'.
out->numberofvpoints = (int) ntets;
out->vpointlist = new REAL[out->numberofvpoints * 3];
if (out->vpointlist == (REAL *) NULL) {
terminatetetgen(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));
}
circumsphere(pt[0], pt[1], pt[2], pt[3], ccent, NULL);
if (out == (tetgenio *) NULL) {
fprintf(outfile, "%4d %16.8e %16.8e %16.8e\n", vpointcount + shift,
ccent[0], ccent[1], ccent[2]);
} else {
out->vpointlist[index++] = ccent[0];
out->vpointlist[index++] = ccent[1];
out->vpointlist[index++] = ccent[2];
}
setelemindex(tetloop.tet, vpointcount);
vpointcount++;
tetloop.tet = tetrahedrontraverse();
}
if (out == (tetgenio *) NULL) {
fprintf(outfile, "# Generated by %s\n", b->commandline);
fclose(outfile);
}
// Output Voronoi edges to .v.edge file.
if (out == (tetgenio *) NULL) {
strcpy(outfilename, b->outfilename);
strcat(outfilename, ".v.edge");
}
if (!b->quiet) {
if (out == (tetgenio *) NULL) {
printf("Writing %s.\n", outfilename);
} else {
printf("Writing Voronoi edges.\n");
}
}
if (out == (tetgenio *) NULL) {
outfile = fopen(outfilename, "w");
if (outfile == (FILE *) NULL) {
printf("File I/O Error: Cannot create file %s.\n", outfilename);
terminatetetgen(3);
}
// Number of Voronoi edges, no marker.
fprintf(outfile, "%ld 0\n", faces);
} else {
// Allocate space for 'vpointlist'.
out->numberofvedges = (int) faces;
out->vedgelist = new tetgenio::voroedge[out->numberofvedges];
}
// Output the Voronoi edges.
tetrahedrons->traversalinit();
tetloop.tet = tetrahedrontraverse();
vedgecount = 0; // D-Face (V-edge) index (from zero).
index = 0; // The Delaunay-face index.
while (tetloop.tet != (tetrahedron *) NULL) {
// Count the number of Voronoi edges. Look at the four faces of each
// tetrahedron. Count the face if the tetrahedron's index is
// smaller than its neighbor's or the neighbor is outside.
end1 = elemindex(tetloop.tet);
for (tetloop.ver = 0; tetloop.ver < 4; tetloop.ver++) {
fsym(tetloop, worktet);
if (ishulltet(worktet) ||
(elemindex(tetloop.tet) < elemindex(worktet.tet))) {
// Found a Voronoi edge. Operate on it.
if (out == (tetgenio *) NULL) {
fprintf(outfile, "%4d %4d", vedgecount + shift, end1 + shift);
} else {
vedge = &(out->vedgelist[index++]);
vedge->v1 = end1 + shift;
}
if (!ishulltet(worktet)) {
end2 = elemindex(worktet.tet);
} else {
end2 = -1;
}
// Note that end2 may be -1 (worktet.tet is outside).
if (end2 == -1) {
// Calculate the out normal of this hull face.
pt[0] = dest(worktet);
pt[1] = org(worktet);
pt[2] = apex(worktet);
for (j = 0; j < 3; j++) vec1[j] = pt[1][j] - pt[0][j];
for (j = 0; j < 3; j++) vec2[j] = pt[2][j] - pt[0][j];
cross(vec1, vec2, infvec);
// Normalize it.
L = sqrt(infvec[0] * infvec[0] + infvec[1] * infvec[1]
+ infvec[2] * infvec[2]);
if (L > 0) for (j = 0; j < 3; j++) infvec[j] /= L;
if (out == (tetgenio *) NULL) {
fprintf(outfile, " -1");
fprintf(outfile, " %g %g %g\n", infvec[0], infvec[1], infvec[2]);
} else {
vedge->v2 = -1;
vedge->vnormal[0] = infvec[0];
vedge->vnormal[1] = infvec[1];
vedge->vnormal[2] = infvec[2];
}
} else {
if (out == (tetgenio *) NULL) {
fprintf(outfile, " %4d\n", end2 + shift);
} else {
vedge->v2 = end2 + shift;
vedge->vnormal[0] = 0.0;
vedge->vnormal[1] = 0.0;
vedge->vnormal[2] = 0.0;
}
}
// Save the V-edge index in this tet and its neighbor.
fidxs = (int *) (tetloop.tet[11]);
fidxs[tetloop.ver] = vedgecount;
fidxs = (int *) (worktet.tet[11]);
fidxs[worktet.ver & 3] = vedgecount;
vedgecount++;
}
} // tetloop.ver
tetloop.tet = tetrahedrontraverse();
}
if (out == (tetgenio *) NULL) {
fprintf(outfile, "# Generated by %s\n", b->commandline);
fclose(outfile);
}
// Output Voronoi faces to .v.face file.
if (out == (tetgenio *) NULL) {
strcpy(outfilename, b->outfilename);
strcat(outfilename, ".v.face");
}
if (!b->quiet) {
if (out == (tetgenio *) NULL) {
printf("Writing %s.\n", outfilename);
} else {
printf("Writing Voronoi faces.\n");
}
}
if (out == (tetgenio *) NULL) {
outfile = fopen(outfilename, "w");
if (outfile == (FILE *) NULL) {
printf("File I/O Error: Cannot create file %s.\n", outfilename);
terminatetetgen(3);
}
// Number of Voronoi faces.
fprintf(outfile, "%ld 0\n", edges);
} else {
out->numberofvfacets = edges;
out->vfacetlist = new tetgenio::vorofacet[out->numberofvfacets];
if (out->vfacetlist == (tetgenio::vorofacet *) NULL) {
terminatetetgen(1);
}
}
// 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(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(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)) {
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");
}
// DEBUG BEGIN
for (i = 0; i < ptlist->objects; i++) {
neipt = * (point *) fastlookup(ptlist, i);
if (neipt != dummypoint) {
assert(!pinfected(neipt));
}
}
// DEBUG END
tetlist->restart();
ptlist->restart();
vpointcount++;
}
ploop = pointtraverse();
}
// Delete the space for face/edge indices.
delete [] indexarray;
if (out == (tetgenio *) NULL) {
fprintf(outfile, "# Generated by %s\n", b->commandline);
fclose(outfile);
}
}
///////////////////////////////////////////////////////////////////////////////
// //
// outsmesh() Write surface mesh to a .smesh file, which can be read and //
// tetrahedralized by TetGen. //
// //
// You can specify a filename (without suffix) in 'smfilename'. If you don't //
// supply a filename (let smfilename be NULL), the default name stored in //
// 'tetgenbehavior' will be used. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::outsmesh(char* smfilename)
{
FILE *outfile;
char nodfilename[FILENAMESIZE];
char smefilename[FILENAMESIZE];
face faceloop;
point p1, p2, p3;
int firstindex, shift;
int bmark;
int faceid, marker;
int i;
if (smfilename != (char *) NULL && smfilename[0] != '\0') {
strcpy(smefilename, smfilename);
} else if (b->outfilename[0] != '\0') {
strcpy(smefilename, b->outfilename);
} else {
strcpy(smefilename, "unnamed");
}
strcpy(nodfilename, smefilename);
strcat(smefilename, ".smesh");
strcat(nodfilename, ".node");
if (!b->quiet) {
printf("Writing %s.\n", smefilename);
}
outfile = fopen(smefilename, "w");
if (outfile == (FILE *) NULL) {
printf("File I/O Error: Cannot create file %s.\n", smefilename);
return;
}
// Determine the first index (0 or 1).
firstindex = b->zeroindex ? 0 : in->firstnumber;
shift = 0; // Default no shiftment.
if ((in->firstnumber == 1) && (firstindex == 0)) {
shift = 1; // Shift the output indices by 1.
}
fprintf(outfile, "# %s. TetGen's input file.\n", smefilename);
fprintf(outfile, "\n# part 1: node list.\n");
fprintf(outfile, "0 3 0 0 # nodes are found in %s.\n", nodfilename);
marker = 0; // avoid compile warning.
bmark = !b->nobound && in->facetmarkerlist;
fprintf(outfile, "\n# part 2: facet list.\n");
// Number of facets, boundary marker.
fprintf(outfile, "%ld %d\n", subfaces->items, bmark);
subfaces->traversalinit();
faceloop.sh = shellfacetraverse(subfaces);
while (faceloop.sh != (shellface *) NULL) {
p1 = sorg(faceloop);
p2 = sdest(faceloop);
p3 = sapex(faceloop);
if (bmark) {
faceid = shellmark(faceloop) - 1;
if (faceid >= 0) {
marker = in->facetmarkerlist[faceid];
} else {
marker = 0; // This subface must be added manually later.
}
}
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);
}
///////////////////////////////////////////////////////////////////////////////
// //
// outmesh2medit() Write mesh to a .mesh file, which can be read and //
// rendered by Medit (a free mesh viewer from INRIA). //
// //
// You can specify a filename (without suffix) in 'mfilename'. If you don't //
// supply a filename (let mfilename be NULL), the default name stored in //
// 'tetgenbehavior' will be used. The output file will have the suffix .mesh.//
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::outmesh2medit(char* mfilename)
{
FILE *outfile;
char mefilename[FILENAMESIZE];
tetrahedron* tetptr;
triface tface, tsymface;
face segloop, checkmark;
point ptloop, p1, p2, p3, p4;
long ntets, faces;
int pointnumber;
int faceid, marker;
int i;
if (mfilename != (char *) NULL && mfilename[0] != '\0') {
strcpy(mefilename, mfilename);
} else if (b->outfilename[0] != '\0') {
strcpy(mefilename, b->outfilename);
} else {
strcpy(mefilename, "unnamed");
}
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]);
} else {
fprintf(outfile, " 0\n");
}
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();
while (tface.tet != (tetrahedron *) NULL) {
for (tface.ver = 0; tface.ver < 4; tface.ver ++) {
fsym(tface, tsymface);
if (ishulltet(tsymface) ||
(elemindex(tface.tet) < elemindex(tsymface.tet))) {
p1 = org (tface);
p2 = dest(tface);
p3 = apex(tface);
fprintf(outfile, "%5d %5d %5d",
pointmark(p1), pointmark(p2), pointmark(p3));
// Check if it is a subface.
tspivot(tface, checkmark);
if (checkmark.sh == NULL) {
marker = 0; // It is an inner face. It's marker is 0.
} else {
if (in->facetmarkerlist) {
// The facet marker is given, get it.
faceid = shellmark(checkmark) - 1;
marker = in->facetmarkerlist[faceid];
} else {
marker = 1; // The default marker for subface is 1.
}
}
fprintf(outfile, " %d\n", marker);
}
}
tface.tet = tetrahedrontraverse();
}
fprintf(outfile, "\n# Set of Tetrahedra\n");
fprintf(outfile, "Tetrahedra\n");
fprintf(outfile, "%ld\n", ntets);
tetrahedrons->traversalinit();
tetptr = tetrahedrontraverse();
while (tetptr != (tetrahedron *) NULL) {
p1 = (point) tetptr[4];
p2 = (point) tetptr[5];
p3 = (point) tetptr[6];
p4 = (point) tetptr[7];
fprintf(outfile, "%5d %5d %5d %5d",
pointmark(p1), pointmark(p2), pointmark(p3), pointmark(p4));
if (in->numberoftetrahedronattributes > 0) {
fprintf(outfile, " %.17g", elemattribute(tetptr, 0));
} else {
fprintf(outfile, " 0");
}
fprintf(outfile, "\n");
tetptr = tetrahedrontraverse();
}
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);
}
///////////////////////////////////////////////////////////////////////////////
// //
// outmesh2vtk() Save mesh to file in VTK Legacy format. //
// //
// This function was contributed by Bryn Llyod from ETH, 2007. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::outmesh2vtk(char* ofilename)
{
FILE *outfile;
char vtkfilename[FILENAMESIZE];
point pointloop;
tetrahedron* tptr;
double x, y, z;
int n1, n2, n3, n4;
int nnodes = 4;
int celltype = 10;
int NEL = tetrahedrons->items - 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; 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();
//elementnumber = firstindex; // in->firstnumber;
if (b->order == 2) {
printf(" Write VTK not implemented for order 2 elements \n");
return;
}
while (tptr != (tetrahedron *) NULL) {
point p1 = (point) tptr[4];
point p2 = (point) tptr[5];
point p3 = (point) tptr[6];
point p4 = (point) tptr[7];
n1 = pointmark(p1) - in->firstnumber;
n2 = pointmark(p2) - in->firstnumber;
n3 = pointmark(p3) - in->firstnumber;
n4 = pointmark(p4) - in->firstnumber;
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<NEL; tid++){
fprintf(outfile, "%d\n", celltype);
}
fprintf(outfile, "\n");
fclose(outfile);
}
//// ////
//// ////
//// output_cxx ///////////////////////////////////////////////////////////////
//// main_cxx /////////////////////////////////////////////////////////////////
//// ////
//// ////
///////////////////////////////////////////////////////////////////////////////
// //
// tetrahedralize() The interface for users using TetGen library to //
// generate tetrahedral meshes with all features. //
// //
// The sequence is roughly as follows. Many of these steps can be skipped, //
// depending on the command line switches. //
// //
// - Initialize constants and parse the command line. //
// - Read the vertices from a file and either //
// - tetrahedralize them (no -r), or //
// - read an old mesh from files and reconstruct it (-r). //
// - Insert the boundary segments and facets (-p or -Y). //
// - Read the holes (-p), regional attributes (-pA), and regional volume //
// constraints (-pa). Carve the holes and concavities, and spread the //
// regional attributes and volume constraints. //
// - Enforce the constraints on minimum quality bound (-q) and maximum //
// volume (-a), and a mesh size function (-m). //
// - Optimize the mesh wrt. specified quality measures (-O and -o). //
// - Write the output files and print the statistics. //
// - Check the consistency of the mesh (-C). //
// //
///////////////////////////////////////////////////////////////////////////////
void tetrahedralize(tetgenbehavior *b, tetgenio *in, tetgenio *out,
tetgenio *addin, tetgenio *bgmin)
{
tetgenmesh m;
clock_t tv[17]; // Timing informations (defined in time.h)
tv[0] = clock();
m.b = b;
m.in = in;
if ((bgmin != NULL) &&
((bgmin->numberofpoints > 0) && (bgmin->pointmtrlist != NULL))) {
m.bgm = new tetgenmesh(); // Create an empty background mesh.
m.bgm->b = b;
m.bgm->in = bgmin;
}
#ifdef INEXACT_GEOM_PRED
if (!b->quiet) {
printf("Using inexact geometric predicates.\n");
}
#else
exactinit();
#endif
m.initializepools();
m.transfernodes();
tv[1] = clock();
if (b->refine) {
m.reconstructmesh();
} else { // b->plc
if (!b->diagnose) {
m.incrementaldelaunay(tv[16]);
}
}
tv[2] = clock();
if (!b->quiet) {
if (b->refine) {
printf("Mesh reconstruction seconds: %g\n",
(tv[2] - tv[1]) / (REAL) CLOCKS_PER_SEC);
} else {
if (!b->diagnose) {
printf("Delaunay seconds: %g\n",
(tv[2] - tv[1]) / (REAL) CLOCKS_PER_SEC);
if (b->verbose) {
printf(" Point sorting seconds: %g\n",
(tv[16] - tv[1]) / (REAL) CLOCKS_PER_SEC);
#ifdef WITH_RUNTIME_COUNTERS
printf(" Point location seconds: %g\n",
m.t_ptloc / (REAL) CLOCKS_PER_SEC);
printf(" Point insertion seconds: %g\n",
m.t_ptinsert / (REAL) CLOCKS_PER_SEC);
#endif
}
}
}
}
if (b->plc) {
m.meshsurface();
}
tv[3] = clock();
if (!b->quiet) {
if (b->plc) {
printf("Surface mesh seconds: %g\n",
(tv[3] - tv[2]) / (REAL) CLOCKS_PER_SEC);
}
}
if (b->plc && b->diagnose) { // -d
m.detectinterfaces();
tv[4] = clock();
if (!b->quiet) {
printf("Self-intersection seconds: %g\n",
(tv[4] - tv[3]) / (REAL) CLOCKS_PER_SEC);
}
// Only output when self-intersecting faces exist.
if (m.subfaces->items > 0l) {
m.outnodes(out);
m.outsubfaces(out);
}
return;
}
if (b->plc) {
if (b->nobisect) { // with -Y option
m.recoverboundary(tv[15]);
} else {
m.constraineddelaunay(tv[15]);
}
}
tv[4] = clock();
if (!b->quiet) {
if (b->plc) {
printf("Boundary recovery seconds: %g\n",
(tv[4] - tv[3]) / (REAL) CLOCKS_PER_SEC);
if (b->verbose) {
printf(" Segment recovery seconds: %g\n",
(tv[15] - tv[3]) / (REAL) CLOCKS_PER_SEC);
printf(" Facet recovery seconds: %g\n",
(tv[4] - tv[15]) / (REAL) CLOCKS_PER_SEC);
}
}
}
if (b->plc && !b->convex) {
m.carveholes();
}
tv[5] = clock();
if (!b->quiet) {
if (b->plc && !b->convex) {
printf("Exterior tets removal seconds: %g\n",
(tv[5] - tv[4]) / (REAL) CLOCKS_PER_SEC);
}
}
if (b->plc && b->nobisect) {
m.suppresssteinerpoints();
}
tv[6] = clock();
if (!b->quiet) {
if (b->plc && b->nobisect) {
printf("Steiner suppression seconds: %g\n",
(tv[6] - tv[5]) / (REAL) CLOCKS_PER_SEC);
}
}
if (b->plc && b->nobisect) {
m.recoverdelaunay();
}
tv[7] = clock();
if (!b->quiet) {
if (b->plc && b->nobisect) {
printf("Delaunay recovery seconds: %g\n",
(tv[7] - tv[6]) / (REAL) CLOCKS_PER_SEC);
}
}
if ((b->plc || b->refine) && (b->metric && (m.bgm != NULL))) {
m.bgm->initializepools();
m.bgm->transfernodes();
m.bgm->reconstructmesh();
}
tv[8] = clock();
if (!b->quiet) {
if ((b->plc || b->refine) && (b->metric && (m.bgm != NULL))) {
printf("Background mesh reconstruct seconds: %g\n",
(tv[8] - tv[7]) / (REAL) CLOCKS_PER_SEC);
}
}
if ((b->plc || b->refine) && (b->metric && (m.bgm != NULL))) {
m.interpolatemeshsize();
}
tv[9] = clock();
if (!b->quiet) {
if ((b->plc || b->refine) && (b->metric && (m.bgm != NULL))) {
printf("Size interpolating seconds: %g\n",
(tv[9] - tv[8]) / (REAL) CLOCKS_PER_SEC);
}
}
if ((b->plc || b->refine) && b->insertaddpoints) { // -i
if ((addin != NULL) && (addin->numberofpoints > 0)) {
m.insertconstrainedpoints(addin);
}
}
tv[10] = clock();
if (!b->quiet) {
if ((b->plc || b->refine) && b->insertaddpoints) {
if ((addin != NULL) && (addin->numberofpoints > 0)) {
printf("Constrained points seconds: %g\n",
(tv[10] - tv[9]) / (REAL) CLOCKS_PER_SEC);
}
}
}
tv[11] = clock();
if ((b->plc || b->refine) && b->quality) {
m.delaunayrefinement();
}
tv[12] = clock();
if (!b->quiet) {
if ((b->plc || b->refine) && b->quality) {
printf("Refinement seconds: %g\n",
(tv[12] - tv[11]) / (REAL) CLOCKS_PER_SEC);
}
}
if ((b->plc || b->refine) && (b->optlevel > 0)) {
m.optimizemesh(1);
}
tv[13] = clock();
if (!b->quiet) {
if ((b->plc || b->refine) && (b->optlevel > 0)) {
printf("Optimization seconds: %g\n",
(tv[13] - tv[12]) / (REAL) CLOCKS_PER_SEC);
}
}
if (!b->nojettison && ((m.dupverts > 0) || (m.unuverts > 0)
|| (b->refine && (in->numberofcorners == 10)))) {
m.jettisonnodes();
}
if (!b->quiet) {
printf("\n");
}
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 {
m.outnodes(out);
}
if (b->noelewritten == 1) {
if (!b->quiet) {
printf("NOT writing an .ele file.\n");
}
m.numberedges();
} else {
if (m.tetrahedrons->items > 0l) {
m.outelements(out);
}
}
if (b->nofacewritten) {
if (!b->quiet) {
printf("NOT writing an .face file.\n");
}
} else {
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->edgesout) {
if (b->edgesout > 1) {
m.outedges(out); // -ee, output all mesh edges.
} else {
m.outsubsegments(out); // -e, only output subsegments.
}
}
if ((b->plc || b->refine) && b->metric) { // -m
m.outmetrics(out);
}
if (!out && b->plc &&
((b->object == tetgenbehavior::OFF) ||
(b->object == tetgenbehavior::PLY) ||
(b->object == tetgenbehavior::STL))) {
m.outsmesh(b->outfilename);
}
if (!out && b->meditview) {
m.outmesh2medit(b->outfilename);
}
if (!out && b->vtkview) {
m.outmesh2vtk(b->outfilename);
}
if (b->neighout) {
m.outneighbors(out);
}
if ((!(b->plc || b->refine)) && b->voroout) {
m.outvoronoi(out);
}
tv[14] = clock();
if (!b->quiet) {
printf("\nOutput seconds: %g\n",
(tv[14] - tv[13]) / (REAL) CLOCKS_PER_SEC);
printf("Total running seconds: %g\n",
(tv[14] - tv[0]) / (REAL) CLOCKS_PER_SEC);
}
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();
}
}
#ifndef TETLIBRARY
///////////////////////////////////////////////////////////////////////////////
// //
// main() The entrance for running TetGen from command line. //
// //
///////////////////////////////////////////////////////////////////////////////
int main(int argc, char *argv[])
#else // with TETLIBRARY
///////////////////////////////////////////////////////////////////////////////
// //
// tetrahedralize() The entrance for calling TetGen from another program. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetrahedralize(char *switches, tetgenio *in, tetgenio *out,
tetgenio *addin, tetgenio *bgmin)
#endif // not TETLIBRARY
{
tetgenbehavior b;
#ifndef TETLIBRARY
tetgenio in, addin, bgmin;
if (!b.parse_commandline(argc, argv)) {
terminatetetgen(10);
}
// Read input files.
if (b.refine) { // -r
if (!in.load_tetmesh(b.infilename, (int) b.object)) {
terminatetetgen(10);
}
} else { // -p
if (!in.load_plc(b.infilename, (int) b.object)) {
terminatetetgen(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);
}
tetrahedralize(&b, &in, NULL, &addin, &bgmin);
return 0;
#else // with TETLIBRARY
if (!b.parse_commandline(switches)) {
terminatetetgen(10);
}
tetrahedralize(&b, in, out, addin, bgmin);
#endif // not TETLIBRARY
}
//// ////
//// ////
//// main_cxx /////////////////////////////////////////////////////////////////