//============================================================================// // // // TetGen // // // // A Quality Tetrahedral Mesh Generator and A 3D Delaunay Triangulator // // // // Version 1.6.0 // // August 31, 2020 // // // // Copyright (C) 2002--2020 // // // // Hang Si // // Research Group: Numerical Mathematics and Scientific Computing // // Weierstrass Institute for Applied Analysis and Stochastics (WIAS) // // Mohrenstr. 39, 10117 Berlin, Germany // // si@wias-berlin.de // // // // TetGen is a tetrahedral mesh generator. It creates 3d triangulations of // // polyhedral domains. It generates meshes with well-shaped elements whose // // sizes are adapted to the geometric features or user-provided sizing // // functions. It has applications in various applications in scientific // // computing, such as computer graphics (CG), computer-aided design (CAD), // // geometry processing (parametrizations and computer animation), and // // physical simulations (finite element analysis). // // // // TetGen computes (weighted) Delaunay triangulations for three-dimensional // // (weighted) point sets, and constrained Delaunay triangulations for // // three-dimensional polyhedral domains. In the latter case, input edges // // and triangles can be completely preserved in the output meshes. TetGen // // can refine or coarsen an existing mesh to result in good quality and // // size-adapted mesh according to the geometric features and user-defined // // mesh sizing functions. // // // // TetGen implements theoretically proven algorithms for computing the // // Delaunay and constrained Delaunay tetrahedralizations. TetGen achieves // // robustness and efficiency by using advanced techniques in computational // // geometry. A technical paper describes the algorithms and methods // // implemented in TetGen is available in ACM-TOMS, Hang Si ``TetGen, a // // Delaunay-Based Quality Tetrahedral Mesh Generator", ACM Transactions on // // Mathematical Software, February 2015, https://doi.org/10.1145/2629697. // // // // 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. // // // //============================================================================// #ifndef tetgenH #define tetgenH // To compile TetGen as a library instead of an executable program, define // the TETLIBRARY symbol. #define TETLIBRARY // TetGen default uses the double-precision (64 bit) for a real number. // Alternatively, one can use the single-precision (32 bit) 'float' if the // memory is limited. #define REAL double // #define REAL float // The maximum number of characters in a file name (including the null). #define FILENAMESIZE 1024 // The maximum number of chars in a line read from a file (including the null). #define INPUTLINESIZE 2048 // C standard libraries to perform Input/output operations, general utililities, // manipulate strings and arrays, compute common mathematical operations, // get date and time information. #include #include #include #include #include // The types 'intptr_t' and 'uintptr_t' are signed and unsigned integer types, // respectively. They are guaranteed to be the same width as a pointer. // They are defined in by the C99 Standard. #include //============================================================================// // // // tetgenio // // // // A structure for transferring input/output data between the user and // // TetGen's internal data structure (class tetgenmesh). // // // // This data structure contains a collection of arrays, i.e., points, facets, // // tetrahedra. It contains functions to read input data from files (.node, // // .poly, .face, .edge, .ele) as well as write output data into files. // // // // Once an object of tetgenio is declared, no array is created. One has to // // allocate enough memory for them. On the deletion of this object, the // // memory occupied by these arrays needs to be freed. The routine // // deinitialize() will be automatically called. It frees the memory for // // an array if it is not a NULL. Note that it assumes that the memory is // // allocated by the C++ "new" operator. Otherwise, the user is responsible // // to free them and all pointers must be NULL. // // // //============================================================================// class tetgenio { public: // A "polygon" describes a simple polygon (no holes). It is not necessarily // convex. Each polygon contains a number of corners (points) and the same // number of sides (edges). The points of the polygon must be given in // either counterclockwise or clockwise order and they form a ring, so // every two consecutive points forms an edge of the polygon. typedef struct { int *vertexlist; int numberofvertices; } polygon; // A "facet" describes a polygonal region possibly with holes, edges, and // points floating in it. Each facet consists of a list of polygons and // a list of hole points (which lie strictly inside holes). typedef struct { polygon *polygonlist; int numberofpolygons; REAL *holelist; int numberofholes; } facet; // A "voroedge" is an edge of the Voronoi diagram. It corresponds to a // Delaunay face. Each voroedge is either a line segment connecting // two Voronoi vertices or a ray starting from a Voronoi vertex to an // "infinite vertex". 'v1' and 'v2' are two indices pointing to the // list of Voronoi vertices. 'v1' must be non-negative, while 'v2' may // be -1 if it is a ray, in this case, the unit normal of this ray is // given in 'vnormal'. typedef struct { int v1, v2; REAL vnormal[3]; } voroedge; // A "vorofacet" is an facet of the Voronoi diagram. It corresponds to a // Delaunay edge. Each Voronoi facet is a convex polygon formed by a // list of Voronoi edges, it may not be closed. 'c1' and 'c2' are two // indices pointing into the list of Voronoi cells, i.e., the two cells // share this facet. 'elist' is an array of indices pointing into the // list of Voronoi edges, 'elist[0]' saves the number of Voronoi edges // (including rays) of this facet. typedef struct { int c1, c2; int *elist; } vorofacet; // Additional parameters associated with an input (or mesh) vertex. // These informations are provided by CAD libraries. typedef struct { REAL uv[2]; int tag; int type; // 0, 1, or 2. } pointparam; // Callback functions for meshing PSCs. typedef REAL (* GetVertexParamOnEdge)(void*, int, int); typedef void (* GetSteinerOnEdge)(void*, int, REAL, REAL*); typedef void (* GetVertexParamOnFace)(void*, int, int, REAL*); typedef void (* GetEdgeSteinerParamOnFace)(void*, int, REAL, int, REAL*); typedef void (* GetSteinerOnFace)(void*, int, REAL*, REAL*); // A callback function for mesh refinement. typedef bool (* TetSizeFunc)(REAL*, REAL*, REAL*, REAL*, REAL*, REAL); // Items are numbered starting from 'firstnumber' (0 or 1), default is 0. int firstnumber; // Dimension of the mesh (2 or 3), default is 3. int mesh_dim; // Does the lines in .node file contain index or not, default is 1. int useindex; // 'pointlist': An array of point coordinates. The first point's x // coordinate is at index [0] and its y coordinate at index [1], its // z coordinate is at index [2], followed by the coordinates of the // remaining points. Each point occupies three REALs. // 'pointattributelist': An array of point attributes. Each point's // attributes occupy 'numberofpointattributes' REALs. // 'pointmtrlist': An array of metric tensors at points. Each point's // tensor occupies 'numberofpointmtr' REALs. // 'pointmarkerlist': An array of point markers; one integer per point. // 'point2tetlist': An array of tetrahedra indices; one integer per point. REAL *pointlist; REAL *pointattributelist; REAL *pointmtrlist; int *pointmarkerlist; int *point2tetlist; pointparam *pointparamlist; int numberofpoints; int numberofpointattributes; int numberofpointmtrs; // 'tetrahedronlist': An array of tetrahedron corners. The first // tetrahedron's first corner is at index [0], followed by its other // corners, followed by six nodes on the edges of the tetrahedron if the // second order option (-o2) is applied. Each tetrahedron occupies // 'numberofcorners' ints. The second order nodes are ouput only. // 'tetrahedronattributelist': An array of tetrahedron attributes. Each // tetrahedron's attributes occupy 'numberoftetrahedronattributes' REALs. // 'tetrahedronvolumelist': An array of constraints, i.e. tetrahedron's // volume; one REAL per element. Input only. // 'neighborlist': An array of tetrahedron neighbors; 4 ints per element. // 'tet2facelist': An array of tetrahedron face indices; 4 ints per element. // 'tet2edgelist': An array of tetrahedron edge indices; 6 ints per element. int *tetrahedronlist; REAL *tetrahedronattributelist; REAL *tetrahedronvolumelist; int *neighborlist; int *tet2facelist; int *tet2edgelist; int numberoftetrahedra; int numberofcorners; int numberoftetrahedronattributes; // 'facetlist': An array of facets. Each entry is a structure of facet. // 'facetmarkerlist': An array of facet markers; one int per facet. facet *facetlist; int *facetmarkerlist; int numberoffacets; // 'holelist': An array of holes (in volume). Each hole is given by a // seed (point) which lies strictly inside it. The first seed's x, y and z // coordinates are at indices [0], [1] and [2], followed by the // remaining seeds. Three REALs per hole. REAL *holelist; int numberofholes; // 'regionlist': An array of regions (subdomains). Each region is given by // a seed (point) which lies strictly inside it. The first seed's x, y and // z coordinates are at indices [0], [1] and [2], followed by the regional // attribute at index [3], followed by the maximum volume at index [4]. // Five REALs per region. // Note that each regional attribute is used only if you select the 'A' // switch, and each volume constraint is used only if you select the // 'a' switch (with no number following). REAL *regionlist; int numberofregions; // 'refine_elem_list': An array of tetrahedra to be refined. The first // tetrahedron's first corner is at index [0], followed by its other // corners. Four integers per element. // 'refine_elem_vol_list': An array of constraints, i.e. tetrahedron's // volume; one REAL per element. int *refine_elem_list; REAL *refine_elem_vol_list; int numberofrefineelems; // 'facetconstraintlist': An array of facet constraints. Each constraint // specifies a maximum area bound on the subfaces of that facet. The // first facet constraint is given by a facet marker at index [0] and its // maximum area bound at index [1], followed by the remaining facet con- // straints. Two REALs per facet constraint. Note: the facet marker is // actually an integer. REAL *facetconstraintlist; int numberoffacetconstraints; // 'segmentconstraintlist': An array of segment constraints. Each constraint // specifies a maximum length bound on the subsegments of that segment. // The first constraint is given by the two endpoints of the segment at // index [0] and [1], and the maximum length bound at index [2], followed // by the remaining segment constraints. Three REALs per constraint. // Note the segment endpoints are actually integers. REAL *segmentconstraintlist; int numberofsegmentconstraints; // 'trifacelist': An array of face (triangle) corners. The first face's // three corners are at indices [0], [1] and [2], followed by the remaining // faces. Three ints per face. // 'trifacemarkerlist': An array of face markers; one int per face. // 'o2facelist': An array of second order nodes (on the edges) of the face. // It is output only if the second order option (-o2) is applied. The // first face's three second order nodes are at [0], [1], and [2], // followed by the remaining faces. Three ints per face. // 'face2tetlist': An array of tetrahedra indices; 2 ints per face. // 'face2edgelist': An array of edge indices; 3 ints per face. int *trifacelist; int *trifacemarkerlist; int *o2facelist; int *face2tetlist; int *face2edgelist; int numberoftrifaces; // 'edgelist': An array of edge endpoints. The first edge's endpoints // are at indices [0] and [1], followed by the remaining edges. // Two ints per edge. // 'edgemarkerlist': An array of edge markers; one int per edge. // 'o2edgelist': An array of midpoints of edges. It is output only if the // second order option (-o2) is applied. One int per edge. // 'edge2tetlist': An array of tetrahedra indices. One int per edge. int *edgelist; int *edgemarkerlist; int *o2edgelist; int *edge2tetlist; int numberofedges; // 'vpointlist': An array of Voronoi vertex coordinates (like pointlist). // 'vedgelist': An array of Voronoi edges. Each entry is a 'voroedge'. // 'vfacetlist': An array of Voronoi facets. Each entry is a 'vorofacet'. // 'vcelllist': An array of Voronoi cells. Each entry is an array of // indices pointing into 'vfacetlist'. The 0th entry is used to store // the length of this array. REAL *vpointlist; voroedge *vedgelist; vorofacet *vfacetlist; int **vcelllist; int numberofvpoints; int numberofvedges; int numberofvfacets; int numberofvcells; // Variable (and callback functions) for meshing PSCs. void *geomhandle; GetVertexParamOnEdge getvertexparamonedge; GetSteinerOnEdge getsteineronedge; GetVertexParamOnFace getvertexparamonface; GetEdgeSteinerParamOnFace getedgesteinerparamonface; GetSteinerOnFace getsteineronface; // A callback function. TetSizeFunc tetunsuitable; // Input & output routines. bool load_node_call(FILE* infile, int markers, int uvflag, char*); bool load_node(char*); bool load_edge(char*); bool load_face(char*); bool load_tet(char*); bool load_vol(char*); bool load_var(char*); bool load_mtr(char*); bool load_elem(char*); bool load_poly(char*); bool load_off(char*); bool load_ply(char*); bool load_stl(char*); bool load_vtk(char*); bool load_medit(char*, int); bool load_neumesh(char*, int); bool load_plc(char*, int); bool load_tetmesh(char*, int); void save_nodes(const char*); void save_elements(const char*); void save_faces(const char*); void save_edges(char*); void save_neighbors(char*); void save_poly(const char*); void save_faces2smesh(char*); // Read line and parse string functions. char *readline(char* string, FILE* infile, int *linenumber); char *findnextfield(char* string); char *readnumberline(char* string, FILE* infile, char* infilename); char *findnextnumber(char* string); static void init(polygon* p) { p->vertexlist = (int *) NULL; p->numberofvertices = 0; } static void init(facet* f) { f->polygonlist = (polygon *) NULL; f->numberofpolygons = 0; f->holelist = (REAL *) NULL; f->numberofholes = 0; } // Initialize routine. void initialize() { firstnumber = 0; mesh_dim = 3; useindex = 1; pointlist = (REAL *) NULL; pointattributelist = (REAL *) NULL; pointmtrlist = (REAL *) NULL; pointmarkerlist = (int *) NULL; point2tetlist = (int *) NULL; pointparamlist = (pointparam *) NULL; numberofpoints = 0; numberofpointattributes = 0; numberofpointmtrs = 0; tetrahedronlist = (int *) NULL; tetrahedronattributelist = (REAL *) NULL; tetrahedronvolumelist = (REAL *) NULL; neighborlist = (int *) NULL; tet2facelist = (int *) NULL; tet2edgelist = (int *) NULL; numberoftetrahedra = 0; numberofcorners = 4; numberoftetrahedronattributes = 0; trifacelist = (int *) NULL; trifacemarkerlist = (int *) NULL; o2facelist = (int *) NULL; face2tetlist = (int *) NULL; face2edgelist = (int *) NULL; numberoftrifaces = 0; edgelist = (int *) NULL; edgemarkerlist = (int *) NULL; o2edgelist = (int *) NULL; edge2tetlist = (int *) NULL; numberofedges = 0; facetlist = (facet *) NULL; facetmarkerlist = (int *) NULL; numberoffacets = 0; holelist = (REAL *) NULL; numberofholes = 0; regionlist = (REAL *) NULL; numberofregions = 0; refine_elem_list = (int *) NULL; refine_elem_vol_list = (REAL *) NULL; numberofrefineelems = 0; facetconstraintlist = (REAL *) NULL; numberoffacetconstraints = 0; segmentconstraintlist = (REAL *) NULL; numberofsegmentconstraints = 0; vpointlist = (REAL *) NULL; vedgelist = (voroedge *) NULL; vfacetlist = (vorofacet *) NULL; vcelllist = (int **) NULL; numberofvpoints = 0; numberofvedges = 0; numberofvfacets = 0; numberofvcells = 0; tetunsuitable = NULL; geomhandle = NULL; getvertexparamonedge = NULL; getsteineronedge = NULL; getvertexparamonface = NULL; getedgesteinerparamonface = NULL; getsteineronface = NULL; } // Free the memory allocated in 'tetgenio'. Note that it assumes that the // memory was allocated by the "new" operator (C++). void clean_memory() { int i, j; if (pointlist != (REAL *) NULL) { delete [] pointlist; } if (pointattributelist != (REAL *) NULL) { delete [] pointattributelist; } if (pointmtrlist != (REAL *) NULL) { delete [] pointmtrlist; } if (pointmarkerlist != (int *) NULL) { delete [] pointmarkerlist; } if (point2tetlist != (int *) NULL) { delete [] point2tetlist; } if (pointparamlist != (pointparam *) NULL) { delete [] pointparamlist; } if (tetrahedronlist != (int *) NULL) { delete [] tetrahedronlist; } if (tetrahedronattributelist != (REAL *) NULL) { delete [] tetrahedronattributelist; } if (tetrahedronvolumelist != (REAL *) NULL) { delete [] tetrahedronvolumelist; } if (neighborlist != (int *) NULL) { delete [] neighborlist; } if (tet2facelist != (int *) NULL) { delete [] tet2facelist; } if (tet2edgelist != (int *) NULL) { delete [] tet2edgelist; } if (trifacelist != (int *) NULL) { delete [] trifacelist; } if (trifacemarkerlist != (int *) NULL) { delete [] trifacemarkerlist; } if (o2facelist != (int *) NULL) { delete [] o2facelist; } if (face2tetlist != (int *) NULL) { delete [] face2tetlist; } if (face2edgelist != (int *) NULL) { delete [] face2edgelist; } if (edgelist != (int *) NULL) { delete [] edgelist; } if (edgemarkerlist != (int *) NULL) { delete [] edgemarkerlist; } if (o2edgelist != (int *) NULL) { delete [] o2edgelist; } if (edge2tetlist != (int *) NULL) { delete [] edge2tetlist; } if (facetlist != (facet *) NULL) { facet *f; polygon *p; for (i = 0; i < numberoffacets; i++) { f = &facetlist[i]; for (j = 0; j < f->numberofpolygons; j++) { p = &f->polygonlist[j]; delete [] p->vertexlist; } delete [] f->polygonlist; if (f->holelist != (REAL *) NULL) { delete [] f->holelist; } } delete [] facetlist; } if (facetmarkerlist != (int *) NULL) { delete [] facetmarkerlist; } if (holelist != (REAL *) NULL) { delete [] holelist; } if (regionlist != (REAL *) NULL) { delete [] regionlist; } if (refine_elem_list != (int *) NULL) { delete [] refine_elem_list; if (refine_elem_vol_list != (REAL *) NULL) { delete [] refine_elem_vol_list; } } if (facetconstraintlist != (REAL *) NULL) { delete [] facetconstraintlist; } if (segmentconstraintlist != (REAL *) NULL) { delete [] segmentconstraintlist; } if (vpointlist != (REAL *) NULL) { delete [] vpointlist; } if (vedgelist != (voroedge *) NULL) { delete [] vedgelist; } if (vfacetlist != (vorofacet *) NULL) { for (i = 0; i < numberofvfacets; i++) { delete [] vfacetlist[i].elist; } delete [] vfacetlist; } if (vcelllist != (int **) NULL) { for (i = 0; i < numberofvcells; i++) { delete [] vcelllist[i]; } delete [] vcelllist; } } // Constructor & destructor. tetgenio() {initialize();} ~tetgenio() {clean_memory();} }; // class tetgenio //============================================================================// // // // tetgenbehavior // // // // A structure for maintaining the switches and parameters used by TetGen's // // internal data structure and algorithms. // // // // All switches and parameters are initialized with default values. They are // // set by the command line arguments (argc, argv). // // // // NOTE: Some switches are incompatible with others. While some may depend // // on other switches. The routine parse_commandline() sets the switches from // // the command line (a list of strings) and checks the consistency of the // // applied switches. // // // //============================================================================// class tetgenbehavior { public: // Switches of TetGen. int plc; // '-p', 0. int psc; // '-s', 0. int refine; // '-r', 0. int quality; // '-q', 0. int nobisect; // '-Y', 0. int cdt; // '-D', 0. int cdtrefine; // '-D#', 7. int coarsen; // '-R', 0. int weighted; // '-w', 0. int brio_hilbert; // '-b', 1. int flipinsert; // '-L', 0. int metric; // '-m', 0. int varvolume; // '-a', 0. int fixedvolume; // '-a', 0. int regionattrib; // '-A', 0. int insertaddpoints; // '-i', 0. int diagnose; // '-d', 0. int convex; // '-c', 0. int nomergefacet; // '-M', 0. int nomergevertex; // '-M', 0. int noexact; // '-X', 0. int nostaticfilter; // '-X', 0. int zeroindex; // '-z', 0. int facesout; // '-f', 0. int edgesout; // '-e', 0. int neighout; // '-n', 0. int voroout; // '-v', 0. int meditview; // '-g', 0. int vtkview; // '-k', 0. int vtksurfview; // '-k', 0. int nobound; // '-B', 0. int nonodewritten; // '-N', 0. int noelewritten; // '-E', 0. int nofacewritten; // '-F', 0. int noiterationnum; // '-I', 0. int nojettison; // '-J', 0. int docheck; // '-C', 0. int quiet; // '-Q', 0. int nowarning; // '-W', 0. int verbose; // '-V', 0. // Parameters of TetGen. int vertexperblock; // '-x', 4092. int tetrahedraperblock; // '-x', 8188. int shellfaceperblock; // '-x', 2044. int supsteiner_level; // '-Y/', 2. int addsteiner_algo; // '-Y//', 1. int coarsen_param; // '-R', 0. int weighted_param; // '-w', 0. int fliplinklevel; // -1. int flipstarsize; // -1. int fliplinklevelinc; // 1. int opt_max_flip_level; // '-O', 3. int opt_scheme; // '-O/#', 7. int opt_iterations; // -O//#, 3. int smooth_cirterion; // -s, 1. int smooth_maxiter; // -s, 7. int delmaxfliplevel; // 1. int order; // '-o', 1. int reversetetori; // '-o/', 0. int steinerleft; // '-S', 0. int unflip_queue_limit; // '-U#', 1000. int no_sort; // 0. int hilbert_order; // '-b///', 52. int hilbert_limit; // '-b//' 8. int brio_threshold; // '-b' 64. REAL brio_ratio; // '-b/' 0.125. REAL epsilon; // '-T', 1.0e-8. REAL facet_separate_ang_tol; // '-p', 179.9. REAL collinear_ang_tol; // '-p/', 179.9. REAL facet_small_ang_tol; // '-p//', 15.0. REAL maxvolume; // '-a', -1.0. REAL maxvolume_length; // '-a', -1.0. REAL minratio; // '-q', 0.0. REAL opt_max_asp_ratio; // 1000.0. REAL opt_max_edge_ratio; // 100.0. REAL mindihedral; // '-q', 5.0. REAL optmaxdihedral; // -o/# 177.0. REAL metric_scale; // -m#, 1.0. REAL smooth_alpha; // '-s', 0.3. REAL coarsen_percent; // -R1/#, 1.0. REAL elem_growth_ratio; // Growth ratio of # elements, -r#, 0.0. REAL refine_progress_ratio; // -r/#, 0.333. // Strings of command line arguments and input/output file names. char commandline[1024]; char infilename[1024]; char outfilename[1024]; char addinfilename[1024]; char bgmeshfilename[1024]; // Read an additional tetrahedral mesh and treat it as holes [2018-07-30]. int hole_mesh; // '-H', 0. char hole_mesh_filename[1024]; // The input object of TetGen. They are recognized by either the input // file extensions or by the specified options. // Currently the following objects are supported: // - NODES, a list of nodes (.node); // - POLY, a piecewise linear complex (.poly or .smesh); // - OFF, a polyhedron (.off, Geomview's file format); // - PLY, a polyhedron (.ply, file format from gatech, only ASCII); // - STL, a surface mesh (.stl, stereolithography format); // - MEDIT, a surface mesh (.mesh, Medit's file format); // - MESH, a tetrahedral mesh (.ele). // If no extension is available, the imposed command line switch // (-p or -r) implies the object. enum objecttype {NODES, POLY, OFF, PLY, STL, MEDIT, VTK, MESH, NEU_MESH} object; void syntax(); void usage(); // Command line parse routine. bool parse_commandline(int argc, char **argv); bool parse_commandline(char *switches) { return parse_commandline(0, &switches); } // Initialize all variables. tetgenbehavior() { plc = 0; psc = 0; refine = 0; quality = 0; nobisect = 0; cdt = 0; // set by -D (without a number following it) cdtrefine = 7; // default, set by -D# coarsen = 0; metric = 0; weighted = 0; brio_hilbert = 1; flipinsert = 0; varvolume = 0; fixedvolume = 0; noexact = 0; nostaticfilter = 0; insertaddpoints = 0; regionattrib = 0; diagnose = 0; convex = 0; zeroindex = 0; facesout = 0; edgesout = 0; neighout = 0; voroout = 0; meditview = 0; vtkview = 0; vtksurfview = 0; nobound = 0; nonodewritten = 0; noelewritten = 0; nofacewritten = 0; noiterationnum = 0; nomergefacet = 0; nomergevertex = 0; nojettison = 0; docheck = 0; quiet = 0; nowarning = 0; verbose = 0; vertexperblock = 4092; tetrahedraperblock = 8188; shellfaceperblock = 4092; supsteiner_level = 2; addsteiner_algo = 1; coarsen_param = 0; weighted_param = 0; fliplinklevel = -1; flipstarsize = -1; fliplinklevelinc = 1; opt_scheme = 7; opt_max_flip_level = 3; opt_iterations = 3; delmaxfliplevel = 1; order = 1; reversetetori = 0; steinerleft = -1; unflip_queue_limit = 1000; no_sort = 0; hilbert_order = 52; //-1; hilbert_limit = 8; brio_threshold = 64; brio_ratio = 0.125; facet_separate_ang_tol = 179.9; collinear_ang_tol = 179.9; facet_small_ang_tol = 15.0; maxvolume = -1.0; maxvolume_length = -1.0; minratio = 2.0; opt_max_asp_ratio = 1000.; opt_max_edge_ratio = 100.; mindihedral = 3.5; optmaxdihedral = 177.00; epsilon = 1.0e-8; coarsen_percent = 1.0; metric_scale = 1.0; // -m# elem_growth_ratio = 0.0; // -r# refine_progress_ratio = 0.333; // -r/# object = NODES; smooth_cirterion = 3; // -s# default smooth surface and volume vertices. smooth_maxiter = 7; // set by -s#/7 smooth_alpha = 0.3; // relax parameter, set by -s#/#/0.3 commandline[0] = '\0'; infilename[0] = '\0'; outfilename[0] = '\0'; addinfilename[0] = '\0'; bgmeshfilename[0] = '\0'; hole_mesh = 0; hole_mesh_filename[0] = '\0'; } }; // class tetgenbehavior //============================================================================// // // // Robust Geometric predicates // // // // The following routines are the robust geometric predicates for orientation // // test and point-in-sphere test implemented by Jonathan Shewchuk. // // He generously provided the source code in the public domain, // // http://www.cs.cmu.edu/~quake/robust.html. // // predicates.cxx is a C++ version of the original C code. // // // // The original predicates of Shewchuk only use "dynamic filters", i.e., it // // computes the error at runtime step by step. TetGen first uses a "static // // filter" in each predicate. It estimates the maximal possible error in all // // cases. It safely and quickly "filters" many easy cases. // // // //============================================================================// void exactinit(int, int, int, REAL, REAL, REAL); REAL orient3d(REAL *pa, REAL *pb, REAL *pc, REAL *pd); REAL insphere(REAL *pa, REAL *pb, REAL *pc, REAL *pd, REAL *pe); REAL orient4d(REAL *pa, REAL *pb, REAL *pc, REAL *pd, REAL *pe, REAL ah, REAL bh, REAL ch, REAL dh, REAL eh); REAL orient2dexact(REAL *pa, REAL *pb, REAL *pc); REAL orient3dexact(REAL *pa, REAL *pb, REAL *pc, REAL *pd); REAL orient4dexact(REAL* pa, REAL* pb, REAL* pc, REAL* pd, REAL* pe, REAL ah, REAL bh, REAL ch, REAL dh, REAL eh); //============================================================================// // // // tetgenmesh TetGen's internal mesh data structure. // // // // It uses a tetrahedron-based mesh data structure. It implements elementary // // flip operations to locally modify the mesh. It implements basic meshing // // algorithms to create Delaunay tetrahedraliations, to perform boundary // // recovery, to place Steiner points in the mesh domain, and to optimize the // // quality of the mesh. // // // //============================================================================// class tetgenmesh { public: //============================================================================// // // // Mesh data structure // // // // A tetrahedral mesh T of a 3D piecewise linear complex (PLC) X is a 3D // // simplicial complex whose underlying space is equal to the space of X. T // // contains a 2D subcomplex S which is a triangular mesh of the boundary of // // X. S contains a 1D subcomplex L which is a linear mesh of the boundary of // // S. Faces and edges in S and L are respectively called subfaces and segme- // // nts to distinguish them from others in T. // // // // TetGen uses a tetrahedron-based data structure. It stores tetrahedra and // // vertices. This data structure is pointer-based. Each tetrahedron contains // // pointers to its vertices and adjacent tetrahedra. Each vertex holds its x-,// // y-, z-coordinates, and a pointer to one of the tetrahedra having it. Both // // tetrahedra and vertices may contain user data. // // // // Let T be a tetrahedralization. Each triangular face of T belongs to either // // two or one tetrahedron. In the latter case, it is an exterior boundary // // face of T. TetGen attaches tetrahedra (one-to-one) to such faces. All such // // tetrahedra contain an "infinite vertex" (which has no geometric coordinates// // ). One can imagine such a vertex lies in 4D space and is visible by all // // exterior boundary faces simultaneously. This extended set of tetrahedra // // (including the infinite vertex) becomes a tetrahedralization of a 3-sphere // // that has no boundary in 3d. It has the nice property that every triangular // // face is shared by exactly two tetrahedra. // // // // The current version of TetGen stores explicitly the subfaces and segments // // (which are in surface mesh S and the linear mesh L), respectively. Extra // // pointers are allocated in tetrahedra and subfaces to point each other. // // // //============================================================================// // The tetrahedron data structure. It includes the following fields: // - a list of four adjoining tetrahedra; // - a list of four vertices; // - a pointer to a list of four subfaces (optional, for -p switch); // - a pointer to a list of six segments (optional, for -p switch); // - a list of user-defined floating-point attributes (optional); // - a volume constraint (optional, for -a switch); // - an integer of element marker (and flags); // The structure of a tetrahedron is an array of pointers. Its actual size // (the length of the array) is determined at runtime. typedef REAL **tetrahedron; // The subface data structure. It includes the following fields: // - a list of three adjoining subfaces; // - a list of three vertices; // - a list of three adjoining segments; // - two adjoining tetrahedra; // - an area constraint (optional, for -q switch); // - an integer for boundary marker; // - an integer for type, flags, etc. typedef REAL **shellface; // The point data structure. It includes the following fields: // - x, y and z coordinates; // - a list of user-defined point attributes (optional); // - u, v coordinates (optional, for -s switch); // - a metric tensor (optional, for -q or -m switch); // - a pointer to an adjacent tetrahedron; // - a pointer to a parent (or a duplicate) point; // - a pointer to an adjacent subface or segment (optional, -p switch); // - a pointer to a tet in background mesh (optional, for -m switch); // - an integer for boundary marker (point index); // - an integer for point type (and flags). // - an integer for geometry tag (optional, for -s switch). // The structure of a point is an array of REALs. Its acutal size is // determined at the runtime. typedef REAL *point; //============================================================================// // // // Handles // // // // Navigation and manipulation in a tetrahedralization are accomplished by // // operating on structures referred as ``handles". A handle is a pair (t,v), // // where t is a pointer to a tetrahedron, and v is a 4-bit integer, in the // // range from 0 to 11. v is called the ``version'' of a tetrahedron, it rep- // // resents a directed edge of a specific face of the tetrahedron. // // // // There are 12 even permutations of the four vertices, each of them corres- // // ponds to a directed edge (a version) of the tetrahedron. The 12 versions // // can be grouped into 4 distinct ``edge rings'' in 4 ``oriented faces'' of // // this tetrahedron. One can encode each version (a directed edge) into a // // 4-bit integer such that the two upper bits encode the index (from 0 to 2) // // of this edge in the edge ring, and the two lower bits encode the index ( // // from 0 to 3) of the oriented face which contains this edge. // // // // The four vertices of a tetrahedron are indexed from 0 to 3 (according to // // their storage in the data structure). Give each face the same index as // // the node opposite it in the tetrahedron. Denote the edge connecting face // // i to face j as i/j. We number the twelve versions as follows: // // // // | edge 0 edge 1 edge 2 // // --------|-------------------------------- // // face 0 | 0 (0/1) 4 (0/3) 8 (0/2) // // face 1 | 1 (1/2) 5 (1/3) 9 (1/0) // // face 2 | 2 (2/3) 6 (2/1) 10 (2/0) // // face 3 | 3 (3/0) 7 (3/1) 11 (3/2) // // // // Similarly, navigation and manipulation in a (boundary) triangulation are // // done by using handles of triangles. Each handle is a pair (s, v), where s // // is a pointer to a triangle, and v is a version in the range from 0 to 5. // // Each version corresponds to a directed edge of this triangle. // // // // Number the three vertices of a triangle from 0 to 2 (according to their // // storage in the data structure). Give each edge the same index as the node // // opposite it in the triangle. The six versions of a triangle are: // // // // | edge 0 edge 1 edge 2 // // ---------------|-------------------------- // // ccw orieation | 0 2 4 // // cw orieation | 1 3 5 // // // // In the following, a 'triface' is a handle of tetrahedron, and a 'face' is // // a handle of a triangle. // // // //============================================================================// class triface { public: tetrahedron *tet; int ver; // Range from 0 to 11. triface() : tet(0), ver(0) {} triface& operator=(const triface& t) { tet = t.tet; ver = t.ver; return *this; } }; class face { public: shellface *sh; int shver; // Range from 0 to 5. face() : sh(0), shver(0) {} face& operator=(const face& s) { sh = s.sh; shver = s.shver; return *this; } }; //============================================================================// // // // Arraypool // // // // A dynamic linear array. (It is written by J. Shewchuk) // // // // Each arraypool contains an array of pointers to a number of blocks. Each // // block contains the same fixed number of objects. Each index of the array // // addresses a particular object in the pool. The most significant bits add- // // ress the index of the block containing the object. The less significant // // bits address this object within the block. // // // // 'objectbytes' is the size of one object in blocks; 'log2objectsperblock' // // is the base-2 logarithm of 'objectsperblock'; 'objects' counts the number // // of allocated objects; 'totalmemory' is the total memory in bytes. // // // //============================================================================// class arraypool { public: int objectbytes; int objectsperblock; int log2objectsperblock; int objectsperblockmark; int toparraylen; char **toparray; long objects; unsigned long totalmemory; void restart(); void poolinit(int sizeofobject, int log2objperblk); char* getblock(int objectindex); void* lookup(int objectindex); int newindex(void **newptr); arraypool(int sizeofobject, int log2objperblk); ~arraypool(); }; // fastlookup() -- A fast, unsafe operation. Return the pointer to the object // with a given index. Note: The object's block must have been allocated, // i.e., by the function newindex(). #define fastlookup(pool, index) \ (void *) ((pool)->toparray[(index) >> (pool)->log2objectsperblock] + \ ((index) & (pool)->objectsperblockmark) * (pool)->objectbytes) //============================================================================// // // // Memorypool // // // // A structure for memory allocation. (It is written by J. Shewchuk) // // // // firstblock is the first block of items. nowblock is the block from which // // items are currently being allocated. nextitem points to the next slab // // of free memory for an item. deaditemstack is the head of a linked list // // (stack) of deallocated items that can be recycled. unallocateditems is // // the number of items that remain to be allocated from nowblock. // // // // Traversal is the process of walking through the entire list of items, and // // is separate from allocation. Note that a traversal will visit items on // // the "deaditemstack" stack as well as live items. pathblock points to // // the block currently being traversed. pathitem points to the next item // // to be traversed. pathitemsleft is the number of items that remain to // // be traversed in pathblock. // // // //============================================================================// class memorypool { public: void **firstblock, **nowblock; void *nextitem; void *deaditemstack; void **pathblock; void *pathitem; int alignbytes; int itembytes, itemwords; int itemsperblock; long items, maxitems; int unallocateditems; int pathitemsleft; memorypool(); memorypool(int, int, int, int); ~memorypool(); void poolinit(int, int, int, int); void restart(); void *alloc(); void dealloc(void*); void traversalinit(); void *traverse(); }; //============================================================================// // // // badface // // // // Despite of its name, a 'badface' can be used to represent one of the // // following objects: // // - a face of a tetrahedron which is (possibly) non-Delaunay; // // - an encroached subsegment or subface; // // - a bad-quality tetrahedron, i.e, has too large radius-edge ratio; // // - a sliver, i.e., has good radius-edge ratio but nearly zero volume; // // - a recently flipped face (saved for undoing the flip later). // // // //============================================================================// class badface { public: triface tt; face ss; REAL key, cent[6]; // circumcenter or cos(dihedral angles) at 6 edges. point forg, fdest, fapex, foppo, noppo; badface *nextitem; badface() : key(0), forg(0), fdest(0), fapex(0), foppo(0), noppo(0), nextitem(0) {} void init() { key = 0.; for (int k = 0; k < 6; k++) cent[k] = 0.; tt.tet = NULL; tt.ver = 0; ss.sh = NULL; ss.shver = 0; forg = fdest = fapex = foppo = noppo = NULL; nextitem = NULL; } }; //============================================================================// // // // insertvertexflags // // // // A collection of flags that pass to the routine insertvertex(). // // // //============================================================================// class insertvertexflags { public: int iloc; // input/output. int bowywat, lawson; int splitbdflag, validflag, respectbdflag; int rejflag, chkencflag, cdtflag; int assignmeshsize; int sloc, sbowywat; // Used by Delaunay refinement. int collect_inial_cavity_flag; int ignore_near_vertex; int check_insert_radius; int refineflag; // 0, 1, 2, 3 triface refinetet; face refinesh; int smlenflag; // for useinsertradius. REAL smlen; // for useinsertradius. point parentpt; void init() { iloc = bowywat = lawson = 0; splitbdflag = validflag = respectbdflag = 0; rejflag = chkencflag = cdtflag = 0; assignmeshsize = 0; sloc = sbowywat = 0; collect_inial_cavity_flag = 0; ignore_near_vertex = 0; check_insert_radius = 0; refineflag = 0; refinetet.tet = NULL; refinesh.sh = NULL; smlenflag = 0; smlen = 0.0; parentpt = NULL; } insertvertexflags() { init(); } }; //============================================================================// // // // flipconstraints // // // // A structure of a collection of data (options and parameters) which pass // // to the edge flip function flipnm(). // // // //============================================================================// class flipconstraints { public: // Elementary flip flags. int enqflag; // (= flipflag) int chkencflag; // Control flags int unflip; // Undo the performed flips. int collectnewtets; // Collect the new tets created by flips. int collectencsegflag; // Optimization flags. int noflip_in_surface; // do not flip edges (not segment) in surface. int remove_ndelaunay_edge; // Remove a non-Delaunay edge. REAL bak_tetprism_vol; // The value to be minimized. REAL tetprism_vol_sum; int remove_large_angle; // Remove a large dihedral angle at edge. REAL cosdihed_in; // The input cosine of the dihedral angle (> 0). REAL cosdihed_out; // The improved cosine of the dihedral angle. REAL max_asp_out; // Max asp ratio after the improvement of dihedral angle. // Boundary recovery flags. int checkflipeligibility; point seg[2]; // A constraining edge to be recovered. point fac[3]; // A constraining face to be recovered. point remvert; // A vertex to be removed. flipconstraints() { enqflag = 0; chkencflag = 0; unflip = 0; collectnewtets = 0; collectencsegflag = 0; noflip_in_surface = 0; remove_ndelaunay_edge = 0; bak_tetprism_vol = 0.0; tetprism_vol_sum = 0.0; remove_large_angle = 0; cosdihed_in = 0.0; cosdihed_out = 0.0; max_asp_out = 0.0; checkflipeligibility = 0; seg[0] = NULL; fac[0] = NULL; remvert = NULL; } }; //============================================================================// // // // optparameters // // // // Optimization options and parameters. // // // //============================================================================// class optparameters { public: // The one of goals of optimization. int max_min_volume; // Maximize the minimum volume. int min_max_aspectratio; // Minimize the maximum aspect ratio. int min_max_dihedangle; // Minimize the maximum dihedral angle. // The initial and improved value. REAL initval, imprval; int numofsearchdirs; REAL searchstep; int maxiter; // Maximum smoothing iterations (disabled by -1). int smthiter; // Performed iterations. optparameters() { max_min_volume = 0; min_max_aspectratio = 0; min_max_dihedangle = 0; initval = imprval = 0.0; numofsearchdirs = 10; searchstep = 0.01; maxiter = -1; // Unlimited smoothing iterations. smthiter = 0; } }; //============================================================================// // // // Labels (enumeration declarations) used by TetGen. // // // //============================================================================// // Labels that signify the type of a vertex. enum verttype {UNUSEDVERTEX, DUPLICATEDVERTEX, RIDGEVERTEX, /*ACUTEVERTEX,*/ FACETVERTEX, VOLVERTEX, FREESEGVERTEX, FREEFACETVERTEX, FREEVOLVERTEX, NREGULARVERTEX, DEADVERTEX}; // Labels that signify the result of triangle-triangle intersection test. enum interresult {DISJOINT, INTERSECT, SHAREVERT, SHAREEDGE, SHAREFACE, TOUCHEDGE, TOUCHFACE, ACROSSVERT, ACROSSEDGE, ACROSSFACE, SELF_INTERSECT}; // Labels that signify the result of point location. enum locateresult {UNKNOWN, OUTSIDE, INTETRAHEDRON, ONFACE, ONEDGE, ONVERTEX, ENCVERTEX, ENCSEGMENT, ENCSUBFACE, NEARVERTEX, NONREGULAR, INSTAR, BADELEMENT, NULLCAVITY, SHARPCORNER, FENSEDIN, NONCOPLANAR, SELF_ENCROACH}; //============================================================================// // // // Variables of TetGen // // // //============================================================================// // Pointer to the input data (a set of nodes, a PLC, or a mesh). tetgenio *in, *addin; // Pointer to the switches and parameters. tetgenbehavior *b; // Pointer to a background mesh (contains size specification map). tetgenmesh *bgm; // Memorypools to store mesh elements (points, tetrahedra, subfaces, and // segments) and extra pointers between tetrahedra, subfaces, and segments. memorypool *tetrahedrons, *subfaces, *subsegs, *points; memorypool *tet2subpool, *tet2segpool; // Memorypools to store bad-quality (or encroached) elements. memorypool *badtetrahedrons, *badsubfacs, *badsubsegs; memorypool *split_subfaces_pool, *split_segments_pool; arraypool *unsplit_badtets, *unsplit_subfaces, *unsplit_segments; arraypool *check_tets_list; badface *stack_enc_segments, *stack_enc_subfaces; // Bad quality subfaces are ordered by priority queues. badface *queuefront[64]; badface *queuetail[64]; int nextnonemptyq[64]; int firstnonemptyq, recentq; // Bad quality tetrahedra are ordered by priority queues. memorypool *badqual_tets_pool; badface *bt_queuefront[64]; badface *bt_queuetail[64]; int bt_nextnonemptyq[64]; int bt_firstnonemptyq, bt_recentq; // A memorypool to store faces to be flipped. memorypool *flippool; arraypool *later_unflip_queue, *unflipqueue; badface *flipstack, *unflip_queue_front, *unflip_queue_tail; // Arrays used for point insertion (the Bowyer-Watson algorithm). arraypool *cavetetlist, *cavebdrylist, *caveoldtetlist; arraypool *cave_oldtet_list; // only tetrahedron's arraypool *cavetetshlist, *cavetetseglist, *cavetetvertlist; arraypool *caveencshlist, *caveencseglist; arraypool *caveshlist, *caveshbdlist, *cavesegshlist; triface _bw_faces[4096]; // _bw_faces[64][64]; // Stacks used for CDT construction and boundary recovery. arraypool *subsegstack, *subfacstack, *subvertstack; arraypool *skipped_segment_list, *skipped_facet_list; // Arrays of encroached segments and subfaces (for mesh refinement). arraypool *encseglist, *encshlist; // The map between facets to their vertices (for mesh refinement). int number_of_facets; int *idx2facetlist; point *facetverticeslist; int *idx_segment_facet_list; // segment-to-facet map. int *segment_facet_list; int *idx_ridge_vertex_facet_list; // vertex-to-facet map. int *ridge_vertex_facet_list; // The map between segments to their endpoints (for mesh refinement). int segmentendpointslist_length; point *segmentendpointslist; double *segment_info_list; int *idx_segment_ridge_vertex_list; // are two ridge vertices form a segment? point *segment_ridge_vertex_list; // The infinite vertex. point dummypoint; // The recently visited tetrahedron, subface. triface recenttet; face recentsh; // PI is the ratio of a circle's circumference to its diameter. static REAL PI; // The list of subdomains. (-A option). int subdomains; // Number of subdomains. int *subdomain_markers; // Various variables. int numpointattrib; // Number of point attributes. int numelemattrib; // Number of tetrahedron attributes. int sizeoftensor; // Number of REALs per metric tensor. int pointmtrindex; // Index to find the metric tensor of a point. int pointparamindex; // Index to find the u,v coordinates of a point. int point2simindex; // Index to find a simplex adjacent to a point. int pointmarkindex; // Index to find boundary marker of a point. int pointinsradiusindex; // Index to find the insertion radius of a point. int elemattribindex; // Index to find attributes of a tetrahedron. int polarindex; // Index to find the polar plane parameters. int volumeboundindex; // Index to find volume bound of a tetrahedron. int elemmarkerindex; // Index to find marker of a tetrahedron. int shmarkindex; // Index to find boundary marker of a subface. int areaboundindex; // Index to find area bound of a subface. int checksubsegflag; // Are there segments in the tetrahedralization yet? int checksubfaceflag; // Are there subfaces in the tetrahedralization yet? int boundary_recovery_flag; int checkconstraints; // Are there variant (node, seg, facet) constraints? int nonconvex; // Is current mesh non-convex? int autofliplinklevel; // The increase of link levels, default is 1. int useinsertradius; // Save the insertion radius for Steiner points. long samples; // Number of random samples for point location. unsigned long randomseed; // Current random number seed. REAL cosmaxdihed, cosmindihed; // The cosine values of max/min dihedral. REAL cossmtdihed; // The cosine value of a bad dihedral to be smoothed. REAL cosslidihed; // The cosine value of the max dihedral of a sliver. REAL cos_large_dihed; // The cosine value of large dihedral (135 degree). REAL opt_max_sliver_asp_ratio; // = 10 x b->opt_max_asp_ratio. REAL minfaceang, minfacetdihed; // The minimum input (dihedral) angles. REAL cos_facet_separate_ang_tol; REAL cos_collinear_ang_tol; REAL tetprism_vol_sum; // The total volume of tetrahedral-prisms (in 4D). REAL longest; // The longest possible edge length. REAL minedgelength; // = longest * b->epsion. REAL xmax, xmin, ymax, ymin, zmax, zmin; // Bounding box of points. // Options for mesh refinement. REAL big_radius_edge_ratio; // calculated by qualitystatistics(). REAL smallest_insradius; // Save the smallest insertion radius. long elem_limit; long insert_point_count; // number of attempted insertions. long report_refine_progress; // the next report event. long last_point_count; // number of points after last report event. long last_insertion_count; // number of insertions after last report event. // Counters. long insegments; // Number of input segments. long hullsize; // Number of exterior boundary faces. long meshedges; // Number of mesh edges. long meshhulledges; // Number of boundary mesh edges. long steinerleft; // Number of Steiner points not yet used. long dupverts; // Are there duplicated vertices? long unuverts; // Are there unused vertices? long duplicated_facets_count; // Are there duplicated facets.? long nonregularcount; // Are there non-regular vertices? long st_segref_count, st_facref_count, st_volref_count; // Steiner points. long fillregioncount, cavitycount, cavityexpcount; long flip14count, flip26count, flipn2ncount; long flip23count, flip32count, flip44count, flip41count; long flip31count, flip22count; long opt_flips_count, opt_collapse_count, opt_smooth_count; long recover_delaunay_count; unsigned long totalworkmemory; // Total memory used by working arrays. //============================================================================// // // // Mesh manipulation primitives // // // //============================================================================// // Fast lookup tables for mesh manipulation primitives. static int bondtbl[12][12], fsymtbl[12][12]; static int esymtbl[12], enexttbl[12], eprevtbl[12]; static int enextesymtbl[12], eprevesymtbl[12]; static int eorgoppotbl[12], edestoppotbl[12]; static int facepivot1[12], facepivot2[12][12]; static int orgpivot[12], destpivot[12], apexpivot[12], oppopivot[12]; static int tsbondtbl[12][6], stbondtbl[12][6]; static int tspivottbl[12][6], stpivottbl[12][6]; static int ver2edge[12], edge2ver[6], epivot[12]; static int sorgpivot [6], sdestpivot[6], sapexpivot[6]; static int snextpivot[6]; void inittables(); // Primitives for tetrahedra. inline tetrahedron encode(triface& t); inline tetrahedron encode2(tetrahedron* ptr, int ver); inline void decode(tetrahedron ptr, triface& t); inline tetrahedron* decode_tet_only(tetrahedron ptr); inline int decode_ver_only(tetrahedron ptr); inline void bond(triface& t1, triface& t2); inline void dissolve(triface& t); inline void esym(triface& t1, triface& t2); inline void esymself(triface& t); inline void enext(triface& t1, triface& t2); inline void enextself(triface& t); inline void eprev(triface& t1, triface& t2); inline void eprevself(triface& t); inline void enextesym(triface& t1, triface& t2); inline void enextesymself(triface& t); inline void eprevesym(triface& t1, triface& t2); inline void eprevesymself(triface& t); inline void eorgoppo(triface& t1, triface& t2); inline void eorgoppoself(triface& t); inline void edestoppo(triface& t1, triface& t2); inline void edestoppoself(triface& t); inline void fsym(triface& t1, triface& t2); inline void fsymself(triface& t); inline void fnext(triface& t1, triface& t2); inline void fnextself(triface& t); inline point org (triface& t); inline point dest(triface& t); inline point apex(triface& t); inline point oppo(triface& t); inline void setorg (triface& t, point p); inline void setdest(triface& t, point p); inline void setapex(triface& t, point p); inline void setoppo(triface& t, point p); inline REAL elemattribute(tetrahedron* ptr, int attnum); inline void setelemattribute(tetrahedron* ptr, int attnum, REAL value); inline REAL* get_polar(tetrahedron* ptr); inline REAL get_volume(tetrahedron* ptr); inline REAL volumebound(tetrahedron* ptr); inline void setvolumebound(tetrahedron* ptr, REAL value); inline int elemindex(tetrahedron* ptr); inline void setelemindex(tetrahedron* ptr, int value); inline int elemmarker(tetrahedron* ptr); inline void setelemmarker(tetrahedron* ptr, int value); inline void infect(triface& t); inline void uninfect(triface& t); inline bool infected(triface& t); inline void marktest(triface& t); inline void unmarktest(triface& t); inline bool marktested(triface& t); inline void markface(triface& t); inline void unmarkface(triface& t); inline bool facemarked(triface& t); inline void markedge(triface& t); inline void unmarkedge(triface& t); inline bool edgemarked(triface& t); inline void marktest2(triface& t); inline void unmarktest2(triface& t); inline bool marktest2ed(triface& t); inline int elemcounter(triface& t); inline void setelemcounter(triface& t, int value); inline void increaseelemcounter(triface& t); inline void decreaseelemcounter(triface& t); inline bool ishulltet(triface& t); inline bool isdeadtet(triface& t); // Primitives for subfaces and subsegments. inline void sdecode(shellface sptr, face& s); inline shellface sencode(face& s); inline shellface sencode2(shellface *sh, int shver); inline void spivot(face& s1, face& s2); inline void spivotself(face& s); inline void sbond(face& s1, face& s2); inline void sbond1(face& s1, face& s2); inline void sdissolve(face& s); inline point sorg(face& s); inline point sdest(face& s); inline point sapex(face& s); inline void setsorg(face& s, point pointptr); inline void setsdest(face& s, point pointptr); inline void setsapex(face& s, point pointptr); inline void sesym(face& s1, face& s2); inline void sesymself(face& s); inline void senext(face& s1, face& s2); inline void senextself(face& s); inline void senext2(face& s1, face& s2); inline void senext2self(face& s); inline REAL areabound(face& s); inline void setareabound(face& s, REAL value); inline int shellmark(face& s); inline void setshellmark(face& s, int value); inline void sinfect(face& s); inline void suninfect(face& s); inline bool sinfected(face& s); inline void smarktest(face& s); inline void sunmarktest(face& s); inline bool smarktested(face& s); inline void smarktest2(face& s); inline void sunmarktest2(face& s); inline bool smarktest2ed(face& s); inline void smarktest3(face& s); inline void sunmarktest3(face& s); inline bool smarktest3ed(face& s); inline void setfacetindex(face& f, int value); inline int getfacetindex(face& f); inline bool isdeadsh(face& s); // Primitives for interacting tetrahedra and subfaces. inline void tsbond(triface& t, face& s); inline void tsdissolve(triface& t); inline void stdissolve(face& s); inline void tspivot(triface& t, face& s); inline void stpivot(face& s, triface& t); // Primitives for interacting tetrahedra and segments. inline void tssbond1(triface& t, face& seg); inline void sstbond1(face& s, triface& t); inline void tssdissolve1(triface& t); inline void sstdissolve1(face& s); inline void tsspivot1(triface& t, face& s); inline void sstpivot1(face& s, triface& t); // Primitives for interacting subfaces and segments. inline void ssbond(face& s, face& edge); inline void ssbond1(face& s, face& edge); inline void ssdissolve(face& s); inline void sspivot(face& s, face& edge); // Primitives for points. inline int pointmark(point pt); inline void setpointmark(point pt, int value); inline enum verttype pointtype(point pt); inline void setpointtype(point pt, enum verttype value); inline int pointgeomtag(point pt); inline void setpointgeomtag(point pt, int value); inline REAL pointgeomuv(point pt, int i); inline void setpointgeomuv(point pt, int i, REAL value); inline void pinfect(point pt); inline void puninfect(point pt); inline bool pinfected(point pt); inline void pmarktest(point pt); inline void punmarktest(point pt); inline bool pmarktested(point pt); inline void pmarktest2(point pt); inline void punmarktest2(point pt); inline bool pmarktest2ed(point pt); inline void pmarktest3(point pt); inline void punmarktest3(point pt); inline bool pmarktest3ed(point pt); inline tetrahedron point2tet(point pt); inline void setpoint2tet(point pt, tetrahedron value); inline shellface point2sh(point pt); inline void setpoint2sh(point pt, shellface value); inline point point2ppt(point pt); inline void setpoint2ppt(point pt, point value); inline tetrahedron point2bgmtet(point pt); inline void setpoint2bgmtet(point pt, tetrahedron value); inline void setpointinsradius(point pt, REAL value); inline REAL getpointinsradius(point pt); inline bool issteinerpoint(point pt); // Advanced primitives. inline void point2tetorg(point pt, triface& t); inline void point2shorg(point pa, face& s); inline point farsorg(face& seg); inline point farsdest(face& seg); //============================================================================// // // // Memory managment // // // //============================================================================// void tetrahedrondealloc(tetrahedron*); tetrahedron *tetrahedrontraverse(); tetrahedron *alltetrahedrontraverse(); void shellfacedealloc(memorypool*, shellface*); shellface *shellfacetraverse(memorypool*); void pointdealloc(point); point pointtraverse(); void makeindex2pointmap(point*&); void makepoint2submap(memorypool*, int*&, face*&); void maketetrahedron(triface*); void maketetrahedron2(triface*, point, point, point, point); void makeshellface(memorypool*, face*); void makepoint(point*, enum verttype); void initializepools(); //============================================================================// // // // Advanced geometric predicates and calculations // // // // the routine insphere_s() implements a simplified symbolic perturbation // // scheme from Edelsbrunner, et al [*]. Hence the point-in-sphere test never // // returns a zero. The idea is to perturb the weights of vertices in 4D. // // // // The routine tri_edge_test() determines whether or not a triangle and an // // edge intersect in 3D. If they do cross, their intersection type is also // // reported. This test is a combination of n 3D orientation tests (3 < n < 9).// // It uses the robust orient3d() test to make the branch decisions. // // // // There are several routines to calculate geometrical quantities, e.g., // // circumcenters, angles, dihedral angles, face normals, face areas, etc. // // They are implemented using floating-point arithmetics. // // // //============================================================================// // Symbolic perturbations (robust) REAL insphere_s(REAL*, REAL*, REAL*, REAL*, REAL*); REAL orient4d_s(REAL*, REAL*, REAL*, REAL*, REAL*, REAL, REAL, REAL, REAL, REAL); // An embedded 2-dimensional geometric predicate (non-robust) REAL incircle3d(point pa, point pb, point pc, point pd); // Triangle-edge intersection test (robust) int tri_edge_2d(point, point, point, point, point, point, int, int*, int*); int tri_edge_tail(point,point,point,point,point,point,REAL,REAL,int,int*,int*); int tri_edge_test(point, point, point, point, point, point, int, int*, int*); // Triangle-triangle intersection test (robust) int tri_edge_inter_tail(point, point, point, point, point, REAL, REAL); int tri_tri_inter(point, point, point, point, point, point); // Linear algebra functions inline REAL dot(REAL* v1, REAL* v2); inline void cross(REAL* v1, REAL* v2, REAL* n); bool lu_decmp(REAL lu[4][4], int n, int* ps, REAL* d, int N); void lu_solve(REAL lu[4][4], int n, int* ps, REAL* b, int N); // Geometric calculations (non-robust) REAL orient3dfast(REAL *pa, REAL *pb, REAL *pc, REAL *pd); inline REAL norm2(REAL x, REAL y, REAL z); inline REAL distance(REAL* p1, REAL* p2); inline REAL distance2(REAL* p1, REAL* p2); void facenormal(point pa, point pb, point pc, REAL *n, int pivot, REAL *lav); REAL facedihedral(REAL* pa, REAL* pb, REAL* pc1, REAL* pc2); REAL triarea(REAL* pa, REAL* pb, REAL* pc); REAL interiorangle(REAL* o, REAL* p1, REAL* p2, REAL* n); REAL cos_interiorangle(REAL* o, REAL* p1, REAL* p2); void projpt2edge(REAL* p, REAL* e1, REAL* e2, REAL* prj); void projpt2face(REAL* p, REAL* f1, REAL* f2, REAL* f3, REAL* prj); bool circumsphere(REAL*, REAL*, REAL*, REAL*, REAL* cent, REAL* radius); bool orthosphere(REAL*,REAL*,REAL*,REAL*,REAL,REAL,REAL,REAL,REAL*,REAL*); void planelineint(REAL*, REAL*, REAL*, REAL*, REAL*, REAL*, REAL*); int linelineint(REAL*, REAL*, REAL*, REAL*, REAL*, REAL*, REAL*, REAL*); REAL tetprismvol(REAL* pa, REAL* pb, REAL* pc, REAL* pd); bool calculateabovepoint(arraypool*, point*, point*, point*); void calculateabovepoint4(point, point, point, point); //============================================================================// // // // Local mesh transformations // // // // A local transformation replaces a set of tetrahedra with another set that // // partitions the same space and boundaries. // // // // In 3D, the most straightforward local transformations are the elementary // // flips performed within the convex hull of five vertices: 2-to-3, 3-to-2, // // 1-to-4, and 4-to-1 flips. The numbers indicate the number of tetrahedra // // before and after each flip. The 1-to-4 and 4-to-1 flip involve inserting // // or deleting a vertex, respectively. // // // // There are complex local transformations that are a combination of element- // // ary flips. For example, a 4-to-4 flip, which replaces two coplanar edges, // // combines a 2-to-3 flip and a 3-to-2 flip. Note that the first 2-to-3 flip // // will temporarily create a degenerate tetrahedron removed immediately by // // the followed 3-to-2 flip. More generally, an n-to-m flip, where n > 3, // // m = (n - 2) * 2, which removes an edge, can be done by first performing a // // sequence of (n - 3) 2-to-3 flips followed by a 3-to-2 flip. // // // // The routines flip23(), flip32(), and flip41() perform the three elementray // // flips. The flip14() is available inside the routine insertpoint(). // // // // The routines flipnm() and flipnm_post() implement a generalized edge flip // // algorithm that uses elementary flips. // // // // The routine insertpoint() implements the Bowyer-Watson's cavity algorithm // // to insert a vertex. It works for arbitrary tetrahedralization, either // // Delaunay, or constrained Delaunay, or non-Delaunay. // // // //============================================================================// void flippush(badface*&, triface*); // The elementary flips. void flip23(triface*, int, flipconstraints* fc); void flip32(triface*, int, flipconstraints* fc); void flip41(triface*, int, flipconstraints* fc); // A generalized edge flip. int flipnm(triface*, int n, int level, int, flipconstraints* fc); int flipnm_post(triface*, int n, int nn, int, flipconstraints* fc); // Point insertion. int insertpoint(point, triface*, face*, face*, insertvertexflags*); void insertpoint_abort(face*, insertvertexflags*); //============================================================================// // // // Delaunay tetrahedralization // // // // The routine incrementaldelaunay() implemented two incremental algorithms // // for constructing Delaunay tetrahedralizations (DTs): the Bowyer-Watson // // (B-W) algorithm and the incremental flip algorithm of Edelsbrunner and // // Shah, "Incremental topological flipping works for regular triangulation," // // Algorithmica, 15:233-241, 1996. // // // // The routine incrementalflip() implements the flip algorithm of [Edelsbrun- // // ner and Shah, 1996]. It flips a queue of locally non-Delaunay faces (in // // arbitrary order). The success is guaranteed when the Delaunay tetrahedra- // // lization is constructed incrementally by adding one vertex at a time. // // // // The routine locate() finds a tetrahedron contains a new point in current // // DT. It uses a simple stochastic walk algorithm: starting from an arbitrary // // tetrahedron in DT, it finds the destination by visit one tetrahedron at a // // time, randomly chooses a tetrahedron if there are more than one choices. // // This algorithm terminates due to Edelsbrunner's acyclic theorem. // // // // Choose a good starting tetrahedron is crucial to the speed of the walk. // // TetGen initially uses the "jump-and-walk" algorithm of Muecke, E.P., Saias,// // I., and Zhu, B. "Fast Randomized Point Location Without Preprocessing." In // // Proceedings of the 12th ACM Symposium on Computational Geometry, 274-283, // // 1996. It first randomly samples several tetrahedra in the DT and then // // choosing the closet one to start walking. // // // // The above algorithm slows download dramatically as the number of points // // grows -- reported in Amenta, N., Choi, S. and Rote, G., "Incremental // // construction con {BRIO}," In Proceedings of 19th ACM Symposium on Computa- // // tional Geometry, 211-219, 2003. On the other hand, Liu and Snoeyink showed // // that the point location could be made in constant time if the points are // // pre-sorted so that the nearby points in space have nearby indices, then // // adding the points in this order. They sorted the points along the 3D // // Hilbert curve. // // // // The routine hilbert_sort3() sorts a set of 3D points along the 3D Hilbert // // curve. It recursively splits a point set according to the Hilbert indices // // mapped to the subboxes of the bounding box of the point set. The Hilbert // // indices is calculated by Butz's algorithm in 1971. An excellent exposition // // of this algorithm can be found in the paper of Hamilton, C., "Compact // // Hilbert Indices", Technical Report CS-2006-07, Computer Science, Dalhousie // // University, 2006 (the Section 2). My implementation also referenced Steven // // Witham's performance of "Hilbert walk" (hopefully, it is still available // // at http://www.tiac.net/~sw/2008/10/Hilbert/). // // // // TetGen sorts the points using the method in the paper of Boissonnat,J.-D., // // Devillers, O. and Hornus, S. "Incremental Construction of the Delaunay // // Triangulation and the Delaunay Graph in Medium Dimension," In Proceedings // // of the 25th ACM Symposium on Computational Geometry, 2009. It first // // randomly sorts the points into subgroups using the Biased Randomized // // Insertion Ordering (BRIO) of Amenta et al 2003, then sorts the points in // // each subgroup along the 3D Hilbert curve. Inserting points in this order // // ensure a randomized "sprinkling" of the points over the domain, while // // sorting of each subset provides locality. // // // //============================================================================// void transfernodes(); // Point sorting. int transgc[8][3][8], tsb1mod3[8]; void hilbert_init(int n); int hilbert_split(point* vertexarray, int arraysize, int gc0, int gc1, REAL, REAL, REAL, REAL, REAL, REAL); void hilbert_sort3(point* vertexarray, int arraysize, int e, int d, REAL, REAL, REAL, REAL, REAL, REAL, int depth); void brio_multiscale_sort(point*,int,int threshold,REAL ratio,int* depth); // Point location. unsigned long randomnation(unsigned int choices); void randomsample(point searchpt, triface *searchtet); enum locateresult locate(point searchpt, triface *searchtet, int chkencflag = 0); // Incremental Delaunay construction. enum locateresult locate_dt(point searchpt, triface *searchtet); int insert_vertex_bw(point, triface*, insertvertexflags*); void initialdelaunay(point pa, point pb, point pc, point pd); void incrementaldelaunay(clock_t&); //============================================================================// // // // Surface triangulation // // // //============================================================================// void flipshpush(face*); void flip22(face*, int, int); void flip31(face*, int); long lawsonflip(); int sinsertvertex(point newpt, face*, face*, int iloc, int bowywat, int); int sremovevertex(point delpt, face*, face*, int lawson); enum locateresult slocate(point, face*, int, int, int); enum interresult sscoutsegment(face*, point, int, int, int); void scarveholes(int, REAL*); int triangulate(int, arraypool*, arraypool*, int, REAL*); void unifysegments(); void identifyinputedges(point*); void mergefacets(); void meshsurface(); //============================================================================// // // // Constrained Delaunay tetrahedralization // // // // A constrained Delaunay tetrahedralization (CDT) is a variation of a Delau- // // nay tetrahedralization (DT) that respects the boundary of a 3D PLC (mesh // // domain). A crucial difference between a CDT and a DT is that triangles in // // the PLC's polygons are not required to be locally Delaunay, which frees // // the CDT to respect the PLC's polygons better. CDTs have optimal properties // // similar to those of DTs. // // // // Steiner Points and Steiner CDTs. It is well-known that even a simple 3D // // polyhedron may not have a tetrahedralization which only uses its vertices. // // Some extra points, so-called "Steiner points" are needed to form a tetrah- // // edralization of such polyhedron. A Steiner CDT of a 3D PLC is a CDT // // containing Steiner points. TetGen generates Steiner CDTs. // // // // The routine constraineddelaunay() creates a (Steiner) CDT of the PLC // // (including Steiner points). It has two steps, (1) segment recovery and (2) // // facet (polygon) recovery. // // // // The routine delaunizesegments() implements the segment recovery algorithm // // of Si, H., and Gaertner, K. "Meshing Piecewise Linear Complexes by // // Constrained Delaunay Tetrahedralizations," In Proceedings of the 14th // // International Meshing Roundtable, 147--163, 2005. It adds Steiner points // // into non-Delaunay segments until all subsegments appear together in a DT. // // The running time of this algorithm is proportional to the number of // // Steiner points. // // // // There are two incremental facet recovery algorithms: the cavity re- // // triangulation algorithm of Si, H., and Gaertner, K. "3D Boundary Recovery // // by Constrained Delaunay Tetrahedralization," International Journal for // // Numerical Methods in Engineering, 85:1341-1364, 2011, and the flip // // algorithm of Shewchuk, J. "Updating and Constructing Constrained Delaunay // // and Constrained Regular Triangulations by Flips." In Proceedings of the // // 19th ACM Symposium on Computational Geometry, 86-95, 2003. // // // // Although no Steiner point is needed in step (2), a facet with non-coplanar // // vertices might need Steiner points. It is discussed in the paper of Si, H.,// // and Shewchuk, J., "Incrementally Constructing and Updating Constrained // // Delaunay Tetrahedralizations with Finite Precision Coordinates." In // // Proceedings of the 21th International Meshing Roundtable, 2012. // // // // Our implementation of the facet recovery algorithms recovers a "missing // // region" at a time. Each missing region is a subset of connected interiors // // of a polygon. The routine formcavity() creates the cavity of crossing // // tetrahedra of the missing region. The cavity re-triangulation algorithm is // // implemented by three subroutines, delaunizecavity(), fillcavity(), and // // carvecavity(). Since it may fail due to non-coplanar vertices, the // // subroutine restorecavity() is used to restore the original cavity. // // // // The routine flipinsertfacet() implements the flip algorithm. The sub- // // routine flipcertify() is used to maintain the priority queue of flips. // // The routine refineregion() is called when the facet recovery algorithm // // fails to recover a missing region. It inserts Steiner points to refine the // // missing region. To avoid inserting Steiner points very close to existing // // segments. The classical encroachment rules of the Delaunay refinement // // algorithm are used to choose the Steiner points. The routine // // constrainedfacets() does the facet recovery by using either the cavity re- // // triangulation algorithm (default) or the flip algorithm. It results in a // // CDT of the (modified) PLC (including Steiner points). // // // //============================================================================// enum interresult finddirection(triface* searchtet, point endpt); enum interresult scoutsegment(point, point, face*, triface*, point*, arraypool*); int getsteinerptonsegment(face* seg, point refpt, point steinpt); void delaunizesegments(); int scoutsubface(face* searchsh,triface* searchtet,int shflag); void formregion(face*, arraypool*, arraypool*, arraypool*); int scoutcrossedge(triface& crosstet, arraypool*, arraypool*); bool formcavity(triface*, arraypool*, arraypool*, arraypool*, arraypool*, arraypool*, arraypool*); // Facet recovery by cavity re-triangulation [Si and Gaertner 2011]. void delaunizecavity(arraypool*, arraypool*, arraypool*, arraypool*, arraypool*, arraypool*); bool fillcavity(arraypool*, arraypool*, arraypool*, arraypool*, arraypool*, arraypool*, triface* crossedge); void carvecavity(arraypool*, arraypool*, arraypool*); void restorecavity(arraypool*, arraypool*, arraypool*, arraypool*); // Facet recovery by flips [Shewchuk 2003]. void flipcertify(triface *chkface, badface **pqueue, point, point, point); void flipinsertfacet(arraypool*, arraypool*, arraypool*, arraypool*); int insertpoint_cdt(point, triface*, face*, face*, insertvertexflags*, arraypool*, arraypool*, arraypool*, arraypool*, arraypool*, arraypool*); void refineregion(face&, arraypool*, arraypool*, arraypool*, arraypool*, arraypool*, arraypool*); void constrainedfacets(); void constraineddelaunay(clock_t&); //============================================================================// // // // Constrained tetrahedralizations. // // // //============================================================================// void sort_2pts(point p1, point p2, point ppt[2]); void sort_3pts(point p1, point p2, point p3, point ppt[3]); bool is_collinear_at(point mid, point left, point right); bool is_segment(point p1, point p2); bool valid_constrained_f23(triface&, point pd, point pe); bool valid_constrained_f32(triface*, point pa, point pb); int checkflipeligibility(int fliptype, point, point, point, point, point, int level, int edgepivot, flipconstraints* fc); int removeedgebyflips(triface*, flipconstraints*); int removefacebyflips(triface*, flipconstraints*); int recoveredgebyflips(point, point, face*, triface*, int fullsearch, int& idir); int add_steinerpt_in_schoenhardtpoly(triface*, int, int, int chkencflag); int add_steinerpt_in_segment(face*, int searchlevel, int& idir); int add_steinerpt_to_recover_edge(point, point, face*, int, int, int& idir); int recoversegments(arraypool*, int fullsearch, int steinerflag); int recoverfacebyflips(point,point,point,face*,triface*,int&,point*,point*); int recoversubfaces(arraypool*, int steinerflag); int getvertexstar(int, point searchpt, arraypool*, arraypool*, arraypool*); int getedge(point, point, triface*); int reduceedgesatvertex(point startpt, arraypool* endptlist); int removevertexbyflips(point steinerpt); int smoothpoint(point smtpt, arraypool*, int ccw, optparameters *opm); int suppressbdrysteinerpoint(point steinerpt); int suppresssteinerpoints(); void recoverboundary(clock_t&); //============================================================================// // // // Mesh reconstruction // // // //============================================================================// void carveholes(); void reconstructmesh(); int search_face(point p0, point p1, point p2, triface &tetloop); int search_edge(point p0, point p1, triface &tetloop); int scout_point(point, triface*, int randflag); REAL getpointmeshsize(point, triface*, int iloc); void interpolatemeshsize(); void insertconstrainedpoints(point *insertarray, int arylen, int rejflag); void insertconstrainedpoints(tetgenio *addio); void collectremovepoints(arraypool *remptlist); void meshcoarsening(); //============================================================================// // // // Mesh refinement // // // // The purpose of mesh refinement is to obtain a tetrahedral mesh with well- // // -shaped tetrahedra and appropriate mesh size. It is necessary to insert // // new Steiner points to achieve this property. The questions are (1) how to // // choose the Steiner points? and (2) how to insert them? // // // // Delaunay refinement is a technique first developed by Chew [1989] and // // Ruppert [1993, 1995] to generate quality triangular meshes in the plane. // // It provides guarantee on the smallest angle of the triangles. Rupper's // // algorithm guarantees that the mesh is size-optimal (to within a constant // // factor) among all meshes with the same quality. // // Shewchuk generalized Ruppert's algorithm into 3D in his PhD thesis // // [Shewchuk 1997]. A short version of his algorithm appears in "Tetrahedral // // Mesh Generation by Delaunay Refinement," In Proceedings of the 14th ACM // // Symposium on Computational Geometry, 86-95, 1998. It guarantees that all // // tetrahedra of the output mesh have a "radius-edge ratio" (equivalent to // // the minimal face angle) bounded. However, it does not remove slivers, a // // type of very flat tetrahedra which can have no small face angles but have // // very small (and large) dihedral angles. Moreover, it may not terminate if // // the input PLC contains "sharp features", e.g., two edges (or two facets) // // meet at an acute angle (or dihedral angle). // // // // TetGen uses the basic Delaunay refinement scheme to insert Steiner points. // // While it always maintains a constrained Delaunay mesh. The algorithm is // // described in Si, H., "Adaptive Constrained Delaunay Mesh Generation," // // International Journal for Numerical Methods in Engineering, 75:856-880. // // This algorithm always terminates and sharp features are easily preserved. // // The mesh has good quality (same as Shewchuk's Delaunay refinement algori- // // thm) in the bulk of the mesh domain. Moreover, it supports the generation // // of adaptive mesh according to a (isotropic) mesh sizing function. // // // //============================================================================// void makesegmentendpointsmap(); REAL set_ridge_vertex_protecting_ball(point); REAL get_min_angle_at_ridge_vertex(face* seg); REAL get_min_diahedral_angle(face* seg); void create_segment_info_list(); void makefacetverticesmap(); void create_segment_facet_map(); int ridge_vertices_adjacent(point, point); int facet_ridge_vertex_adjacent(face *, point); int segsegadjacent(face *, face *); int segfacetadjacent(face *checkseg, face *checksh); int facetfacetadjacent(face *, face *); bool is_sharp_segment(face* seg); bool does_seg_contain_acute_vertex(face* seg); bool create_a_shorter_edge(point steinerpt, point nearpt); void enqueuesubface(memorypool*, face*); void enqueuetetrahedron(triface*); bool check_encroachment(point pa, point pb, point checkpt); bool check_enc_segment(face *chkseg, point *pencpt); bool get_steiner_on_segment(face* seg, point encpt, point newpt); bool split_segment(face *splitseg, point encpt, REAL *param, int qflag, int, int*); void repairencsegs(REAL *param, int qflag, int chkencflag); bool get_subface_ccent(face *chkfac, REAL *ccent); bool check_enc_subface(face *chkfac, point *pencpt, REAL *ccent, REAL *radius); bool check_subface(face *chkfac, REAL *ccent, REAL radius, REAL *param); void enqueue_subface(face *bface, point encpt, REAL *ccent, REAL *param); badface* top_subface(); void dequeue_subface(); void parallel_shift(point pa, point pb, point pc, point pt, REAL* ppt); enum locateresult locate_on_surface(point searchpt, face* searchsh); bool split_subface(face *splitfac, point encpt, REAL *ccent, REAL*, int, int, int*); void repairencfacs(REAL *param, int qflag, int chkencflag); bool check_tetrahedron(triface *chktet, REAL* param, int& qflag); bool checktet4split(triface *chktet, REAL* param, int& qflag); enum locateresult locate_point_walk(point searchpt, triface*, int chkencflag); bool split_tetrahedron(triface*, REAL*, int, int, insertvertexflags &ivf); void repairbadtets(REAL queratio, int chkencflag); void delaunayrefinement(); //============================================================================// // // // Mesh optimization // // // //============================================================================// long lawsonflip3d(flipconstraints *fc); void recoverdelaunay(); int get_seg_laplacian_center(point mesh_vert, REAL target[3]); int get_surf_laplacian_center(point mesh_vert, REAL target[3]); int get_laplacian_center(point mesh_vert, REAL target[3]); bool move_vertex(point mesh_vert, REAL target[3]); void smooth_vertices(); bool get_tet(point, point, point, point, triface *); bool get_tetqual(triface *chktet, point oppo_pt, badface *bf); bool get_tetqual(point, point, point, point, badface *bf); void enqueue_badtet(badface *bf); badface* top_badtet(); void dequeue_badtet(); bool add_steinerpt_to_repair(badface *bf, bool bSmooth); bool flip_edge_to_improve(triface *sliver_edge, REAL& improved_cosmaxd); bool repair_tet(badface *bf, bool bFlips, bool bSmooth, bool bSteiners); long repair_badqual_tets(bool bFlips, bool bSmooth, bool bSteiners); void improve_mesh(); //============================================================================// // // // Mesh check and statistics // // // //============================================================================// // Mesh validations. int check_mesh(int topoflag); int check_shells(); int check_segments(); int check_delaunay(int perturb = 1); int check_regular(int); int check_conforming(int); // Mesh statistics. void printfcomma(unsigned long n); void qualitystatistics(); void memorystatistics(); void statistics(); //============================================================================// // // // Mesh output // // // //============================================================================// void jettisonnodes(); void highorder(); void indexelements(); void numberedges(); void outnodes(tetgenio*); void outmetrics(tetgenio*); void outelements(tetgenio*); void outfaces(tetgenio*); void outhullfaces(tetgenio*); void outsubfaces(tetgenio*); void outedges(tetgenio*); void outsubsegments(tetgenio*); void outneighbors(tetgenio*); void outvoronoi(tetgenio*); void outsmesh(char*); void outmesh2medit(char*); void outmesh2vtk(char*, int); void out_surfmesh_vtk(char*, int); void out_intersected_facets(); //============================================================================// // // // Constructor & destructor // // // //============================================================================// void initializetetgenmesh() { in = addin = NULL; b = NULL; bgm = NULL; tetrahedrons = subfaces = subsegs = points = NULL; tet2segpool = tet2subpool = NULL; dummypoint = NULL; badtetrahedrons = badsubfacs = badsubsegs = NULL; split_segments_pool = split_subfaces_pool = NULL; unsplit_badtets = unsplit_subfaces = unsplit_segments = NULL; check_tets_list = NULL; badqual_tets_pool = NULL; stack_enc_segments = stack_enc_subfaces = NULL; flippool = NULL; flipstack = unflip_queue_front = unflip_queue_tail = NULL; later_unflip_queue = unflipqueue = NULL; cavetetlist = cavebdrylist = caveoldtetlist = NULL; cave_oldtet_list = NULL; cavetetshlist = cavetetseglist = cavetetvertlist = NULL; caveencshlist = caveencseglist = NULL; caveshlist = caveshbdlist = cavesegshlist = NULL; subsegstack = subfacstack = subvertstack = NULL; skipped_segment_list = skipped_facet_list = NULL; encseglist = encshlist = NULL; number_of_facets = 0; idx2facetlist = NULL; facetverticeslist = NULL; idx_segment_facet_list = NULL; segment_facet_list = NULL; idx_ridge_vertex_facet_list = NULL; ridge_vertex_facet_list = NULL; segmentendpointslist_length = 0; segmentendpointslist = NULL; segment_info_list = NULL; idx_segment_ridge_vertex_list = NULL; segment_ridge_vertex_list = NULL; subdomains = 0; subdomain_markers = NULL; numpointattrib = numelemattrib = 0; sizeoftensor = 0; pointmtrindex = 0; pointparamindex = 0; pointmarkindex = 0; point2simindex = 0; pointinsradiusindex = 0; elemattribindex = 0; polarindex = 0; volumeboundindex = 0; shmarkindex = 0; areaboundindex = 0; checksubsegflag = 0; checksubfaceflag = 0; boundary_recovery_flag = 0; checkconstraints = 0; nonconvex = 0; autofliplinklevel = 1; useinsertradius = 0; samples = 0l; randomseed = 1l; minfaceang = minfacetdihed = PI; cos_facet_separate_ang_tol = cos(179.9/180.*PI); cos_collinear_ang_tol = cos(179.9/180.*PI); tetprism_vol_sum = 0.0; longest = minedgelength = 0.0; xmax = xmin = ymax = ymin = zmax = zmin = 0.0; smallest_insradius = 1.e+30; big_radius_edge_ratio = 100.0; elem_limit = 0; insert_point_count = 0l; report_refine_progress = 0l; last_point_count = 0l; last_insertion_count = 0l; insegments = 0l; hullsize = 0l; meshedges = meshhulledges = 0l; steinerleft = -1; dupverts = 0l; unuverts = 0l; duplicated_facets_count = 0l; nonregularcount = 0l; st_segref_count = st_facref_count = st_volref_count = 0l; fillregioncount = cavitycount = cavityexpcount = 0l; flip14count = flip26count = flipn2ncount = 0l; flip23count = flip32count = flip44count = flip41count = 0l; flip22count = flip31count = 0l; recover_delaunay_count = 0l; opt_flips_count = opt_collapse_count = opt_smooth_count = 0l; totalworkmemory = 0l; } // tetgenmesh() void freememory() { if (bgm != NULL) { delete bgm; } if (points != (memorypool *) NULL) { delete points; delete [] dummypoint; } if (tetrahedrons != (memorypool *) NULL) { delete tetrahedrons; } if (subfaces != (memorypool *) NULL) { delete subfaces; delete subsegs; } if (tet2segpool != NULL) { delete tet2segpool; delete tet2subpool; } if (badtetrahedrons) { delete badtetrahedrons; } if (badsubfacs) { delete badsubfacs; } if (badsubsegs) { delete badsubsegs; } if (unsplit_badtets) { delete unsplit_badtets; } if (check_tets_list) { delete check_tets_list; } if (flippool != NULL) { delete flippool; delete later_unflip_queue; delete unflipqueue; } if (cavetetlist != NULL) { delete cavetetlist; delete cavebdrylist; delete caveoldtetlist; delete cavetetvertlist; delete cave_oldtet_list; } if (caveshlist != NULL) { delete caveshlist; delete caveshbdlist; delete cavesegshlist; delete cavetetshlist; delete cavetetseglist; delete caveencshlist; delete caveencseglist; } if (subsegstack != NULL) { delete subsegstack; delete subfacstack; delete subvertstack; } if (idx2facetlist != NULL) { delete [] idx2facetlist; delete [] facetverticeslist; delete [] idx_segment_facet_list; delete [] segment_facet_list; delete [] idx_ridge_vertex_facet_list; delete [] ridge_vertex_facet_list; } if (segmentendpointslist != NULL) { delete [] segmentendpointslist; delete [] idx_segment_ridge_vertex_list; delete [] segment_ridge_vertex_list; } if (segment_info_list != NULL) { delete [] segment_info_list; } if (subdomain_markers != NULL) { delete [] subdomain_markers; } initializetetgenmesh(); } tetgenmesh() { initializetetgenmesh(); } ~tetgenmesh() { freememory(); } // ~tetgenmesh() }; // End of class tetgenmesh. //============================================================================// // // // tetrahedralize() Interface for using TetGen's library to generate // // Delaunay tetrahedralizations, constrained Delaunay // // tetrahedralizations, quality tetrahedral meshes. // // // // 'in' is an object of 'tetgenio' containing a PLC or a previously generated // // tetrahedral mesh you want to refine. 'out' is another object of 'tetgenio'// // for returing the generated tetrahedral mesh. If it is a NULL pointer, the // // output mesh is saved to file(s). If 'bgmin' != NULL, it contains a back- // // ground mesh defining a mesh size function. // // // //============================================================================// void tetrahedralize(tetgenbehavior *b, tetgenio *in, tetgenio *out, tetgenio *addin = NULL, tetgenio *bgmin = NULL); #ifdef TETLIBRARY void tetrahedralize(char *switches, tetgenio *in, tetgenio *out, tetgenio *addin = NULL, tetgenio *bgmin = NULL); #endif // #ifdef TETLIBRARY //============================================================================// // // // terminatetetgen() Terminate TetGen with a given exit code. // // // //============================================================================// inline void terminatetetgen(tetgenmesh *m, int x) { #ifdef TETLIBRARY throw x; #else switch (x) { case 1: // Out of memory. printf("Error: Out of memory.\n"); break; case 2: // Encounter an internal error. printf("Please report this bug to Hang.Si@wias-berlin.de. Include\n"); printf(" the message above, your input data set, and the exact\n"); printf(" command line you used to run this program, thank you.\n"); break; case 3: printf("The input surface mesh contain self-intersections. Program stopped.\n"); //printf("Hint: use -d option to detect all self-intersections.\n"); break; case 4: printf("A very small input feature size was detected. Program stopped.\n"); if (m) { printf("Hint: use -T option to set a smaller tolerance. Current is %g\n", m->b->epsilon); } break; case 5: printf("Two very close input facets were detected. Program stopped.\n"); printf("Hint: use -Y option to avoid adding Steiner points in boundary.\n"); break; case 10: printf("An input error was detected. Program stopped.\n"); break; case 200: printf("Boundary contains Steiner points (-YY option). Program stopped.\n"); break; } // switch (x) exit(x); #endif // #ifdef TETLIBRARY } //============================================================================// // // // Primitives for tetrahedra // // // //============================================================================// // encode() compress a handle into a single pointer. It relies on the // assumption that all addresses of tetrahedra are aligned to sixteen- // byte boundaries, so that the last four significant bits are zero. inline tetgenmesh::tetrahedron tetgenmesh::encode(triface& t) { return (tetrahedron) ((uintptr_t) (t).tet | (uintptr_t) (t).ver); } inline tetgenmesh::tetrahedron tetgenmesh::encode2(tetrahedron* ptr, int ver) { return (tetrahedron) ((uintptr_t) (ptr) | (uintptr_t) (ver)); } // decode() converts a pointer to a handle. The version is extracted from // the four least significant bits of the pointer. inline void tetgenmesh::decode(tetrahedron ptr, triface& t) { (t).ver = (int) ((uintptr_t) (ptr) & (uintptr_t) 15); (t).tet = (tetrahedron *) ((uintptr_t) (ptr) ^ (uintptr_t) (t).ver); } inline tetgenmesh::tetrahedron* tetgenmesh::decode_tet_only(tetrahedron ptr) { return (tetrahedron *) ((((uintptr_t) ptr) >> 4) << 4); } inline int tetgenmesh::decode_ver_only(tetrahedron ptr) { return (int) ((uintptr_t) (ptr) & (uintptr_t) 15); } // bond() connects two tetrahedra together. (t1,v1) and (t2,v2) must // refer to the same face and the same edge. inline void tetgenmesh::bond(triface& t1, triface& t2) { t1.tet[t1.ver & 3] = encode2(t2.tet, bondtbl[t1.ver][t2.ver]); t2.tet[t2.ver & 3] = encode2(t1.tet, bondtbl[t2.ver][t1.ver]); } // dissolve() a bond (from one side). inline void tetgenmesh::dissolve(triface& t) { t.tet[t.ver & 3] = NULL; } // enext() finds the next edge (counterclockwise) in the same face. inline void tetgenmesh::enext(triface& t1, triface& t2) { t2.tet = t1.tet; t2.ver = enexttbl[t1.ver]; // (t1.ver + 4) % 12; } inline void tetgenmesh::enextself(triface& t) { t.ver = enexttbl[t.ver]; // (t.ver + 4) % 12; } // eprev() finds the next edge (clockwise) in the same face. inline void tetgenmesh::eprev(triface& t1, triface& t2) { t2.tet = t1.tet; t2.ver = eprevtbl[t1.ver]; // (t1.ver + 8) % 12; } inline void tetgenmesh::eprevself(triface& t) { t.ver = eprevtbl[t.ver]; // (t.ver + 8) % 12; } // esym() finds the reversed edge. It is in the other face of the // same tetrahedron. inline void tetgenmesh::esym(triface& t1, triface& t2) { (t2).tet = (t1).tet; (t2).ver = esymtbl[(t1).ver]; } inline void tetgenmesh::esymself(triface& t) { (t).ver = esymtbl[(t).ver]; } // enextesym() finds the reversed edge of the next edge. It is in the other // face of the same tetrahedron. It is the combination esym() * enext(). inline void tetgenmesh::enextesym(triface& t1, triface& t2) { t2.tet = t1.tet; t2.ver = enextesymtbl[t1.ver]; } inline void tetgenmesh::enextesymself(triface& t) { t.ver = enextesymtbl[t.ver]; } // eprevesym() finds the reversed edge of the previous edge. inline void tetgenmesh::eprevesym(triface& t1, triface& t2) { t2.tet = t1.tet; t2.ver = eprevesymtbl[t1.ver]; } inline void tetgenmesh::eprevesymself(triface& t) { t.ver = eprevesymtbl[t.ver]; } // eorgoppo() Finds the opposite face of the origin of the current edge. // Return the opposite edge of the current edge. inline void tetgenmesh::eorgoppo(triface& t1, triface& t2) { t2.tet = t1.tet; t2.ver = eorgoppotbl[t1.ver]; } inline void tetgenmesh::eorgoppoself(triface& t) { t.ver = eorgoppotbl[t.ver]; } // edestoppo() Finds the opposite face of the destination of the current // edge. Return the opposite edge of the current edge. inline void tetgenmesh::edestoppo(triface& t1, triface& t2) { t2.tet = t1.tet; t2.ver = edestoppotbl[t1.ver]; } inline void tetgenmesh::edestoppoself(triface& t) { t.ver = edestoppotbl[t.ver]; } // fsym() finds the adjacent tetrahedron at the same face and the same edge. inline void tetgenmesh::fsym(triface& t1, triface& t2) { decode((t1).tet[(t1).ver & 3], t2); t2.ver = fsymtbl[t1.ver][t2.ver]; } #define fsymself(t) \ t1ver = (t).ver; \ decode((t).tet[(t).ver & 3], (t));\ (t).ver = fsymtbl[t1ver][(t).ver] // fnext() finds the next face while rotating about an edge according to // a right-hand rule. The face is in the adjacent tetrahedron. It is // the combination: fsym() * esym(). inline void tetgenmesh::fnext(triface& t1, triface& t2) { decode(t1.tet[facepivot1[t1.ver]], t2); t2.ver = facepivot2[t1.ver][t2.ver]; } #define fnextself(t) \ t1ver = (t).ver; \ decode((t).tet[facepivot1[(t).ver]], (t)); \ (t).ver = facepivot2[t1ver][(t).ver] // The following primtives get or set the origin, destination, face apex, // or face opposite of an ordered tetrahedron. inline tetgenmesh::point tetgenmesh::org(triface& t) { return (point) (t).tet[orgpivot[(t).ver]]; } inline tetgenmesh::point tetgenmesh:: dest(triface& t) { return (point) (t).tet[destpivot[(t).ver]]; } inline tetgenmesh::point tetgenmesh:: apex(triface& t) { return (point) (t).tet[apexpivot[(t).ver]]; } inline tetgenmesh::point tetgenmesh:: oppo(triface& t) { return (point) (t).tet[oppopivot[(t).ver]]; } inline void tetgenmesh:: setorg(triface& t, point p) { (t).tet[orgpivot[(t).ver]] = (tetrahedron) (p); } inline void tetgenmesh:: setdest(triface& t, point p) { (t).tet[destpivot[(t).ver]] = (tetrahedron) (p); } inline void tetgenmesh:: setapex(triface& t, point p) { (t).tet[apexpivot[(t).ver]] = (tetrahedron) (p); } inline void tetgenmesh:: setoppo(triface& t, point p) { (t).tet[oppopivot[(t).ver]] = (tetrahedron) (p); } #define setvertices(t, torg, tdest, tapex, toppo) \ (t).tet[orgpivot[(t).ver]] = (tetrahedron) (torg);\ (t).tet[destpivot[(t).ver]] = (tetrahedron) (tdest); \ (t).tet[apexpivot[(t).ver]] = (tetrahedron) (tapex); \ (t).tet[oppopivot[(t).ver]] = (tetrahedron) (toppo) inline REAL* tetgenmesh::get_polar(tetrahedron* ptr) { return &(((REAL *) (ptr))[polarindex]); } inline REAL tetgenmesh::get_volume(tetrahedron* ptr) { return ((REAL *) (ptr))[polarindex + 4]; } // Check or set a tetrahedron's attributes. inline REAL tetgenmesh::elemattribute(tetrahedron* ptr, int attnum) { return ((REAL *) (ptr))[elemattribindex + attnum]; } inline void tetgenmesh::setelemattribute(tetrahedron* ptr, int attnum, REAL value) { ((REAL *) (ptr))[elemattribindex + attnum] = value; } // Check or set a tetrahedron's maximum volume bound. inline REAL tetgenmesh::volumebound(tetrahedron* ptr) { return ((REAL *) (ptr))[volumeboundindex]; } inline void tetgenmesh::setvolumebound(tetrahedron* ptr, REAL value) { ((REAL *) (ptr))[volumeboundindex] = value; } // Get or set a tetrahedron's index (only used for output). // These two routines use the reserved slot ptr[10]. inline int tetgenmesh::elemindex(tetrahedron* ptr) { int *iptr = (int *) &(ptr[10]); return iptr[0]; } inline void tetgenmesh::setelemindex(tetrahedron* ptr, int value) { int *iptr = (int *) &(ptr[10]); iptr[0] = value; } // Get or set a tetrahedron's marker. // Set 'value = 0' cleans all the face/edge flags. inline int tetgenmesh::elemmarker(tetrahedron* ptr) { return ((int *) (ptr))[elemmarkerindex]; } inline void tetgenmesh::setelemmarker(tetrahedron* ptr, int value) { ((int *) (ptr))[elemmarkerindex] = value; } // infect(), infected(), uninfect() -- primitives to flag or unflag a // tetrahedron. The last bit of the element marker is flagged (1) // or unflagged (0). inline void tetgenmesh::infect(triface& t) { ((int *) (t.tet))[elemmarkerindex] |= 1; } inline void tetgenmesh::uninfect(triface& t) { ((int *) (t.tet))[elemmarkerindex] &= ~1; } inline bool tetgenmesh::infected(triface& t) { return (((int *) (t.tet))[elemmarkerindex] & 1) != 0; } // marktest(), marktested(), unmarktest() -- primitives to flag or unflag a // tetrahedron. Use the second lowerest bit of the element marker. inline void tetgenmesh::marktest(triface& t) { ((int *) (t.tet))[elemmarkerindex] |= 2; } inline void tetgenmesh::unmarktest(triface& t) { ((int *) (t.tet))[elemmarkerindex] &= ~2; } inline bool tetgenmesh::marktested(triface& t) { return (((int *) (t.tet))[elemmarkerindex] & 2) != 0; } // markface(), unmarkface(), facemarked() -- primitives to flag or unflag a // face of a tetrahedron. From the last 3rd to 6th bits are used for // face markers, e.g., the last third bit corresponds to loc = 0. inline void tetgenmesh::markface(triface& t) { ((int *) (t.tet))[elemmarkerindex] |= (4 << (t.ver & 3)); } inline void tetgenmesh::unmarkface(triface& t) { ((int *) (t.tet))[elemmarkerindex] &= ~(4 << (t.ver & 3)); } inline bool tetgenmesh::facemarked(triface& t) { return (((int *) (t.tet))[elemmarkerindex] & (4 << (t.ver & 3))) != 0; } // markedge(), unmarkedge(), edgemarked() -- primitives to flag or unflag an // edge of a tetrahedron. From the last 7th to 12th bits are used for // edge markers, e.g., the last 7th bit corresponds to the 0th edge, etc. // Remark: The last 7th bit is marked by 2^6 = 64. inline void tetgenmesh::markedge(triface& t) { ((int *) (t.tet))[elemmarkerindex] |= (int) (64 << ver2edge[(t).ver]); } inline void tetgenmesh::unmarkedge(triface& t) { ((int *) (t.tet))[elemmarkerindex] &= ~(int) (64 << ver2edge[(t).ver]); } inline bool tetgenmesh::edgemarked(triface& t) { return (((int *) (t.tet))[elemmarkerindex] & (int) (64 << ver2edge[(t).ver])) != 0; } // marktest2(), unmarktest2(), marktest2ed() -- primitives to flag and unflag // a tetrahedron. The 13th bit (2^12 = 4096) is used for this flag. inline void tetgenmesh::marktest2(triface& t) { ((int *) (t.tet))[elemmarkerindex] |= (int) (4096); } inline void tetgenmesh::unmarktest2(triface& t) { ((int *) (t.tet))[elemmarkerindex] &= ~(int) (4096); } inline bool tetgenmesh::marktest2ed(triface& t) { return (((int *) (t.tet))[elemmarkerindex] & (int) (4096)) != 0; } // elemcounter(), setelemcounter() -- primitives to read or ser a (small) // integer counter in this tet. It is saved from the 16th bit. On 32 bit // system, the range of the counter is [0, 2^15 = 32768]. inline int tetgenmesh::elemcounter(triface& t) { return (((int *) (t.tet))[elemmarkerindex]) >> 16; } inline void tetgenmesh::setelemcounter(triface& t, int value) { int c = ((int *) (t.tet))[elemmarkerindex]; // Clear the old counter while keep the other flags. c &= 65535; // sum_{i=0^15} 2^i c |= (value << 16); ((int *) (t.tet))[elemmarkerindex] = c; } inline void tetgenmesh::increaseelemcounter(triface& t) { int c = elemcounter(t); setelemcounter(t, c + 1); } inline void tetgenmesh::decreaseelemcounter(triface& t) { int c = elemcounter(t); setelemcounter(t, c - 1); } // ishulltet() tests if t is a hull tetrahedron. inline bool tetgenmesh::ishulltet(triface& t) { return (point) (t).tet[7] == dummypoint; } // isdeadtet() tests if t is a tetrahedron is dead. inline bool tetgenmesh::isdeadtet(triface& t) { return ((t.tet == NULL) || (t.tet[4] == NULL)); } //============================================================================// // // // Primitives for subfaces and subsegments // // // //============================================================================// // Each subface contains three pointers to its neighboring subfaces, with // edge versions. To save memory, both information are kept in a single // pointer. To make this possible, all subfaces are aligned to eight-byte // boundaries, so that the last three bits of each pointer are zeros. An // edge version (in the range 0 to 5) is compressed into the last three // bits of each pointer by 'sencode()'. 'sdecode()' decodes a pointer, // extracting an edge version and a pointer to the beginning of a subface. inline void tetgenmesh::sdecode(shellface sptr, face& s) { s.shver = (int) ((uintptr_t) (sptr) & (uintptr_t) 7); s.sh = (shellface *) ((uintptr_t) (sptr) ^ (uintptr_t) (s.shver)); } inline tetgenmesh::shellface tetgenmesh::sencode(face& s) { return (shellface) ((uintptr_t) s.sh | (uintptr_t) s.shver); } inline tetgenmesh::shellface tetgenmesh::sencode2(shellface *sh, int shver) { return (shellface) ((uintptr_t) sh | (uintptr_t) shver); } // sbond() bonds two subfaces (s1) and (s2) together. s1 and s2 must refer // to the same edge. No requirement is needed on their orientations. inline void tetgenmesh::sbond(face& s1, face& s2) { s1.sh[s1.shver >> 1] = sencode(s2); s2.sh[s2.shver >> 1] = sencode(s1); } // sbond1() bonds s1 <== s2, i.e., after bonding, s1 is pointing to s2, // but s2 is not pointing to s1. s1 and s2 must refer to the same edge. // No requirement is needed on their orientations. inline void tetgenmesh::sbond1(face& s1, face& s2) { s1.sh[s1.shver >> 1] = sencode(s2); } // Dissolve a subface bond (from one side). Note that the other subface // will still think it's connected to this subface. inline void tetgenmesh::sdissolve(face& s) { s.sh[s.shver >> 1] = NULL; } // spivot() finds the adjacent subface (s2) for a given subface (s1). // s1 and s2 share at the same edge. inline void tetgenmesh::spivot(face& s1, face& s2) { shellface sptr = s1.sh[s1.shver >> 1]; sdecode(sptr, s2); } inline void tetgenmesh::spivotself(face& s) { shellface sptr = s.sh[s.shver >> 1]; sdecode(sptr, s); } // These primitives determine or set the origin, destination, or apex // of a subface with respect to the edge version. inline tetgenmesh::point tetgenmesh::sorg(face& s) { return (point) s.sh[sorgpivot[s.shver]]; } inline tetgenmesh::point tetgenmesh::sdest(face& s) { return (point) s.sh[sdestpivot[s.shver]]; } inline tetgenmesh::point tetgenmesh::sapex(face& s) { return (point) s.sh[sapexpivot[s.shver]]; } inline void tetgenmesh::setsorg(face& s, point pointptr) { s.sh[sorgpivot[s.shver]] = (shellface) pointptr; } inline void tetgenmesh::setsdest(face& s, point pointptr) { s.sh[sdestpivot[s.shver]] = (shellface) pointptr; } inline void tetgenmesh::setsapex(face& s, point pointptr) { s.sh[sapexpivot[s.shver]] = (shellface) pointptr; } #define setshvertices(s, pa, pb, pc)\ setsorg(s, pa);\ setsdest(s, pb);\ setsapex(s, pc) // sesym() reserves the direction of the lead edge. inline void tetgenmesh::sesym(face& s1, face& s2) { s2.sh = s1.sh; s2.shver = (s1.shver ^ 1); // Inverse the last bit. } inline void tetgenmesh::sesymself(face& s) { s.shver ^= 1; } // senext() finds the next edge (counterclockwise) in the same orientation // of this face. inline void tetgenmesh::senext(face& s1, face& s2) { s2.sh = s1.sh; s2.shver = snextpivot[s1.shver]; } inline void tetgenmesh::senextself(face& s) { s.shver = snextpivot[s.shver]; } inline void tetgenmesh::senext2(face& s1, face& s2) { s2.sh = s1.sh; s2.shver = snextpivot[snextpivot[s1.shver]]; } inline void tetgenmesh::senext2self(face& s) { s.shver = snextpivot[snextpivot[s.shver]]; } // Check or set a subface's maximum area bound. inline REAL tetgenmesh::areabound(face& s) { return ((REAL *) (s.sh))[areaboundindex]; } inline void tetgenmesh::setareabound(face& s, REAL value) { ((REAL *) (s.sh))[areaboundindex] = value; } // These two primitives read or set a shell marker. Shell markers are used // to hold user boundary information. inline int tetgenmesh::shellmark(face& s) { return ((int *) (s.sh))[shmarkindex]; } inline void tetgenmesh::setshellmark(face& s, int value) { ((int *) (s.sh))[shmarkindex] = value; } // sinfect(), sinfected(), suninfect() -- primitives to flag or unflag a // subface. The last bit of ((int *) ((s).sh))[shmarkindex+1] is flagged. inline void tetgenmesh::sinfect(face& s) { ((int *) ((s).sh))[shmarkindex+1] = (((int *) ((s).sh))[shmarkindex+1] | (int) 1); } inline void tetgenmesh::suninfect(face& s) { ((int *) ((s).sh))[shmarkindex+1] = (((int *) ((s).sh))[shmarkindex+1] & ~(int) 1); } // Test a subface for viral infection. inline bool tetgenmesh::sinfected(face& s) { return (((int *) ((s).sh))[shmarkindex+1] & (int) 1) != 0; } // smarktest(), smarktested(), sunmarktest() -- primitives to flag or unflag // a subface. The last 2nd bit of the integer is flagged. inline void tetgenmesh::smarktest(face& s) { ((int *) ((s).sh))[shmarkindex+1] = (((int *)((s).sh))[shmarkindex+1] | (int) 2); } inline void tetgenmesh::sunmarktest(face& s) { ((int *) ((s).sh))[shmarkindex+1] = (((int *)((s).sh))[shmarkindex+1] & ~(int)2); } inline bool tetgenmesh::smarktested(face& s) { return ((((int *) ((s).sh))[shmarkindex+1] & (int) 2) != 0); } // smarktest2(), smarktest2ed(), sunmarktest2() -- primitives to flag or // unflag a subface. The last 3rd bit of the integer is flagged. inline void tetgenmesh::smarktest2(face& s) { ((int *) ((s).sh))[shmarkindex+1] = (((int *)((s).sh))[shmarkindex+1] | (int) 4); } inline void tetgenmesh::sunmarktest2(face& s) { ((int *) ((s).sh))[shmarkindex+1] = (((int *)((s).sh))[shmarkindex+1] & ~(int)4); } inline bool tetgenmesh::smarktest2ed(face& s) { return ((((int *) ((s).sh))[shmarkindex+1] & (int) 4) != 0); } // The last 4th bit of ((int *) ((s).sh))[shmarkindex+1] is flagged. inline void tetgenmesh::smarktest3(face& s) { ((int *) ((s).sh))[shmarkindex+1] = (((int *)((s).sh))[shmarkindex+1] | (int) 8); } inline void tetgenmesh::sunmarktest3(face& s) { ((int *) ((s).sh))[shmarkindex+1] = (((int *)((s).sh))[shmarkindex+1] & ~(int)8); } inline bool tetgenmesh::smarktest3ed(face& s) { return ((((int *) ((s).sh))[shmarkindex+1] & (int) 8) != 0); } // Each facet has a unique index (automatically indexed). Starting from '0'. // We save this index in the same field of the shell type. inline void tetgenmesh::setfacetindex(face& s, int value) { ((int *) (s.sh))[shmarkindex + 2] = value; } inline int tetgenmesh::getfacetindex(face& s) { return ((int *) (s.sh))[shmarkindex + 2]; } // Tests if the subface (subsegment) s is dead. inline bool tetgenmesh::isdeadsh(face& s) { return ((s.sh == NULL) || (s.sh[3] == NULL)); } //============================================================================// // // // Primitives for interacting between tetrahedra and subfaces // // // //============================================================================// // tsbond() bond a tetrahedron (t) and a subface (s) together. // Note that t and s must be the same face and the same edge. Moreover, // t and s have the same orientation. // Since the edge number in t and in s can be any number in {0,1,2}. We bond // the edge in s which corresponds to t's 0th edge, and vice versa. inline void tetgenmesh::tsbond(triface& t, face& s) { if ((t).tet[9] == NULL) { // Allocate space for this tet. (t).tet[9] = (tetrahedron) tet2subpool->alloc(); // Initialize. for (int i = 0; i < 4; i++) { ((shellface *) (t).tet[9])[i] = NULL; } } // Bond t <== s. ((shellface *) (t).tet[9])[(t).ver & 3] = sencode2((s).sh, tsbondtbl[t.ver][s.shver]); // Bond s <== t. s.sh[9 + ((s).shver & 1)] = (shellface) encode2((t).tet, stbondtbl[t.ver][s.shver]); } // tspivot() finds a subface (s) abutting on the given tetrahdera (t). // Return s.sh = NULL if there is no subface at t. Otherwise, return // the subface s, and s and t must be at the same edge wth the same // orientation. inline void tetgenmesh::tspivot(triface& t, face& s) { if ((t).tet[9] == NULL) { (s).sh = NULL; return; } // Get the attached subface s. sdecode(((shellface *) (t).tet[9])[(t).ver & 3], (s)); (s).shver = tspivottbl[t.ver][s.shver]; } // Quickly check if the handle (t, v) is a subface. #define issubface(t) \ ((t).tet[9] && ((t).tet[9])[(t).ver & 3]) // stpivot() finds a tetrahedron (t) abutting a given subface (s). // Return the t (if it exists) with the same edge and the same // orientation of s. inline void tetgenmesh::stpivot(face& s, triface& t) { decode((tetrahedron) s.sh[9 + (s.shver & 1)], t); if ((t).tet == NULL) { return; } (t).ver = stpivottbl[t.ver][s.shver]; } // Quickly check if this subface is attached to a tetrahedron. #define isshtet(s) \ ((s).sh[9 + ((s).shver & 1)]) // tsdissolve() dissolve a bond (from the tetrahedron side). inline void tetgenmesh::tsdissolve(triface& t) { if ((t).tet[9] != NULL) { ((shellface *) (t).tet[9])[(t).ver & 3] = NULL; } } // stdissolve() dissolve a bond (from the subface side). inline void tetgenmesh::stdissolve(face& s) { (s).sh[9] = NULL; (s).sh[10] = NULL; } //============================================================================// // // // Primitives for interacting between subfaces and segments // // // //============================================================================// // ssbond() bond a subface to a subsegment. inline void tetgenmesh::ssbond(face& s, face& edge) { s.sh[6 + (s.shver >> 1)] = sencode(edge); edge.sh[0] = sencode(s); } inline void tetgenmesh::ssbond1(face& s, face& edge) { s.sh[6 + (s.shver >> 1)] = sencode(edge); //edge.sh[0] = sencode(s); } // ssdisolve() dissolve a bond (from the subface side) inline void tetgenmesh::ssdissolve(face& s) { s.sh[6 + (s.shver >> 1)] = NULL; } // sspivot() finds a subsegment abutting a subface. inline void tetgenmesh::sspivot(face& s, face& edge) { sdecode((shellface) s.sh[6 + (s.shver >> 1)], edge); } // Quickly check if the edge is a subsegment. #define isshsubseg(s) \ ((s).sh[6 + ((s).shver >> 1)]) //============================================================================// // // // Primitives for interacting between tetrahedra and segments // // // //============================================================================// inline void tetgenmesh::tssbond1(triface& t, face& s) { if ((t).tet[8] == NULL) { // Allocate space for this tet. (t).tet[8] = (tetrahedron) tet2segpool->alloc(); // Initialization. for (int i = 0; i < 6; i++) { ((shellface *) (t).tet[8])[i] = NULL; } } ((shellface *) (t).tet[8])[ver2edge[(t).ver]] = sencode((s)); } inline void tetgenmesh::sstbond1(face& s, triface& t) { ((tetrahedron *) (s).sh)[9] = encode(t); } inline void tetgenmesh::tssdissolve1(triface& t) { if ((t).tet[8] != NULL) { ((shellface *) (t).tet[8])[ver2edge[(t).ver]] = NULL; } } inline void tetgenmesh::sstdissolve1(face& s) { ((tetrahedron *) (s).sh)[9] = NULL; } inline void tetgenmesh::tsspivot1(triface& t, face& s) { if ((t).tet[8] != NULL) { sdecode(((shellface *) (t).tet[8])[ver2edge[(t).ver]], s); } else { (s).sh = NULL; } } // Quickly check whether 't' is a segment or not. #define issubseg(t) \ ((t).tet[8] && ((t).tet[8])[ver2edge[(t).ver]]) inline void tetgenmesh::sstpivot1(face& s, triface& t) { decode((tetrahedron) s.sh[9], t); } //============================================================================// // // // Primitives for points // // // //============================================================================// inline int tetgenmesh::pointmark(point pt) { return ((int *) (pt))[pointmarkindex]; } inline void tetgenmesh::setpointmark(point pt, int value) { ((int *) (pt))[pointmarkindex] = value; } // These two primitives set and read the type of the point. inline enum tetgenmesh::verttype tetgenmesh::pointtype(point pt) { return (enum verttype) (((int *) (pt))[pointmarkindex + 1] >> (int) 8); } inline void tetgenmesh::setpointtype(point pt, enum verttype value) { ((int *) (pt))[pointmarkindex + 1] = ((int) value << 8) + (((int *) (pt))[pointmarkindex + 1] & (int) 255); } // pinfect(), puninfect(), pinfected() -- primitives to flag or unflag // a point. The last bit of the integer '[pointindex+1]' is flagged. inline void tetgenmesh::pinfect(point pt) { ((int *) (pt))[pointmarkindex + 1] |= (int) 1; } inline void tetgenmesh::puninfect(point pt) { ((int *) (pt))[pointmarkindex + 1] &= ~(int) 1; } inline bool tetgenmesh::pinfected(point pt) { return (((int *) (pt))[pointmarkindex + 1] & (int) 1) != 0; } // pmarktest(), punmarktest(), pmarktested() -- more primitives to // flag or unflag a point. inline void tetgenmesh::pmarktest(point pt) { ((int *) (pt))[pointmarkindex + 1] |= (int) 2; } inline void tetgenmesh::punmarktest(point pt) { ((int *) (pt))[pointmarkindex + 1] &= ~(int) 2; } inline bool tetgenmesh::pmarktested(point pt) { return (((int *) (pt))[pointmarkindex + 1] & (int) 2) != 0; } inline void tetgenmesh::pmarktest2(point pt) { ((int *) (pt))[pointmarkindex + 1] |= (int) 4; } inline void tetgenmesh::punmarktest2(point pt) { ((int *) (pt))[pointmarkindex + 1] &= ~(int) 4; } inline bool tetgenmesh::pmarktest2ed(point pt) { return (((int *) (pt))[pointmarkindex + 1] & (int) 4) != 0; } inline void tetgenmesh::pmarktest3(point pt) { ((int *) (pt))[pointmarkindex + 1] |= (int) 8; } inline void tetgenmesh::punmarktest3(point pt) { ((int *) (pt))[pointmarkindex + 1] &= ~(int) 8; } inline bool tetgenmesh::pmarktest3ed(point pt) { return (((int *) (pt))[pointmarkindex + 1] & (int) 8) != 0; } // Read and set the geometry tag of the point (used by -s option). inline int tetgenmesh::pointgeomtag(point pt) { return ((int *) (pt))[pointmarkindex + 2]; } inline void tetgenmesh::setpointgeomtag(point pt, int value) { ((int *) (pt))[pointmarkindex + 2] = value; } // Read and set the u,v coordinates of the point (used by -s option). inline REAL tetgenmesh::pointgeomuv(point pt, int i) { return pt[pointparamindex + i]; } inline void tetgenmesh::setpointgeomuv(point pt, int i, REAL value) { pt[pointparamindex + i] = value; } // These following primitives set and read a pointer to a tetrahedron // a subface/subsegment, a point, or a tet of background mesh. inline tetgenmesh::tetrahedron tetgenmesh::point2tet(point pt) { return ((tetrahedron *) (pt))[point2simindex]; } inline void tetgenmesh::setpoint2tet(point pt, tetrahedron value) { ((tetrahedron *) (pt))[point2simindex] = value; } inline tetgenmesh::point tetgenmesh::point2ppt(point pt) { return (point) ((tetrahedron *) (pt))[point2simindex + 1]; } inline void tetgenmesh::setpoint2ppt(point pt, point value) { ((tetrahedron *) (pt))[point2simindex + 1] = (tetrahedron) value; } inline tetgenmesh::shellface tetgenmesh::point2sh(point pt) { return (shellface) ((tetrahedron *) (pt))[point2simindex + 2]; } inline void tetgenmesh::setpoint2sh(point pt, shellface value) { ((tetrahedron *) (pt))[point2simindex + 2] = (tetrahedron) value; } inline tetgenmesh::tetrahedron tetgenmesh::point2bgmtet(point pt) { return ((tetrahedron *) (pt))[point2simindex + 3]; } inline void tetgenmesh::setpoint2bgmtet(point pt, tetrahedron value) { ((tetrahedron *) (pt))[point2simindex + 3] = value; } // The primitives for saving and getting the insertion radius. inline void tetgenmesh::setpointinsradius(point pt, REAL value) { pt[pointinsradiusindex] = value; } inline REAL tetgenmesh::getpointinsradius(point pt) { return pt[pointinsradiusindex]; } inline bool tetgenmesh::issteinerpoint(point pt) { return (pointtype(pt) == FREESEGVERTEX) || (pointtype(pt) == FREEFACETVERTEX) || (pointtype(pt) == FREEVOLVERTEX); } // point2tetorg() Get the tetrahedron whose origin is the point. inline void tetgenmesh::point2tetorg(point pa, triface& searchtet) { decode(point2tet(pa), searchtet); if ((point) searchtet.tet[4] == pa) { searchtet.ver = 11; } else if ((point) searchtet.tet[5] == pa) { searchtet.ver = 3; } else if ((point) searchtet.tet[6] == pa) { searchtet.ver = 7; } else { searchtet.ver = 0; } } // point2shorg() Get the subface/segment whose origin is the point. inline void tetgenmesh::point2shorg(point pa, face& searchsh) { sdecode(point2sh(pa), searchsh); if ((point) searchsh.sh[3] == pa) { searchsh.shver = 0; } else if ((point) searchsh.sh[4] == pa) { searchsh.shver = (searchsh.sh[5] != NULL ? 2 : 1); } else { searchsh.shver = 4; } } // farsorg() Return the origin of the subsegment. // farsdest() Return the destination of the subsegment. inline tetgenmesh::point tetgenmesh::farsorg(face& s) { face travesh, neighsh; travesh = s; while (1) { senext2(travesh, neighsh); spivotself(neighsh); if (neighsh.sh == NULL) break; if (sorg(neighsh) != sorg(travesh)) sesymself(neighsh); senext2(neighsh, travesh); } return sorg(travesh); } inline tetgenmesh::point tetgenmesh::farsdest(face& s) { face travesh, neighsh; travesh = s; while (1) { senext(travesh, neighsh); spivotself(neighsh); if (neighsh.sh == NULL) break; if (sdest(neighsh) != sdest(travesh)) sesymself(neighsh); senext(neighsh, travesh); } return sdest(travesh); } /////////////////////////////////////////////////////////////////////////////// // // // Linear algebra operators. // // // /////////////////////////////////////////////////////////////////////////////// // dot() returns the dot product: v1 dot v2. inline REAL tetgenmesh::dot(REAL* v1, REAL* v2) { return v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2]; } // cross() computes the cross product: n = v1 cross v2. inline void tetgenmesh::cross(REAL* v1, REAL* v2, REAL* n) { n[0] = v1[1] * v2[2] - v2[1] * v1[2]; n[1] = -(v1[0] * v2[2] - v2[0] * v1[2]); n[2] = v1[0] * v2[1] - v2[0] * v1[1]; } // distance() computes the Euclidean distance between two points. inline REAL tetgenmesh::distance(REAL* p1, REAL* p2) { return sqrt((p2[0] - p1[0]) * (p2[0] - p1[0]) + (p2[1] - p1[1]) * (p2[1] - p1[1]) + (p2[2] - p1[2]) * (p2[2] - p1[2])); } inline REAL tetgenmesh::distance2(REAL* p1, REAL* p2) { return norm2(p2[0] - p1[0], p2[1] - p1[1], p2[2] - p1[2]); } inline REAL tetgenmesh::norm2(REAL x, REAL y, REAL z) { return (x) * (x) + (y) * (y) + (z) * (z); } #endif // #ifndef tetgenH