From c0e0d22b4da2ef2efc28bdd98c89bf4fe6cbcbc7 Mon Sep 17 00:00:00 2001 From: Christophe Geuzaine <cgeuzaine@ulg.ac.be> Date: Sat, 27 Jun 2009 06:57:08 +0000 Subject: [PATCH] revert to old tetgen code for now: the new code is just too buggy you can use the new code by configuring with --enable-tetgen-new --- configure | 61 +- configure.in | 31 +- contrib/TetgenNew/LICENSE | 66 + contrib/TetgenNew/Makefile | 65 + contrib/TetgenNew/README | 16 + contrib/TetgenNew/behavior.cxx | 531 ++++ contrib/TetgenNew/constrain.cxx | 4416 +++++++++++++++++++++++++++++ contrib/TetgenNew/delaunay.cxx | 1464 ++++++++++ contrib/TetgenNew/flip.cxx | 2128 ++++++++++++++ contrib/TetgenNew/geom.cxx | 4517 ++++++++++++++++++++++++++++++ contrib/TetgenNew/io.cxx | 3148 +++++++++++++++++++++ contrib/TetgenNew/main.cxx | 387 +++ contrib/TetgenNew/memorypool.cxx | 981 +++++++ contrib/TetgenNew/meshio.cxx | 1237 ++++++++ contrib/TetgenNew/meshstat.cxx | 1764 ++++++++++++ contrib/TetgenNew/predicates.cxx | 4187 +++++++++++++++++++++++++++ contrib/TetgenNew/refine.cxx | 249 ++ contrib/TetgenNew/surface.cxx | 1795 ++++++++++++ contrib/TetgenNew/tetgen.h | 1922 +++++++++++++ 19 files changed, 28948 insertions(+), 17 deletions(-) create mode 100644 contrib/TetgenNew/LICENSE create mode 100644 contrib/TetgenNew/Makefile create mode 100644 contrib/TetgenNew/README create mode 100644 contrib/TetgenNew/behavior.cxx create mode 100644 contrib/TetgenNew/constrain.cxx create mode 100644 contrib/TetgenNew/delaunay.cxx create mode 100644 contrib/TetgenNew/flip.cxx create mode 100644 contrib/TetgenNew/geom.cxx create mode 100644 contrib/TetgenNew/io.cxx create mode 100644 contrib/TetgenNew/main.cxx create mode 100644 contrib/TetgenNew/memorypool.cxx create mode 100644 contrib/TetgenNew/meshio.cxx create mode 100644 contrib/TetgenNew/meshstat.cxx create mode 100644 contrib/TetgenNew/predicates.cxx create mode 100644 contrib/TetgenNew/refine.cxx create mode 100644 contrib/TetgenNew/surface.cxx create mode 100644 contrib/TetgenNew/tetgen.h diff --git a/configure b/configure index 08514685cf..70b44ee07f 100755 --- a/configure +++ b/configure @@ -1972,6 +1972,7 @@ if test "${enable_mpi+set}" = set; then enableval=$enable_mpi; fi + # Check whether --enable-graphics was given. if test "${enable_graphics+set}" = set; then enableval=$enable_graphics; @@ -1982,6 +1983,11 @@ if test "${enable_minimal+set}" = set; then enableval=$enable_minimal; fi +# Check whether --enable-tetgen-new was given. +if test "${enable_tetgen_new+set}" = set; then + enableval=$enable_tetgen_new; +fi + if test "x$enable_minimal" = "xyes"; then enable_gui=no; @@ -4691,8 +4697,44 @@ _ACEOF fi fi + if test "x$enable_tetgen_new" = "xyes"; then + { echo "$as_me:$LINENO: checking for ./contrib/TetgenNew/tetgen.h" >&5 +echo $ECHO_N "checking for ./contrib/TetgenNew/tetgen.h... $ECHO_C" >&6; } +if test "${ac_cv_file___contrib_TetgenNew_tetgen_h+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + test "$cross_compiling" = yes && + { { echo "$as_me:$LINENO: error: cannot check for file existence when cross compiling" >&5 +echo "$as_me: error: cannot check for file existence when cross compiling" >&2;} + { (exit 1); exit 1; }; } +if test -r "./contrib/TetgenNew/tetgen.h"; then + ac_cv_file___contrib_TetgenNew_tetgen_h=yes +else + ac_cv_file___contrib_TetgenNew_tetgen_h=no +fi +fi +{ echo "$as_me:$LINENO: result: $ac_cv_file___contrib_TetgenNew_tetgen_h" >&5 +echo "${ECHO_T}$ac_cv_file___contrib_TetgenNew_tetgen_h" >&6; } +if test $ac_cv_file___contrib_TetgenNew_tetgen_h = yes; then + TETGEN="yes" +fi + + if test "x${TETGEN}" = "xyes"; then + GMSH_DIRS="${GMSH_DIRS} contrib/TetgenNew" + GMSH_LIBS="${GMSH_LIBS} -lGmshTetgenNew" + cat >>confdefs.h <<\_ACEOF +#define HAVE_TETGEN 1 +_ACEOF + + BO="${BO} TetgenNew" + { echo "$as_me:$LINENO: WARNING: You are building with an experimental version of Tetgen that" >&5 +echo "$as_me: WARNING: You are building with an experimental version of Tetgen that" >&2;} + { echo "$as_me:$LINENO: WARNING: is KNOWN TO BE BUGGY on 64 bits archs and on WIN32/MSVC." >&5 +echo "$as_me: WARNING: is KNOWN TO BE BUGGY on 64 bits archs and on WIN32/MSVC." >&2;} + fi + else if test "x$enable_tetgen" != "xno"; then - { echo "$as_me:$LINENO: checking for ./contrib/Tetgen/tetgen.h" >&5 + { echo "$as_me:$LINENO: checking for ./contrib/Tetgen/tetgen.h" >&5 echo $ECHO_N "checking for ./contrib/Tetgen/tetgen.h... $ECHO_C" >&6; } if test "${ac_cv_file___contrib_Tetgen_tetgen_h+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 @@ -4713,20 +4755,21 @@ if test $ac_cv_file___contrib_Tetgen_tetgen_h = yes; then TETGEN="yes" fi - if test "x${TETGEN}" = "xyes"; then - GMSH_DIRS="${GMSH_DIRS} contrib/Tetgen" - GMSH_LIBS="${GMSH_LIBS} -lGmshTetgen" - cat >>confdefs.h <<\_ACEOF + if test "x${TETGEN}" = "xyes"; then + GMSH_DIRS="${GMSH_DIRS} contrib/Tetgen" + GMSH_LIBS="${GMSH_LIBS} -lGmshTetgen" + cat >>confdefs.h <<\_ACEOF #define HAVE_TETGEN 1 _ACEOF - BO="${BO} Tetgen" - { echo "$as_me:$LINENO: WARNING: By including Tetgen you have to comply with Tetgen' special" >&5 + BO="${BO} Tetgen" + { echo "$as_me:$LINENO: WARNING: By including Tetgen you have to comply with Tetgen' special" >&5 echo "$as_me: WARNING: By including Tetgen you have to comply with Tetgen' special" >&2;} - { echo "$as_me:$LINENO: WARNING: licensing requirements stated in contrib/Tetgen/LICENSE. To" >&5 + { echo "$as_me:$LINENO: WARNING: licensing requirements stated in contrib/Tetgen/LICENSE. To" >&5 echo "$as_me: WARNING: licensing requirements stated in contrib/Tetgen/LICENSE. To" >&2;} - { echo "$as_me:$LINENO: WARNING: disable Tetgen, use the --disable-tetgen option" >&5 + { echo "$as_me:$LINENO: WARNING: disable Tetgen, use the --disable-tetgen option" >&5 echo "$as_me: WARNING: disable Tetgen, use the --disable-tetgen option" >&2;} + fi fi fi diff --git a/configure.in b/configure.in index 9aff10bd24..d6e3e75863 100644 --- a/configure.in +++ b/configure.in @@ -145,8 +145,11 @@ AC_ARG_ENABLE(tree-browser, AC_ARG_ENABLE(mpi, AC_HELP_STRING([--enable-mpi], [enable MPI support (default=no)])) + +dnl undocumented options AC_ARG_ENABLE(graphics) AC_ARG_ENABLE(minimal) +AC_ARG_ENABLE(tetgen-new) dnl "minimal" build shortcut if test "x$enable_minimal" = "xyes"; then @@ -485,16 +488,28 @@ if test "x$enable_contrib" != "xno"; then fi dnl Check for Tetgen - if test "x$enable_tetgen" != "xno"; then - AC_CHECK_FILE(./contrib/Tetgen/tetgen.h,TETGEN="yes") + if test "x$enable_tetgen_new" = "xyes"; then + AC_CHECK_FILE(./contrib/TetgenNew/tetgen.h,TETGEN="yes") if test "x${TETGEN}" = "xyes"; then - GMSH_DIRS="${GMSH_DIRS} contrib/Tetgen" - GMSH_LIBS="${GMSH_LIBS} -lGmshTetgen" + GMSH_DIRS="${GMSH_DIRS} contrib/TetgenNew" + GMSH_LIBS="${GMSH_LIBS} -lGmshTetgenNew" AC_DEFINE(HAVE_TETGEN) - BO="${BO} Tetgen" - AC_MSG_WARN([By including Tetgen you have to comply with Tetgen' special]) - AC_MSG_WARN([licensing requirements stated in contrib/Tetgen/LICENSE. To]) - AC_MSG_WARN([disable Tetgen, use the --disable-tetgen option]) + BO="${BO} TetgenNew" + AC_MSG_WARN([You are building with an experimental version of Tetgen that]) + AC_MSG_WARN([is KNOWN TO BE BUGGY on 64 bits archs and on WIN32/MSVC.]) + fi + else + if test "x$enable_tetgen" != "xno"; then + AC_CHECK_FILE(./contrib/Tetgen/tetgen.h,TETGEN="yes") + if test "x${TETGEN}" = "xyes"; then + GMSH_DIRS="${GMSH_DIRS} contrib/Tetgen" + GMSH_LIBS="${GMSH_LIBS} -lGmshTetgen" + AC_DEFINE(HAVE_TETGEN) + BO="${BO} Tetgen" + AC_MSG_WARN([By including Tetgen you have to comply with Tetgen' special]) + AC_MSG_WARN([licensing requirements stated in contrib/Tetgen/LICENSE. To]) + AC_MSG_WARN([disable Tetgen, use the --disable-tetgen option]) + fi fi fi diff --git a/contrib/TetgenNew/LICENSE b/contrib/TetgenNew/LICENSE new file mode 100644 index 0000000000..65e5262522 --- /dev/null +++ b/contrib/TetgenNew/LICENSE @@ -0,0 +1,66 @@ +TetGen License +-------------- + +The software (TetGen) is licensed under the terms of the MIT license +with the following exceptions: + +Distribution of modified versions of this code is permissible UNDER +THE CONDITION THAT THIS CODE AND ANY MODIFICATIONS MADE TO IT IN THE +SAME SOURCE FILES tetgen.h AND tetgen.cxx REMAIN UNDER COPYRIGHT OF +THE ORIGINAL AUTHOR, BOTH SOURCE AND OBJECT CODE ARE MADE FREELY +AVAILABLE WITHOUT CHARGE, AND CLEAR NOTICE IS GIVEN OF THE +MODIFICATIONS. + +Distribution of this code for any commercial purpose is permissible +ONLY BY DIRECT ARRANGEMENT WITH THE COPYRIGHT OWNER. + +The full license text is reproduced below. + +This means that TetGen is no free software, but for private, research, +and educational purposes it can be used at absolutely no cost and +without further arrangements. + + +For details, see http://tetgen.berlios.de + +============================================================================== + +TetGen +A Quality Tetrahedral Mesh Generator and 3D Delaunay Triangulator +Version 1.4 (Released on January 14, 2006). + +Copyright 2002, 2004, 2005, 2006 +Hang Si +Rathausstr. 9, 10178 Berlin, Germany +si@wias-berlin.de + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +Distribution of modified versions of this code is permissible UNDER +THE CONDITION THAT THIS CODE AND ANY MODIFICATIONS MADE TO IT IN THE +SAME SOURCE FILES tetgen.h AND tetgen.cxx REMAIN UNDER COPYRIGHT OF +THE ORIGINAL AUTHOR, BOTH SOURCE AND OBJECT CODE ARE MADE FREELY +AVAILABLE WITHOUT CHARGE, AND CLEAR NOTICE IS GIVEN OF THE +MODIFICATIONS. + +Distribution of this code for any commercial purpose is permissible +ONLY BY DIRECT ARRANGEMENT WITH THE COPYRIGHT OWNER. + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +============================================================================== \ No newline at end of file diff --git a/contrib/TetgenNew/Makefile b/contrib/TetgenNew/Makefile new file mode 100644 index 0000000000..a9109b4bce --- /dev/null +++ b/contrib/TetgenNew/Makefile @@ -0,0 +1,65 @@ +# Gmsh - Copyright (C) 1997-2009 C. Geuzaine, J.-F. Remacle +# +# See the LICENSE.txt file for license information. Please report all +# bugs and problems to <gmsh@geuz.org>. + +include ../../variables + +LIB = ../../lib/libGmshTetgen${LIBEXT} + +# Don't optimize Tetgen +CFLAGS = ${FLAGS} ${DASH}DTETLIBRARY + +SRC = behavior.cxx\ + constrain.cxx\ + delaunay.cxx\ + flip.cxx\ + geom.cxx\ + io.cxx\ + main.cxx\ + memorypool.cxx\ + meshio.cxx\ + meshstat.cxx\ + predicates.cxx\ + refine.cxx\ + surface.cxx + +OBJ = ${SRC:.cxx=${OBJEXT}} + +.SUFFIXES: ${OBJEXT} .cxx + +${LIB}: ${OBJ} + ${AR} ${ARFLAGS}${LIB} ${OBJ} + ${RANLIB} ${LIB} + +cpobj: ${OBJ} + cp -f ${OBJ} ../../lib/ + +.cxx${OBJEXT}: + ${CXX} ${CFLAGS} ${DASH}c $< + +clean: + ${RM} *.o *.obj + +depend: + (sed '/^# DO NOT DELETE THIS LINE/q' Makefile && \ + ${CXX} -MM ${CFLAGS} ${SRC} | sed 's/.o:/$${OBJEXT}:/g' \ + ) > Makefile.new + cp Makefile Makefile.bak + cp Makefile.new Makefile + rm -f Makefile.new + +# DO NOT DELETE THIS LINE +behavior${OBJEXT}: behavior.cxx tetgen.h +constrain${OBJEXT}: constrain.cxx tetgen.h +delaunay${OBJEXT}: delaunay.cxx tetgen.h +flip${OBJEXT}: flip.cxx tetgen.h +geom${OBJEXT}: geom.cxx tetgen.h +io${OBJEXT}: io.cxx tetgen.h +main${OBJEXT}: main.cxx tetgen.h +memorypool${OBJEXT}: memorypool.cxx tetgen.h +meshio${OBJEXT}: meshio.cxx tetgen.h +meshstat${OBJEXT}: meshstat.cxx tetgen.h +predicates${OBJEXT}: predicates.cxx tetgen.h +refine${OBJEXT}: refine.cxx tetgen.h +surface${OBJEXT}: surface.cxx tetgen.h diff --git a/contrib/TetgenNew/README b/contrib/TetgenNew/README new file mode 100644 index 0000000000..5e86013395 --- /dev/null +++ b/contrib/TetgenNew/README @@ -0,0 +1,16 @@ +This is an EXPERIMENTAL version of TetGen provided by Hang Si for Gmsh + +Please see the documentation of TetGen (available at the following link) for +compiling and using TetGen. + + http://tetgen.berlios.de/index.html + +TetGen may be freely copied, modified, and redistributed under the +copyright notices stated in the file LICENSE. + +Please send bugs/comments to Hang Si <si@wias-berlin.de> + +Thank you and enjoy! + +Hang Si + diff --git a/contrib/TetgenNew/behavior.cxx b/contrib/TetgenNew/behavior.cxx new file mode 100644 index 0000000000..e25f7e772c --- /dev/null +++ b/contrib/TetgenNew/behavior.cxx @@ -0,0 +1,531 @@ +#ifndef behaviorCXX +#define behaviorCXX + +#include "tetgen.h" + +static REAL PI = 3.14159265358979323846264338327950288419716939937510582; + +/////////////////////////////////////////////////////////////////////////////// +// // +// tetgenbehavior() Initialize veriables of 'tetgenbehavior'. // +// // +/////////////////////////////////////////////////////////////////////////////// + +tetgenbehavior::tetgenbehavior() +{ + // Initialize command line switches. + plc = 0; + quality = 0; + refine = 0; + coarse = 0; + metric = 0; + minratio = 2.0; + goodratio = 0.0; + minangle = 20.0; + goodangle = 0.0; + maxdihedral = 165.0; + mindihedral = 5.0; + varvolume = 0; + fixedvolume = 0; + maxvolume = -1.0; + regionattrib = 0; + bowyerwatson = 1; + convexity = 0; + insertaddpoints = 0; + diagnose = 0; + conformdel = 0; + zeroindex = 0; + facesout = 0; + edgesout = 0; + neighout = 0; + voroout = 0; + meditview = 0; + gidview = 0; + geomview = 0; + order = 1; + nojettison = 0; + nobound = 0; + nonodewritten = 0; + noelewritten = 0; + nofacewritten = 0; + noiterationnum = 0; + nobisect = 0; + steinerleft = -1l; + nomerge = 0; + docheck = 0; + quiet = 0; + verbose = 0; + useshelles = 0; + epsilon = 1.0e-8; + object = NONE; + // Initialize strings + commandline[0] = '\0'; + infilename[0] = '\0'; + outfilename[0] = '\0'; + addinfilename[0] = '\0'; + bgmeshfilename[0] = '\0'; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// versioninfo() Print the version information of TetGen. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenbehavior::versioninfo() +{ + printf("Develop Version (Started on August 9, 2008).\n"); + printf("\n"); + printf("Copyright (C) 2002 - 2008\n"); + printf("Hang Si\n"); + printf("Mohrenstr. 39, 10117 Berlin, Germany\n"); + printf("si@wias-berlin.de\n"); +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// syntax() Print list of command line switches and exit the program. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenbehavior::syntax() +{ + printf(" tetgen [-prq_Ra_AiMYS_T_dzo_fenvgGOJBNEFICQVh] input_file\n"); + printf(" -p Tetrahedralizes a piecewise linear complex (PLC).\n"); + printf(" -r Reconstructs a previously generated mesh.\n"); + printf(" -q Quality mesh generation (adding new mesh points to "); + printf("improve mesh quality).\n"); + printf(" -R Mesh coarsening (deleting redundant mesh points).\n"); + printf(" -a Applies a maximum tetrahedron volume constraint.\n"); + printf(" -A Assigns attributes to identify tetrahedra in different "); + printf("regions.\n"); + printf(" -i Inserts a list of additional points into mesh.\n"); + printf(" -M Does not merge coplanar facets.\n"); + printf(" -Y Suppresses boundary facets/segments splitting.\n"); + printf(" -S Specifies maximum number of added points.\n"); + printf(" -T Sets a tolerance for coplanar test (default 1e-8).\n"); + printf(" -d Detects self-intersections of facets of the PLC.\n"); + printf(" -z Numbers all output items starting from zero.\n"); + printf(" -o2 Generates second-order subparametric elements.\n"); + printf(" -f Outputs all faces to .face file."); + printf("file.\n"); + printf(" -e Outputs all edges to .edge file.\n"); + printf(" -n Outputs tetrahedra neighbors to .neigh file.\n"); + printf(" -v Outputs Voronoi diagram to files.\n"); + printf(" -g Outputs mesh to .mesh file for viewing by Medit.\n"); + printf(" -G Outputs mesh to .msh file for viewing by Gid.\n"); + printf(" -O Outputs mesh to .off file for viewing by Geomview.\n"); + printf(" -J No jettison of unused vertices from output .node file.\n"); + printf(" -B Suppresses output of boundary information.\n"); + printf(" -N Suppresses output of .node file.\n"); + printf(" -E Suppresses output of .ele file.\n"); + printf(" -F Suppresses output of .face file.\n"); + printf(" -I Suppresses mesh iteration numbers.\n"); + printf(" -C Checks the consistency of the final mesh.\n"); + printf(" -Q Quiet: No terminal output except errors.\n"); + printf(" -V Verbose: Detailed information, more terminal output.\n"); + printf(" -h Help: A brief instruction for using TetGen.\n"); +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// usage() Print a brief instruction for using TetGen. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenbehavior::usage() +{ + printf("TetGen\n"); + printf("A Quality Tetrahedral Mesh Generator and 3D Delaunay "); + printf("Triangulator\n"); + versioninfo(); + printf("\n"); + printf("What Can TetGen Do?\n"); + printf("\n"); + printf(" TetGen generates exact Delaunay tetrahedralizations, exact\n"); + printf(" constrained Delaunay tetrahedralizations, and quality "); + printf("tetrahedral\n meshes. The latter are nicely graded and whose "); + printf("tetrahedra have\n radius-edge ratio bounded, thus are suitable "); + printf("for finite element and\n finite volume analysis.\n"); + printf("\n"); + printf("Command Line Syntax:\n"); + printf("\n"); + printf(" Below is the command line syntax of TetGen with a list of "); + printf("short\n"); + printf(" descriptions. Underscores indicate that numbers may optionally\n"); + printf(" follow certain switches. Do not leave any space between a "); + printf("switch\n"); + printf(" and its numeric parameter. \'input_file\' contains input data\n"); + printf(" depending on the switches you supplied which may be a "); + printf(" piecewise\n"); + printf(" linear complex or a list of nodes. File formats and detailed\n"); + printf(" description of command line switches are found in user's "); + printf("manual.\n"); + printf("\n"); + syntax(); + printf("\n"); + printf("Examples of How to Use TetGen:\n"); + printf("\n"); + printf(" \'tetgen object\' reads vertices from object.node, and writes "); + printf("their\n Delaunay tetrahedralization to object.1.node and "); + printf("object.1.ele.\n"); + printf("\n"); + printf(" \'tetgen -p object\' reads a PLC from object.poly or object."); + printf("smesh (and\n possibly object.node) and writes its constrained "); + printf("Delaunay\n tetrahedralization to object.1.node, object.1.ele and "); + printf("object.1.face.\n"); + printf("\n"); + printf(" \'tetgen -pq1.414a.1 object\' reads a PLC from object.poly or\n"); + printf(" object.smesh (and possibly object.node), generates a mesh "); + printf("whose\n tetrahedra have radius-edge ratio smaller than 1.414 and "); + printf("have volume\n of 0.1 or less, and writes the mesh to "); + printf("object.1.node, object.1.ele\n and object.1.face.\n"); + printf("\n"); + printf("Please send bugs/comments to Hang Si <si@wias-berlin.de>\n"); +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// parse_commandline() Read the command line, identify switches, and set // +// up options and file names. // +// // +// 'argc' and 'argv' are the same parameters passed to the function main() // +// of a C/C++ program. They together represent the command line user invoked // +// from an environment in which TetGen is running. // +// // +// When TetGen is invoked from an environment. 'argc' is nonzero, switches // +// and input filename should be supplied as zero-terminated strings in // +// argv[0] through argv[argc - 1] and argv[0] shall be the name used to // +// invoke TetGen, i.e. "tetgen". Switches are previously started with a // +// dash '-' to identify them from the input filename. // +// // +// When TetGen is called from within another program. 'argc' is set to zero. // +// switches are given in one zero-terminated string (no previous dash is // +// required.), and 'argv' is a pointer points to this string. No input // +// filename is required (usually the input data has been directly created by // +// user in the 'tetgenio' structure). A default filename 'tetgen-tmpfile' // +// will be created for debugging output purpose. // +// // +/////////////////////////////////////////////////////////////////////////////// + +bool tetgenbehavior::parse_commandline(int argc, char **argv) +{ + int startindex; + int increment; + int meshnumber; + int scount; + int i, j, k; + char workstring[1024]; + + // First determine the input style of the switches. + if (argc == 0) { + startindex = 0; // Switches are given without a dash. + argc = 1; // For running the following for-loop once. + commandline[0] = '\0'; + } else { + startindex = 1; + strcpy(commandline, argv[0]); + strcat(commandline, " "); + } + + // Rcount used to count the number of '-R' be used. + scount = 0; + + for (i = startindex; i < argc; i++) { + // Remember the command line switches. + strcat(commandline, argv[i]); + strcat(commandline, " "); + if (startindex == 1) { + // Is this string a filename? + if (argv[i][0] != '-') { + strncpy(infilename, argv[i], 1024 - 1); + infilename[1024 - 1] = '\0'; + // Go to the next string directly. + continue; + } + } + // Parse the individual switch from the string. + for (j = startindex; argv[i][j] != '\0'; j++) { + if (argv[i][j] == 'p') { + plc = 1; + } else if (argv[i][j] == 'r') { + refine = 1; + } else if (argv[i][j] == 'R') { + coarse = 1; + } else if (argv[i][j] == 'q') { + quality++; + if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || + (argv[i][j + 1] == '.')) { + k = 0; + while (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || + (argv[i][j + 1] == '.')) { + j++; + workstring[k] = argv[i][j]; + k++; + } + workstring[k] = '\0'; + if (quality == 1) { + minratio = (REAL) strtod(workstring, (char **) NULL); + } else if (quality == 2) { + mindihedral = (REAL) strtod(workstring, (char **) NULL); + } else if (quality == 3) { + maxdihedral = (REAL) strtod(workstring, (char **) NULL); + } + } + } else if (argv[i][j] == 'm') { + metric++; + } else if (argv[i][j] == 'a') { + if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || + (argv[i][j + 1] == '.')) { + fixedvolume = 1; + k = 0; + while (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || + (argv[i][j + 1] == '.') || (argv[i][j + 1] == 'e') || + (argv[i][j + 1] == '-') || (argv[i][j + 1] == '+')) { + j++; + workstring[k] = argv[i][j]; + k++; + } + workstring[k] = '\0'; + maxvolume = (REAL) strtod(workstring, (char **) NULL); + } else { + varvolume = 1; + } + } else if (argv[i][j] == 'A') { + regionattrib++; + } else if (argv[i][j] == 'b') { + bowyerwatson = 0; + } else if (argv[i][j] == 'c') { + convexity++; + } else if (argv[i][j] == 'i') { + insertaddpoints = 1; + } else if (argv[i][j] == 'd') { + diagnose = 1; + } else if (argv[i][j] == 'z') { + zeroindex = 1; + } else if (argv[i][j] == 'f') { + facesout = 1; + } else if (argv[i][j] == 'e') { + edgesout++; + } else if (argv[i][j] == 'n') { + neighout++; + } else if (argv[i][j] == 'v') { + voroout = 1; + } else if (argv[i][j] == 'g') { + meditview = 1; + } else if (argv[i][j] == 'G') { + gidview = 1; + } else if (argv[i][j] == 'O') { + geomview = 1; + } else if (argv[i][j] == 'M') { + nomerge = 1; + } else if (argv[i][j] == 'Y') { + nobisect++; + } else if (argv[i][j] == 'J') { + nojettison = 1; + } else if (argv[i][j] == 'B') { + nobound = 1; + } else if (argv[i][j] == 'N') { + nonodewritten = 1; + } else if (argv[i][j] == 'E') { + noelewritten = 1; + if (argv[i][j + 1] == '2') { + j++; + noelewritten = 2; + } + } else if (argv[i][j] == 'F') { + nofacewritten = 1; + } else if (argv[i][j] == 'I') { + noiterationnum = 1; + } else if (argv[i][j] == 'o') { + if ((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) { + k = 0; + while ((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) { + j++; + workstring[k] = argv[i][j]; + k++; + } + workstring[k] = '\0'; + order = (int) strtol(workstring, (char **) NULL, 0); + } + } else if (argv[i][j] == 'S') { + if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || + (argv[i][j + 1] == '.')) { + k = 0; + while (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || + (argv[i][j + 1] == '.') || (argv[i][j + 1] == 'e') || + (argv[i][j + 1] == '-') || (argv[i][j + 1] == '+')) { + j++; + workstring[k] = argv[i][j]; + k++; + } + workstring[k] = '\0'; + steinerleft = (int) strtol(workstring, (char **) NULL, 0); + } + } else if (argv[i][j] == 'D') { + conformdel++; + } else if (argv[i][j] == 'T') { + if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || + (argv[i][j + 1] == '.')) { + k = 0; + while (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || + (argv[i][j + 1] == '.') || (argv[i][j + 1] == 'e') || + (argv[i][j + 1] == '-') || (argv[i][j + 1] == '+')) { + j++; + workstring[k] = argv[i][j]; + k++; + } + workstring[k] = '\0'; + epsilon = (REAL) strtod(workstring, (char **) NULL); + } + } else if (argv[i][j] == 'C') { + docheck++; + } else if (argv[i][j] == 'Q') { + quiet = 1; + } else if (argv[i][j] == 'V') { + verbose++; + // } else if (argv[i][j] == 'v') { + // versioninfo(); + // terminatetetgen(0); + } else if ((argv[i][j] == 'h') || (argv[i][j] == 'H') || + (argv[i][j] == '?')) { + usage(); + terminatetetgen(0); + } else { + printf("Warning: Unknown switch -%c.\n", argv[i][j]); + } + } + } + + if (startindex == 0) { + // Set a temporary filename for debugging output. + strcpy(infilename, "tetgen-tmpfile"); + } else { + if (infilename[0] == '\0') { + // No input file name. Print the syntax and exit. + syntax(); + terminatetetgen(0); + } + // Recognize the object from file extension if it is available. + if (!strcmp(&infilename[strlen(infilename) - 5], ".node")) { + infilename[strlen(infilename) - 5] = '\0'; + object = NODES; + } else if (!strcmp(&infilename[strlen(infilename) - 5], ".poly")) { + infilename[strlen(infilename) - 5] = '\0'; + object = POLY; + plc = 1; + } else if (!strcmp(&infilename[strlen(infilename) - 6], ".smesh")) { + infilename[strlen(infilename) - 6] = '\0'; + object = POLY; + plc = 1; + } else if (!strcmp(&infilename[strlen(infilename) - 4], ".off")) { + infilename[strlen(infilename) - 4] = '\0'; + object = OFF; + plc = 1; + } else if (!strcmp(&infilename[strlen(infilename) - 4], ".ply")) { + infilename[strlen(infilename) - 4] = '\0'; + object = PLY; + plc = 1; + } else if (!strcmp(&infilename[strlen(infilename) - 4], ".stl")) { + infilename[strlen(infilename) - 4] = '\0'; + object = STL; + plc = 1; + } else if (!strcmp(&infilename[strlen(infilename) - 5], ".mesh")) { + infilename[strlen(infilename) - 5] = '\0'; + object = MEDIT; + plc = 1; + } else if (!strcmp(&infilename[strlen(infilename) - 4], ".vtk")) { + infilename[strlen(infilename) - 4] = '\0'; + object = VTK; + plc = 1; + } else if (!strcmp(&infilename[strlen(infilename) - 4], ".ele")) { + infilename[strlen(infilename) - 4] = '\0'; + object = MESH; + refine = 1; + } + } + plc = plc || diagnose; + useshelles = plc || refine || coarse || quality; + goodratio = minratio; + goodratio *= goodratio; + + // Detect improper combinations of switches. + if (plc && refine) { + printf("Error: Switch -r cannot use together with -p.\n"); + return false; + } + if (refine && (plc || noiterationnum)) { + printf("Error: Switches %s cannot use together with -r.\n", + "-p, -d, and -I"); + return false; + } + if (diagnose && (quality || insertaddpoints || (order == 2) || neighout + || docheck)) { + printf("Error: Switches %s cannot use together with -d.\n", + "-q, -i, -o2, -n, and -C"); + return false; + } + + // Be careful not to allocate space for element area constraints that + // will never be assigned any value (other than the default -1.0). + if (!refine && !plc) { + varvolume = 0; + } + // Be careful not to add an extra attribute to each element unless the + // input supports it (PLC in, but not refining a preexisting mesh). + if (refine || !plc) { + regionattrib = 0; + } + // If '-a' or '-aa' is in use, enable '-q' option too. + if (fixedvolume || varvolume) { + if (quality == 0) { + quality = 1; + } + } + // Calculate the goodangle for testing bad subfaces. + goodangle = cos(minangle * PI / 180.0); + goodangle *= goodangle; + + increment = 0; + strcpy(workstring, infilename); + j = 1; + while (workstring[j] != '\0') { + if ((workstring[j] == '.') && (workstring[j + 1] != '\0')) { + increment = j + 1; + } + j++; + } + meshnumber = 0; + if (increment > 0) { + j = increment; + do { + if ((workstring[j] >= '0') && (workstring[j] <= '9')) { + meshnumber = meshnumber * 10 + (int) (workstring[j] - '0'); + } else { + increment = 0; + } + j++; + } while (workstring[j] != '\0'); + } + if (noiterationnum) { + strcpy(outfilename, infilename); + } else if (increment == 0) { + strcpy(outfilename, infilename); + strcat(outfilename, ".1"); + } else { + workstring[increment] = '%'; + workstring[increment + 1] = 'd'; + workstring[increment + 2] = '\0'; + sprintf(outfilename, workstring, meshnumber + 1); + } + // Additional input file name has the end ".a". + strcpy(addinfilename, infilename); + strcat(addinfilename, ".a"); + // Background filename has the form "*.b.ele", "*.b.node", ... + strcpy(bgmeshfilename, infilename); + strcat(bgmeshfilename, ".b"); + + return true; +} + +#endif // ifndef behaviorCXX diff --git a/contrib/TetgenNew/constrain.cxx b/contrib/TetgenNew/constrain.cxx new file mode 100644 index 0000000000..2bdd61a326 --- /dev/null +++ b/contrib/TetgenNew/constrain.cxx @@ -0,0 +1,4416 @@ +#ifndef constrainCXX +#define constrainCXX + +#include "tetgen.h" + +/////////////////////////////////////////////////////////////////////////////// +// // +// finddirection() Find the tet on the path from one point to another. // +// // +// The path starts from 'searchtet''s origin and ends at 'endpt'. On finish, // +// 'searchtet' contains a tet on the path, its origin does not change. // +// // +// The return value indicates one of the following cases (let 'searchtet' be // +// abcd, a is the origin of the path): // +// - ACROSSVERT, edge ab is collinear with the path; // +// - ACROSSEDGE, edge bc intersects with the path; // +// - ACROSSFACE, face bcd intersects with the path. // +// // +// WARNING: This routine is designed for convex triangulations, and will not // +// generally work after the holes and concavities have been carved. // +// // +/////////////////////////////////////////////////////////////////////////////// + +enum tetgenmesh::intersection tetgenmesh::finddirection(triface* searchtet, + point endpt) +{ + triface neightet; + point pa, pb, pc, pd, pn; + enum {HMOVE, RMOVE, LMOVE} nextmove; + enum {HCOPLANE, RCOPLANE, LCOPLANE, NCOPLANE} cop; + REAL hori, rori, lori; + REAL dmin, dist; + + tetrahedron ptr; + int *iptr, tver; + + // The origin is fixed. + pa = org(*searchtet); + if ((point) searchtet->tet[7] == dummypoint) { + // A hull tet. Choose the neighbor of its base face. + searchtet->loc = 0; + symself(*searchtet); + // Reset the origin to be pa. + if ((point) searchtet->tet[4] == pa) { + searchtet->loc = 0; searchtet->ver = 0; + } else if ((point) searchtet->tet[5] == pa) { + searchtet->loc = 0; searchtet->ver = 2; + } else if ((point) searchtet->tet[6] == pa) { + searchtet->loc = 0; searchtet->ver = 4; + } else { + assert((point) searchtet->tet[7] == pa); // SELF_CHECK + searchtet->loc = 1; searchtet->ver = 2; + } + } + if (searchtet->ver & 01) { + // Switch to the 0th edge ring. + esymself(*searchtet); + enextself(*searchtet); + } + pb = dest(*searchtet); + pc = apex(*searchtet); + + // Check whether the destination or apex is 'endpt'. + if (pb == endpt) { + // pa->pb is the search edge. + return ACROSSVERT; + } + if (pc == endpt) { + // pa->pc is the search edge. + enext2self(*searchtet); + esymself(*searchtet); + return ACROSSVERT; + } + + // Walk through tets at pa until the right one is found. + while (1) { + + pd = oppo(*searchtet); + + if (b->verbose > 2) { + printf(" From tet (%d, %d, %d, %d) to %d.\n", pointmark(pa), + pointmark(pb), pointmark(pc), pointmark(pd), pointmark(endpt)); + } + + // Check whether the opposite vertex is 'endpt'. + if (pd == endpt) { + // pa->pd is the search edge. + enext0fnextself(*searchtet); + enext2self(*searchtet); + esymself(*searchtet); + return ACROSSVERT; + } + // Check if we have entered outside of the domain. + if (pd == dummypoint) { + assert(0); + } + + // Now assume that the base face abc coincides with the horizon plane, + // and d lies above the horizon. The search point 'endpt' may lie + // above or below the horizon. We test the orientations of 'endpt' + // with respect to three planes: abc (horizon), bad (right plane), + // and acd (left plane). + hori = orient3d(pa, pb, pc, endpt); + rori = orient3d(pb, pa, pd, endpt); + lori = orient3d(pa, pc, pd, endpt); + orient3dcount += 3; + + // Now decide the tet to move. It is possible there are more than one + // tet are viable moves. Use the opposite points of thier neighbors + // to discriminate, i.e., we choose the tet whose opposite point has + // the shortest distance to 'endpt'. + if (hori > 0) { + if (rori > 0) { + if (lori > 0) { + // Any of the three neighbors is a viable move. + nextmove = HMOVE; + sym(*searchtet, neightet); + pn = oppo(neightet); + if (pn != dummypoint) { + dmin = NORM2(endpt[0] - pn[0], endpt[1] - pn[1], endpt[2] - pn[2]); + } else { + dmin = NORM2(xmax - xmin, ymax - ymin, zmax - zmin); + } + enext0fnext(*searchtet, neightet); + symself(neightet); + pn = oppo(neightet); + if (pn != dummypoint) { + dist = NORM2(endpt[0] - pn[0], endpt[1] - pn[1], endpt[2] - pn[2]); + } else { + dist = dmin; + } + if (dist < dmin) { + nextmove = RMOVE; + dmin = dist; + } + enext2fnext(*searchtet, neightet); + symself(neightet); + pn = oppo(neightet); + if (pn != dummypoint) { + dist = NORM2(endpt[0] - pn[0], endpt[1] - pn[1], endpt[2] - pn[2]); + } else { + dist = dmin; + } + if (dist < dmin) { + nextmove = LMOVE; + dmin = dist; + } + } else { + // Two tets, below horizon and below right, are viable. + nextmove = HMOVE; + sym(*searchtet, neightet); + pn = oppo(neightet); + if (pn != dummypoint) { + dmin = NORM2(endpt[0] - pn[0], endpt[1] - pn[1], endpt[2] - pn[2]); + } else { + dmin = NORM2(xmax - xmin, ymax - ymin, zmax - zmin); + } + enext0fnext(*searchtet, neightet); + symself(neightet); + pn = oppo(neightet); + if (pn != dummypoint) { + dist = NORM2(endpt[0] - pn[0], endpt[1] - pn[1], endpt[2] - pn[2]); + } else { + dist = dmin; + } + if (dist < dmin) { + nextmove = RMOVE; + dmin = dist; + } + } + } else { + if (lori > 0) { + // Two tets, below horizon and below left, are viable. + nextmove = HMOVE; + sym(*searchtet, neightet); + pn = oppo(neightet); + if (pn != dummypoint) { + dmin = NORM2(endpt[0] - pn[0], endpt[1] - pn[1], endpt[2] - pn[2]); + } else { + dmin = NORM2(xmax - xmin, ymax - ymin, zmax - zmin); + } + enext2fnext(*searchtet, neightet); + symself(neightet); + pn = oppo(neightet); + if (pn != dummypoint) { + dist = NORM2(endpt[0] - pn[0], endpt[1] - pn[1], endpt[2] - pn[2]); + } else { + dist = dmin; + } + if (dist < dmin) { + nextmove = LMOVE; + dmin = dist; + } + } else { + // The tet below horizon is chosen. + nextmove = HMOVE; + } + } + } else { + if (rori > 0) { + if (lori > 0) { + // Two tets, below right and below left, are viable. + nextmove = RMOVE; + enext0fnext(*searchtet, neightet); + symself(neightet); + pn = oppo(neightet); + if (pn != dummypoint) { + dmin = NORM2(endpt[0] - pn[0], endpt[1] - pn[1], endpt[2] - pn[2]); + } else { + dmin = NORM2(xmax - xmin, ymax - ymin, zmax - zmin); + } + enext2fnext(*searchtet, neightet); + symself(neightet); + pn = oppo(neightet); + if (pn != dummypoint) { + dist = NORM2(endpt[0] - pn[0], endpt[1] - pn[1], endpt[2] - pn[2]); + } else { + dist = dmin; + } + if (dist < dmin) { + nextmove = LMOVE; + dmin = dist; + } + } else { + // The tet below right is chosen. + nextmove = RMOVE; + } + } else { + if (lori > 0) { + // The tet below left is chosen. + nextmove = LMOVE; + } else { + // 'endpt' lies either on the plane(s) or across face bcd. + if (hori == 0) { + if (rori == 0) { + // pa->'endpt' is COLLINEAR with pa->pb. + return ACROSSVERT; + } + if (lori == 0) { + // pa->'endpt' is COLLINEAR with pa->pc. + enext2self(*searchtet); + esymself(*searchtet); + return ACROSSVERT; + } + // pa->'endpt' crosses the edge pb->pc. + // enextself(*searchtet); + // return ACROSSEDGE; + cop = HCOPLANE; + break; + } + if (rori == 0) { + if (lori == 0) { + // pa->'endpt' is COLLINEAR with pa->pd. + enext0fnextself(*searchtet); // face abd. + enext2self(*searchtet); + esymself(*searchtet); + return ACROSSVERT; + } + // pa->'endpt' crosses the edge pb->pd. + // enext0fnextself(*searchtet); // face abd. + // enextself(*searchtet); + // return ACROSSEDGE; + cop = RCOPLANE; + break; + } + if (lori == 0) { + // pa->'endpt' crosses the edge pc->pd. + // enext2fnextself(*searchtet); // face cad + // enext2self(*searchtet); + // return ACROSSEDGE; + cop = LCOPLANE; + break; + } + // pa->'endpt' crosses the face bcd. + // enextfnextself(*searchtet); + // return ACROSSFACE; + cop = NCOPLANE; + break; + } + } + } + + // Move to the next tet, fix pa as its origin. + if (nextmove == RMOVE) { + fnextself(*searchtet); + } else if (nextmove == LMOVE) { + enext2self(*searchtet); + fnextself(*searchtet); + enextself(*searchtet); + } else { // HMOVE + symedgeself(*searchtet); + enextself(*searchtet); + } + assert(org(*searchtet) == pa); // SELF_CHECK + pb = dest(*searchtet); + pc = apex(*searchtet); + + } // while (1) + + // Either case ACROSSEDGE or ACROSSFACE. + if (b->epsilon > 0) { + // Use tolerance to re-evaluate the orientations. + if (cop != HCOPLANE) { + if (iscoplanar(pa, pb, pc, endpt, hori)) hori = 0; + } + if (cop != RCOPLANE) { + if (iscoplanar(pb, pa, pd, endpt, rori)) rori = 0; + } + if (cop != LCOPLANE) { + if (iscoplanar(pa, pc, pd, endpt, lori)) lori = 0; + } + // It is not possible that all orientations are zero. + assert(!((hori == 0) && (rori == 0) && (lori == 0))); // SELF_CHECK + } + + // Now decide the degenerate cases. + if (hori == 0) { + if (rori == 0) { + // pa->'endpt' is COLLINEAR with pa->pb. + return ACROSSVERT; + } + if (lori == 0) { + // pa->'endpt' is COLLINEAR with pa->pc. + enext2self(*searchtet); + esymself(*searchtet); + return ACROSSVERT; + } + // pa->'endpt' crosses the edge pb->pc. + return ACROSSEDGE; + } + if (rori == 0) { + if (lori == 0) { + // pa->'endpt' is COLLINEAR with pa->pd. + enext0fnextself(*searchtet); // face abd. + enext2self(*searchtet); + esymself(*searchtet); + return ACROSSVERT; + } + // pa->'endpt' crosses the edge pb->pd. + enext0fnextself(*searchtet); // face abd. + esymself(*searchtet); + enextself(*searchtet); + return ACROSSEDGE; + } + if (lori == 0) { + // pa->'endpt' crosses the edge pc->pd. + enext2fnextself(*searchtet); // face cad + esymself(*searchtet); + return ACROSSEDGE; + } + // pa->'endpt' crosses the face bcd. + return ACROSSFACE; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// scoutsegment() Look for a given segment in the tetrahedralization T. // +// // +// Search an edge in the tetrahedralization that matches the given segmment. // +// If such an edge exists, the segment is 'locked' at the edge. 'searchtet' // +// returns this (constrained) edge. Otherwise, the segment is missing. // +// // +// The returned value indicates one of the following cases: // +// - SHAREEDGE, the segment exists and is inserted in T; // +// - ACROSSVERT, the segment intersects a vertex ('refpt'). // +// - ACROSSEDGE, the segment intersects an edge (in 'searchtet'). // +// - ACROSSFACE, the segment crosses a face (in 'searchtet'). // +// // +// If the returned value is ACROSSEDGE or ACROSSFACE, i.e., the segment is // +// missing, 'refpt' returns the reference point for splitting thus segment, // +// 'searchtet' returns a tet containing the 'refpt'. // +// // +/////////////////////////////////////////////////////////////////////////////// + +enum tetgenmesh::intersection tetgenmesh::scoutsegment(face* sseg, + triface* searchtet, point* refpt) +{ + triface neightet, reftet; + face splitsh, checkseg; + point startpt, endpt; + point pa, pb, pc, pd; + enum location loc; + enum intersection dir; + REAL angmax, ang; + long facecount; + int types[2], poss[4]; + int pos, i; + + tetrahedron ptr; + shellface sptr; + int *iptr, tver; + + startpt = sorg(*sseg); + endpt = sdest(*sseg); + + // Is 'searchtet' a valid handle? + if (searchtet->tet == NULL) { + point2tetorg(startpt, *searchtet); + } else { + assert(org(*searchtet) == startpt); // SELF_CHECK + } + + if (b->verbose > 1) { + printf(" Scout seg (%d, %d).\n", pointmark(startpt), pointmark(endpt)); + } + + dir = finddirection(searchtet, endpt); + + if (dir == ACROSSVERT) { + pd = dest(*searchtet); + if (pd == endpt) { + // Found! Insert the segment. + tsspivot(*searchtet, checkseg); // SELF_CHECK + if (checkseg.sh == NULL) { + // Let the segment remember an adjacent tet. + sstbond(*sseg, *searchtet); + neightet = *searchtet; + do { + tssbond1(neightet, *sseg); + fnextself(neightet); + } while (neightet.tet != searchtet->tet); + } else { + // Collision! This can happy during facet recovery. + // See fig/dump-cavity-case19, -case20. + assert(checkseg.sh == sseg->sh); // SELF_CHECK + } + // The job is done. + return SHAREEDGE; + } else { + // A point is on the path. + *refpt = pd; + return ACROSSVERT; + } + } + + if (b->verbose > 1) { + printf(" Scout ref point of seg (%d, %d).\n", pointmark(startpt), + pointmark(endpt)); + } + facecount = across_face_count; + + enextfnextself(*searchtet); // Go to the opposite face. + symedgeself(*searchtet); // Enter the adjacent tet. + + pa = org(*searchtet); + angmax = interiorangle(pa, startpt, endpt, NULL); + *refpt = pa; + pb = dest(*searchtet); + ang = interiorangle(pb, startpt, endpt, NULL); + if (ang > angmax) { + angmax = ang; + *refpt = pb; + } + + // Check whether two segments are intersecting. + if (dir == ACROSSEDGE) { + tsspivot(*searchtet, checkseg); + if (checkseg.sh != NULL) { + printf("Error: Invalid PLC. Two segments intersect.\n"); + startpt = farsorg(*sseg); + endpt = farsdest(*sseg); + pa = farsorg(checkseg); + pb = farsdest(checkseg); + printf(" 1st: (%d, %d), 2nd: (%d, %d).\n", pointmark(startpt), + pointmark(endpt), pointmark(pa), pointmark(pb)); + terminatetetgen(1); + } + across_edge_count++; + } + + pc = apex(*searchtet); + ang = interiorangle(pc, startpt, endpt, NULL); + if (ang > angmax) { + angmax = ang; + *refpt = pc; + } + reftet = *searchtet; // Save the tet containing the refpt. + + // Search intersecting faces along the segment. + while (1) { + + pd = oppo(*searchtet); + assert(pd != dummypoint); // SELF_CHECK + + if (b->verbose > 2) { + printf(" Passing face (%d, %d, %d, %d), dir(%d).\n", pointmark(pa), + pointmark(pb), pointmark(pc), pointmark(pd), (int) dir); + } + across_face_count++; + + // Stop if we meet 'endpt'. + if (pd == endpt) break; + + ang = interiorangle(pd, startpt, endpt, NULL); + if (ang > angmax) { + angmax = ang; + *refpt = pd; + reftet = *searchtet; + } + + // Find a face intersecting the segment. + if (dir == ACROSSFACE) { + // One of the three oppo faces in 'searchtet' intersects the segment. + neightet.tet = searchtet->tet; + neightet.ver = 0; + for (i = 0; i < 3; i++) { + neightet.loc = locpivot[searchtet->loc][i]; + pa = org(neightet); + pb = dest(neightet); + pc = apex(neightet); + pd = oppo(neightet); // The above point. + if (tri_edge_test(pa, pb, pc, startpt, endpt, pd, 1, types, poss)) { + dir = (enum intersection) types[0]; + pos = poss[0]; + break; + } else { + dir = DISJOINT; + pos = 0; + } + } + assert(dir != DISJOINT); // SELF_CHECK + } else { // dir == ACROSSEDGE + // Check the two opposite faces (of the edge) in 'searchtet'. + neightet = *searchtet; + neightet.ver = 0; + for (i = 0; i < 2; i++) { + neightet.loc = locverpivot[searchtet->loc][searchtet->ver][i]; + pa = org(neightet); + pb = dest(neightet); + pc = apex(neightet); + pd = oppo(neightet); // The above point. + if (tri_edge_test(pa, pb, pc, startpt, endpt, pd, 1, types, poss)) { + dir = (enum intersection) types[0]; + pos = poss[0]; + break; + } else { + dir = DISJOINT; + pos = 0; + } + } + if (dir == DISJOINT) { + // No intersection. Go to the next tet. + dir = ACROSSEDGE; + fnextself(*searchtet); + continue; + } + } + + if (dir == ACROSSVERT) { + // This segment passing a vertex. Choose it and return. + for (i = 0; i < pos; i++) { + enextself(neightet); + } + pd = org(neightet); + if (b->verbose > 2) { + angmax = interiorangle(pd, startpt, endpt, NULL); + } + *refpt = pd; + break; + } + if (dir == ACROSSEDGE) { + // Get the edge intersects with the segment. + for (i = 0; i < pos; i++) { + enextself(neightet); + } + } + // Go to the next tet. + symedge(neightet, *searchtet); + + if (dir == ACROSSEDGE) { + // Check whether two segments are intersecting. + tsspivot(*searchtet, checkseg); + if (checkseg.sh != NULL) { + printf("Error: Invalid PLC! Two segments intersect.\n"); + startpt = farsorg(*sseg); + endpt = farsdest(*sseg); + pa = farsorg(checkseg); + pb = farsdest(checkseg); + printf(" 1st: (%d, %d), 2nd: (%d, %d).\n", pointmark(startpt), + pointmark(endpt), pointmark(pa), pointmark(pb)); + terminatetetgen(1); + } + across_edge_count++; + } + + } // while (1) + + // dir is either ACROSSVERT, or ACROSSEDGE, or ACROSSFACE. + if (b->verbose > 2) { + printf(" Refpt %d (%g), visited %ld faces.\n", pointmark(*refpt), + angmax / PI * 180.0, (int) dir, across_face_count - facecount); + } + if (across_face_count - facecount > across_max_count) { + across_max_count = across_face_count - facecount; + } + + *searchtet = reftet; + return dir; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// getsegmentsplitpoint() Calculate a split point in the given segment. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::getsegmentsplitpoint(face* sseg, point refpt, REAL* vt) +{ + point ei, ej, ek; + REAL split, L, d, d1, d2, d3; + int stype, sign; + int i; + + // Decide the type of this segment. + sign = 1; + ei = sorg(*sseg); + ej = sdest(*sseg); + + if (getpointtype(ei) == ACUTEVERTEX) { + if (getpointtype(ej) == ACUTEVERTEX) { + // Both ei and ej are ACUTEVERTEX. + stype = 0; + } else { + // ej is either a RIDGEVERTEX or a STEINERVERTEX. + stype = 1; + } + } else { + if (getpointtype(ei) == RIDGEVERTEX) { + if (getpointtype(ej) == ACUTEVERTEX) { + stype = 1; sign = -1; + } else { + if (getpointtype(ej) == RIDGEVERTEX) { + // Both ei and ej are non-acute. + stype = 0; + } else { + // ej is a STEINERVETEX. + ek = farsdest(*sseg); + if (getpointtype(ek) == ACUTEVERTEX) { + stype = 1; sign = -1; + } else { + stype = 0; + } + } + } + } else { + // ei is a STEINERVERTEX. + if (getpointtype(ej) == ACUTEVERTEX) { + stype = 1; sign = -1; + } else { + ek = farsorg(*sseg); + if (getpointtype(ej) == RIDGEVERTEX) { + if (getpointtype(ek) == ACUTEVERTEX) { + stype = 1; + } else { + stype = 0; + } + } else { + // Both ei and ej are STEINERVETEXs. ei has priority. + if (getpointtype(ek) == ACUTEVERTEX) { + stype = 1; + } else { + ek = farsdest(*sseg); + if (getpointtype(ek) == ACUTEVERTEX) { + stype = 1; sign = -1; + } else { + stype = 0; + } + } + } + } + } + } + + // Adjust the endpoints: ei, ej. + if (sign == -1) { + sesymself(*sseg); + ei = sorg(*sseg); + ej = sdest(*sseg); + } + + if (b->verbose > 1) { + printf(" Split a type-%d seg(%d, %d) ref(%d)", stype, + pointmark(ei), pointmark(ej), pointmark(refpt)); + if (stype) { + ek = farsorg(*sseg); + printf(" ek(%d)", pointmark(ek)); + } + printf(".\n"); + } + + // Calculate the split point. + if (stype == 0) { + // Use rule-1. + L = DIST(ei, ej); + d1 = DIST(ei, refpt); + d2 = DIST(ej, refpt); + if (d1 < d2) { + // Choose ei as center. + if (d1 < 0.5 * L) { + split = d1 / L; + // Adjust split if it is close to middle. (2009-02-01) + if ((split > 0.4) || (split < 0.6)) split = 0.5; + } else { + split = 0.5; + } + for (i = 0; i < 3; i++) { + vt[i] = ei[i] + split * (ej[i] - ei[i]); + } + } else { + // Choose ej as center. + if (d2 < 0.5 * L) { + split = d2 / L; + // Adjust split if it is close to middle. (2009-02-01) + if ((split > 0.4) || (split < 0.6)) split = 0.5; + } else { + split = 0.5; + } + for (i = 0; i < 3; i++) { + vt[i] = ej[i] + split * (ei[i] - ej[i]); + } + } + r1count++; + } else { + // Use rule-2. + ek = farsorg(*sseg); + L = DIST(ek, ej); + d = DIST(ek, refpt); + split = d / L; + for (i = 0; i < 3; i++) { + vt[i] = ek[i] + split * (ej[i] - ek[i]); + } + d1 = DIST(vt, refpt); + d2 = DIST(vt, ej); + if (d1 > d2) { + // Use rule-3. + d3 = DIST(ei, refpt); + if (d1 < 0.5 * d3) { + split = (d - d1) / L; + } else { + split = (d - 0.5 * d3) / L; + } + for (i = 0; i < 3; i++) { + vt[i] = ek[i] + split * (ej[i] - ek[i]); + } + } + d1 > d2 ? r3count++ : r2count++; + } + + if (b->verbose > 1) { + printf(" split (%g), vt (%g, %g, %g).\n", split, vt[0], vt[1], vt[2]); + } +} + +// This function only use Rule-1 to split the segment. refpt may be NULL. +// Added 2009-05-20. + +void tetgenmesh::getsegmentsplitpoint2(face* sseg, point refpt, REAL* vt) +{ + point ei, ej; + REAL split, L, d, d1, d2; + int i; + + ei = sorg(*sseg); + ej = sdest(*sseg); + + if (b->verbose > 1) { + printf(" Split seg(%d, %d) ref(%d).\n", pointmark(ei), pointmark(ej), + refpt ? pointmark(refpt) : -1); + } + + if (refpt != NULL) { + L = DIST(ei, ej); + d1 = DIST(ei, refpt); + d2 = DIST(ej, refpt); + if (d1 < d2) { + // Choose ei as center. + if (d1 < 0.5 * L) { + split = d1 / L; + // Adjust split if it is close to middle. (2009-02-01) + // if ((split > 0.4) || (split < 0.6)) split = 0.5; + } else { + split = 0.5; + } + for (i = 0; i < 3; i++) { + vt[i] = ei[i] + split * (ej[i] - ei[i]); + } + } else { + // Choose ej as center. + if (d2 < 0.5 * L) { + split = d2 / L; + // Adjust split if it is close to middle. (2009-02-01) + // if ((split > 0.4) || (split < 0.6)) split = 0.5; + } else { + split = 0.5; + } + for (i = 0; i < 3; i++) { + vt[i] = ej[i] + split * (ei[i] - ej[i]); + } + } + } else { + split = 0.5; + for (i = 0; i < 3; i++) { + vt[i] = ei[i] + split * (ej[i] - ei[i]); + } + } + r1count++; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// getsegmentsplitpoint3() Calculate a split point in the given segment. // +// // +// This routine does not check far origin and far dest. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::getsegmentsplitpoint3(face* sseg, point refpt, REAL* vt) +{ + point ei, ej, ek; + REAL split, L, d, d1, d2, d3; + int stype, sign; + int i; + + // Decide the type of this segment. + sign = 1; + ei = sorg(*sseg); + ej = sdest(*sseg); + ek = NULL; + + // ei can be an ACUTEVERTEX, or a RIDGEVERTEX, or a STEINERVERTEX. + if (getpointtype(ei) == ACUTEVERTEX) { + if (getpointtype(ej) == ACUTEVERTEX) { + // ej is an ACUTEVERTEX. + stype = 0; + } else { + // ej is either a RIDGEVERTEX or a STEINERVERTEX. + stype = 1; + ek = ei; + } + } else { + if (getpointtype(ei) == RIDGEVERTEX) { + if (getpointtype(ej) == ACUTEVERTEX) { + // ej is an ACUTEVERTEX. + stype = 1; + sign = -1; // Exchange ei and ej. + ek = ej; + } else { + if (getpointtype(ej) == RIDGEVERTEX) { + // ej is a RIDGEVERTEX. + stype = 0; + } else { + // ej is a STEINERVETEX. + stype = 0; + /* ek = farsdest(*sseg); + if (getpointtype(ek) == ACUTEVERTEX) { + stype = 1; sign = -1; + } else { + stype = 0; + }*/ + } + } + } else { + // ei is a STEINERVERTEX. + if (getpointtype(ej) == ACUTEVERTEX) { + stype = 1; + sign = -1; // Exchange ei and ej. + ek = ej; + } else { + // ej is either a RIDGEVERTEX or STEINERVERTEX. + stype = 0; + /*ek = farsorg(*sseg); + if (getpointtype(ej) == RIDGEVERTEX) { + if (getpointtype(ek) == ACUTEVERTEX) { + stype = 1; + } else { + stype = 0; + } + } else { + // Both ei and ej are STEINERVETEXs. ei has priority. + if (getpointtype(ek) == ACUTEVERTEX) { + stype = 1; + } else { + ek = farsdest(*sseg); + if (getpointtype(ek) == ACUTEVERTEX) { + stype = 1; sign = -1; + } else { + stype = 0; + } + } + }*/ + } + } + } + + // Adjust the endpoints: ei, ej. + if (sign == -1) { + sesymself(*sseg); + ei = sorg(*sseg); + ej = sdest(*sseg); + } + + if (b->verbose > 1) { + printf(" Split a type-%d seg(%d, %d) ref(%d).\n", stype, + pointmark(ei), pointmark(ej), pointmark(refpt)); + } + + // Calculate the split point. + if (stype == 0) { + // Use rule-1. + L = DIST(ei, ej); + d1 = DIST(ei, refpt); + d2 = DIST(ej, refpt); + if (d1 < d2) { + // Choose ei as center. + if (d1 < 0.5 * L) { + split = d1 / L; + // Adjust split if it is close to middle. (2009-02-01) + if ((split > 0.4) || (split < 0.6)) split = 0.5; + } else { + split = 0.5; + } + for (i = 0; i < 3; i++) { + vt[i] = ei[i] + split * (ej[i] - ei[i]); + } + } else { + // Choose ej as center. + if (d2 < 0.5 * L) { + split = d2 / L; + // Adjust split if it is close to middle. (2009-02-01) + if ((split > 0.4) || (split < 0.6)) split = 0.5; + } else { + split = 0.5; + } + for (i = 0; i < 3; i++) { + vt[i] = ej[i] + split * (ei[i] - ej[i]); + } + } + r1count++; + } else { + // Use rule-2. + // ek = farsorg(*sseg); + L = DIST(ek, ej); + d = DIST(ek, refpt); + split = d / L; + for (i = 0; i < 3; i++) { + vt[i] = ek[i] + split * (ej[i] - ek[i]); + } + d1 = DIST(vt, refpt); + d2 = DIST(vt, ej); + if (d1 > d2) { + // Use rule-3. + d3 = DIST(ei, refpt); + if (d1 < 0.5 * d3) { + split = (d - d1) / L; + } else { + split = (d - 0.5 * d3) / L; + } + for (i = 0; i < 3; i++) { + vt[i] = ek[i] + split * (ej[i] - ek[i]); + } + } + d1 > d2 ? r3count++ : r2count++; + } + + if (b->verbose > 1) { + printf(" split (%g), vt (%g, %g, %g).\n", split, vt[0], vt[1], vt[2]); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// delaunizesegments() Recover segments in a Delaunay tetrahedralization. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::delaunizesegments() +{ + triface searchtet; + face splitsh; + face *psseg, sseg; + point refpt, newpt; + enum intersection dir; + bool visflag; + + // Loop until 'subsegstack' is empty. + while (subsegstack->objects > 0l) { + // seglist is used as a stack. + subsegstack->objects--; + psseg = (face *) fastlookup(subsegstack, subsegstack->objects); + sseg = *psseg; + + if (!sinfected(sseg)) continue; // Not a missing segment. + suninfect(sseg); + + // Insert the segment. + searchtet.tet = NULL; + dir = scoutsegment(&sseg, &searchtet, &refpt); + + if (dir != SHAREEDGE) { + // The segment is missing, split it. + spivot(sseg, splitsh); + if (dir != ACROSSVERT) { + // Create the new point. + makepoint(&newpt); + // getsegmentsplitpoint(&sseg, refpt, newpt); + getsegmentsplitpoint3(&sseg, refpt, newpt); + setpointtype(newpt, STEINERVERTEX); + // Split the segment by newpt. + sinsertvertex(newpt, &splitsh, &sseg, true, false); + // Insert newpt into the DT. If 'checksubfaces == 1' the current + // mesh is constrained Delaunay (but may not Delaunay). + visflag = (checksubfaces == 1); + insertvertex(newpt, &searchtet, true, visflag, false, false); + } else { + /*if (getpointtype(refpt) != ACUTEVERTEX) { + setpointtype(refpt, RIDGEVERTEX); + } + // Split the segment by refpt. + sinsertvertex(refpt, &splitsh, &sseg, true, false);*/ + printf("Error: Invalid PLC! A point and a segment intersect.\n"); + point pa, pb; + pa = farsorg(sseg); + pb = farsdest(sseg); + printf(" Point: %d. Segment: (%d, %d).\n", pointmark(refpt), + pointmark(pa), pointmark(pb)); + terminatetetgen(1); + } + } + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// scoutsubface() Look for a given subface in the tetrahedralization T. // +// // +// 'ssub' is the subface, denoted as abc. If abc exists in T, it is 'locked' // +// at the place where the two tets sharing at it. // +// // +// The returned value indicates one of the following cases: // +// - SHAREFACE, abc exists and is inserted; // +// - TOUCHEDGE, a vertex (the origin of 'searchtet') lies on ab. // +// - EDGETRIINT, all three edges of abc are missing. // +// - ACROSSTET, a tet (in 'searchtet') crosses the facet containg abc. // +// // +// If the retunred value is ACROSSTET, the subface is missing. 'searchtet' // +// returns a tet which shares the same edge as 'pssub'. // +// // +/////////////////////////////////////////////////////////////////////////////// + +enum tetgenmesh::intersection tetgenmesh::scoutsubface(face* pssub, + triface* searchtet) +{ + triface spintet; + face checksh; + point pa, pb, pc, pd; + enum intersection dir; + int i; + + tetrahedron ptr; + int tver; + + if (searchtet->tet == NULL) { + // Search an edge of 'ssub' in tetrahedralization. + pssub->shver = 0; + for (i = 0; i < 3; i++) { + pa = sorg(*pssub); + pb = sdest(*pssub); + // Do not search dummypoint. + assert(pa != dummypoint); // SELF_CHECK + assert(pb != dummypoint); // SELF_CHECK + // Get a tet whose origin is pa. + decode(point2tet(pa), *searchtet); + assert(searchtet->tet != NULL); // SELF_CHECK + assert(searchtet->tet[4] != NULL); // SELF_CHECK + if ((point) searchtet->tet[4] == pa) { + searchtet->loc = 0; searchtet->ver = 0; + } else if ((point) searchtet->tet[5] == pa) { + searchtet->loc = 0; searchtet->ver = 2; + } else if ((point) searchtet->tet[6] == pa) { + searchtet->loc = 0; searchtet->ver = 4; + } else { + if ((point) searchtet->tet[7] != pa) { + printf("Error: Bad pt-to-tet at %d\n", pointmark(pa)); + assert(0); + } + searchtet->loc = 1; searchtet->ver = 2; + } + // Search the edge from pa->pb. + dir = finddirection(searchtet, pb); + if (dir == ACROSSVERT) { + if (dest(*searchtet) == pb) { + // Found the edge. Break the loop. + break; + } else { + // A vertex lies on the search edge. Return it. + enextself(*searchtet); + return TOUCHEDGE; + } + } + senextself(*pssub); + } + if (i == 3) { + // None of the three edges exists. + return EDGETRIINT; // ab intersects the face in 'searchtet'. + } + } else { + // 'searchtet' holds the current edge of 'pssub'. + pa = org(*searchtet); + pb = dest(*searchtet); + } + + pc = sapex(*pssub); + + if (b->verbose > 1) { + printf(" Scout subface (%d, %d, %d) (%ld).\n", pointmark(pa), + pointmark(pb), pointmark(pc), subfacstack->objects); + } + + // Searchtet holds edge pa->pb. Search a face with apex pc. + spintet = *searchtet; + while (1) { + fnextself(spintet); + pd = apex(spintet); // pd may be dummypoint. Search the face anyway. + if (pd == pc) { + // Found! Insert the subface. + tspivot(spintet, checksh); // SELF_CHECK + if (checksh.sh == NULL) { + tsbond(spintet, *pssub); + symedgeself(spintet); + tspivot(spintet, checksh); // SELF_CHECK + assert(checksh.sh == NULL); // SELF_CHECK + tsbond(spintet, *pssub); + return SHAREFACE; + } else { + // Another subface is laready inserted. + assert(checksh.sh != pssub->sh); // SELF_CHECK + // Comment: This is possible when there are faked tets. + *searchtet = spintet; + return COLLISIONFACE; + } + } + if (pd == apex(*searchtet)) break; + } + + return ACROSSTET; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// scoutcrosstet() Scout a tetrahedron across a facet. // +// // +// A subface (abc) of the facet (F) is given in 'pssub', 'searchtet' holds // +// the edge ab, it is the tet starting the search. 'facpoints' contains all // +// points which are co-facet with a, b, and c. // +// // +// The subface (abc) was produced by a 2D CDT algorithm under the Assumption // +// that F is flat. In real data, however, F may not be strictly flat. Hence // +// a tet (abde) that crosses abc may be in one of the two cases: (i) abde // +// intersects F in its interior, or (ii) abde intersects F on its boundary. // +// In case (i) F (or part of it) is missing in DT and needs to be recovered. // +// In (ii) F is not missing, the surface mesh of F needs to be adjusted. // +// // +// This routine distinguishes the two cases by the returned value, which is // +// - ACROSSTET, if it is case (i), 'searchtet' is abde, d and e lies below // +// and above abc, respectively, neither d nor e is dummypoint; or // +// - ACROSSFACE, if it is case (ii), 'searchtet' is abde, where the face // +// abd intersects abc, i.e., d is co-facet with abc, e may be co-facet // +// with abc or dummypoint. // +// // +/////////////////////////////////////////////////////////////////////////////// + +enum tetgenmesh::intersection tetgenmesh::scoutcrosstet(face *pssub, + triface* searchtet, arraypool* facpoints) +{ + triface spintet, crossface; + point pa, pb, pc, pd, pe; + REAL ori, ori1, len, n[3]; + REAL r, dr, drmin; + bool cofacetflag; + int i; + + if (facpoints != NULL) { + // Infect all vertices of the facet. + for (i = 0; i < facpoints->objects; i++) { + pd = * (point *) fastlookup(facpoints, i); + pinfect(pd); + } + } + + // Search an edge crossing the facet containing abc. + if (searchtet->ver & 01) { + esymself(*searchtet); // Adjust to 0th edge ring. + sesymself(*pssub); + } + + pa = sorg(*pssub); + pb = sdest(*pssub); + pc = sapex(*pssub); + + // Search an apex lies below the subface. Note that such apex may not + // exist which indicates there is a co-facet apex. + cofacetflag = false; + pd = apex(*searchtet); + spintet = *searchtet; + while (1) { + if (pd != dummypoint) { + ori = orient3d(pa, pb, pc, pd); + if ((ori != 0) && pinfected(pd)) { + ori = 0; // Force d be co-facet with abc. + } + if (ori > 0) { + break; // Found a lower point. + } + } + fnextself(spintet); // Go to the next face. + pd = apex(spintet); + if (pd == apex(*searchtet)) { + cofacetflag = true; break; // Not found. + } + } + if (!cofacetflag) { + // Search a tet whose apex->oppo crosses the facet containig abc. + while (1) { + pe = oppo(spintet); + if (pe != dummypoint) { + ori = orient3d(pa, pb, pc, pe); + if ((ori != 0) && pinfected(pe)) { + ori = 0; // Force pe be co-facet with abc. + } + if (ori < 0) { + break; // stop at pd->pe. + } + if (ori == 0) { + cofacetflag = true; break; // Found a co-facet point. + } + } + fnextself(spintet); + } + *searchtet = spintet; + // Now if "cofacetflag != true", searchtet contains a cross tet (abde), + // where d and e lie below and above abc, respectively, and + // orient3d(a, b, d, e) < 0. + } + + if (cofacetflag) { + // There are co-facet points. Calculate a point above the subface. + facenormal(pa, pb, pc, n, 1); + len = sqrt(DOT(n, n)); + n[0] /= len; + n[1] /= len; + n[2] /= len; + len = DIST(pa, pb); + len += DIST(pb, pc); + len += DIST(pc, pa); + len /= 3.0; + dummypoint[0] = pa[0] + len * n[0]; + dummypoint[1] = pa[1] + len * n[1]; + dummypoint[2] = pa[2] + len * n[2]; + // Search a co-facet point d, s.t. (i) [a, b, d] intersects [a, b, c], + // AND (ii) a, b, c, d has the closet circumradius of [a, b, c]. + // NOTE: (ii) is needed since there may be several points satisfy (i). + circumsphere(pa, pb, pc, NULL, n, &r); + crossface.tet = NULL; + pe = apex(*searchtet); + spintet = *searchtet; + while (1) { + pd = apex(spintet); + if (pd != dummypoint) { + ori = orient3d(pa, pb, pc, pd); + if ((ori == 0) || pinfected(pd)) { + ori1 = orient3d(pa, pb, dummypoint, pd); + if (ori1 > 0) { + // [a, b, d] intersects with [a, b, c]. + if (pinfected(pd)) { + len = DIST(n, pd); + dr = fabs(len - r); + if (crossface.tet == NULL) { + // This is the first cross face. + crossface = spintet; + drmin = dr; + } else { + if (dr < drmin) { + crossface = spintet; + drmin = dr; + } + } + } else { + assert(ori == 0); // SELF_CHECK + // Found a coplanar but not co-facet point (pd). + printf("Error: Invalid PLC! A point and a subface intersect\n"); + // get_origin_facet_corners(pssub, &pa, &pb, &pc); + printf(" Point %d. Subface (#%d) (%d, %d, %d)\n", + pointmark(pd), getshellmark(*pssub), pointmark(pa), + pointmark(pb), pointmark(pc)); + terminatetetgen(1); + } + } + } + } + fnextself(spintet); // Go to the next face. + // assert(apex(spintet) != pe); // SELF_CHECK + if (apex(spintet) == pe) { + break; + } + } + if(crossface.tet == NULL) { + assert(crossface.tet != NULL); // Not handled yet. + } + *searchtet = crossface; + dummypoint[0] = dummypoint[1] = dummypoint[2] = 0; + } + + if (cofacetflag) { + if (b->verbose > 1) { + printf(" Found a co-facet face (%d, %d, %d) op (%d).\n", + pointmark(pa), pointmark(pb), pointmark(apex(*searchtet)), + pointmark(oppo(*searchtet))); + } + if (facpoints != NULL) { + // Unmark all facet vertices. + for (i = 0; i < facpoints->objects; i++) { + pd = * (point *) fastlookup(facpoints, i); + puninfect(pd); + } + } + // Comment: Now no vertex is infected. + if (getpointtype(apex(*searchtet)) == VOLVERTEX) { + // A vertex lies on the facet. + enext2self(*searchtet); // org(*searchtet) == pd + return TOUCHFACE; + } + return ACROSSFACE; + } else { + // Return a crossing tet. + if (b->verbose > 1) { + printf(" Found a crossing tet (%d, %d, %d, %d).\n", pointmark(pa), + pointmark(pb), pointmark(apex(spintet)), pointmark(pe)); + } + // Comment: if facpoints != NULL, co-facet vertices are stll infected. + // They will be uninfected in formcavity(); + return ACROSSTET; // abc intersects the volume of 'searchtet'. + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// recoversubfacebyflips() Recover a subface by flips in the surface mesh. // +// // +// A subface [a, b, c] ('pssub') intersects with a face [a, b, d] ('cross- // +// face'), where a, b, c, and d belong to the same facet. It indicates that // +// the face [a, b, d] should appear in the surface mesh. // +// // +// This routine recovers [a, b, d] in the surface mesh through a sequence of // +// 2-to-2 flips. No Steiner points is needed. 'pssub' returns [a, b, d]. // +// // +// If 'facfaces' is not NULL, all flipped subfaces are queued for recovery. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::recoversubfacebyflips(face* pssub, triface* crossface, + arraypool *facfaces) +{ + triface neightet; + face flipfaces[2]; + face checkseg; + point pa, pb, pc, pd, pe; + REAL ori, len, n[3]; + + tetrahedron ptr; + shellface sptr; + int tver; + + // Get the missing subface is [a, b, c]. + pa = sorg(*pssub); + pb = sdest(*pssub); + pc = sapex(*pssub); + + // The crossface is [a, b, d, e]. + // assert(org(*crossface) == pa); + // assert(dest(*crossface) == pb); + pd = apex(*crossface); + pe = dummypoint; // oppo(*crossface); + + if (pe == dummypoint) { + // Calculate a point above the faces. + facenormal(pa, pb, pd, n, 1); + len = sqrt(DOT(n, n)); + n[0] /= len; + n[1] /= len; + n[2] /= len; + len = DIST(pa, pb); + len += DIST(pb, pd); + len += DIST(pd, pa); + len /= 3.0; + pe[0] = pa[0] + len * n[0]; + pe[1] = pa[1] + len * n[1]; + pe[2] = pa[2] + len * n[2]; + } + + // Adjust face [a, b, c], so that edge [b, c] crosses edge [a, d]. + ori = orient3d(pb, pc, pe, pd); + assert(ori != 0); // SELF_CHECK + + if (ori > 0) { + // Swap a and b. + sesymself(*pssub); + symedgeself(*crossface); + pa = sorg(*pssub); + pb = sdest(*pssub); + if (pe == dummypoint) { + pe[0] = pe[1] = pe[2] = 0; + } + pe = dummypoint; // oppo(*crossface); + } + + while (1) { + + // Flip edge [b, c], edge [a, d] is missing. + senext(*pssub, flipfaces[0]); + sspivot(flipfaces[0], checkseg); // SELF_CHECK + assert(checkseg.sh == NULL); // SELF_CHECK + spivot(flipfaces[0], flipfaces[1]); + + stpivot(flipfaces[1], neightet); + if (neightet.tet != NULL) { + // A recovered subface, clean sub<==>tet connections. + tsdissolve(neightet); + symself(neightet); + tsdissolve(neightet); + stdissolve(flipfaces[1]); + } + + flip22(flipfaces, 0); + + // Add them into list (make ensure that they must be recovered). + facfaces->newindex((void **) &pssub); + *pssub = flipfaces[0]; + facfaces->newindex((void **) &pssub); + *pssub = flipfaces[1]; + + // Find the edge [a, b]. + senext(flipfaces[1], *pssub); + assert(sorg(*pssub) == pa); // SELF_CHECK + assert(sdest(*pssub) == pb); // SELF_CHECK + + pc = sapex(*pssub); + if (pc == pd) break; + + if (pe == dummypoint) { + // Calculate a point above the faces. + facenormal(pa, pb, pd, n, 1); + len = sqrt(DOT(n, n)); + n[0] /= len; + n[1] /= len; + n[2] /= len; + len = DIST(pa, pb); + len += DIST(pb, pd); + len += DIST(pd, pa); + len /= 3.0; + pe[0] = pa[0] + len * n[0]; + pe[1] = pa[1] + len * n[1]; + pe[2] = pa[2] + len * n[2]; + } + + while (1) { + ori = orient3d(pb, pc, pe, pd); + assert(ori != 0); // SELF_CHECK + if (ori > 0) { + senext2self(*pssub); + spivotself(*pssub); + if (sorg(*pssub) != pa) sesymself(*pssub); + pb = sdest(*pssub); + pc = sapex(*pssub); + continue; + } + break; + } + } + + if (pe == dummypoint) { + pe[0] = pe[1] = pe[2] = 0; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// formcavity() Form the cavity of a missing region. // +// // +// A missing region R is a set of co-facet (co-palanr) subfaces. 'pssub' is // +// a missing subface [a, b, c]. 'crosstets' contains only one tet, [a, b, d, // +// e], where d and e lie below and above [a, b, c], respectively. Other // +// crossing tets are sought from this tet and saved in 'crosstets'. // +// // +// The cavity C is divided into two parts by R,one at top and one at bottom. // +// 'topfaces' and 'botfaces' return the upper and lower boundary faces of C. // +// 'toppoints' contains vertices of 'crosstets' in the top part of C, and so // +// does 'botpoints'. Both 'toppoints' and 'botpoints' contain vertices of R. // +// // +// NOTE: 'toppoints' may contain points which are not vertices of any top // +// faces, and so may 'botpoints'. Such points may belong to other facets and // +// need to be present after the recovery of this cavity (P1029.poly). // +// // +// A pair of boundary faces: 'firsttopface' and 'firstbotface', are saved. // +// They share the same edge in the boundary of the missing region. // +// // +// 'facpoints' contains all vertices of the facet containing R. They are // +// used for searching the crossing tets. On input all vertices are infected. // +// They are uninfected after the cavity is formed. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::formcavity(face *pssub, arraypool* crosstets, + arraypool* topfaces, arraypool* botfaces, arraypool* toppoints, + arraypool* botpoints, arraypool* facpoints) +{ + arraypool *crossedges; + triface *parytet, crosstet, spintet, neightet, faketet; + face neighsh, checksh; + face checkseg; + point pa, pb, pc, pf, pg; + point *ppt; + REAL ori; + int i, j; + + int *iptr; + + // Get the missing subface abc. + pa = sorg(*pssub); + pb = sdest(*pssub); + pc = sapex(*pssub); + + // Comment: Now all facet vertices are infected. + + // Get a crossing tet abde. + parytet = (triface *) fastlookup(crosstets, 0); // face abd. + // The edge de crosses the facet. d lies below abc. + enext2fnext(*parytet, crosstet); + enext2self(crosstet); + esymself(crosstet); // the edge d->e at face [d,e,a] + infect(crosstet); + *parytet = crosstet; // Save it in list. + + // Temporarily re-use 'topfaces'. + crossedges = topfaces; + crossedges->newindex((void **) &parytet); + *parytet = crosstet; + + // Collect all crossing tets. Each cross tet is saved in the standard + // form deab, where de is a corrsing edge, orient3d(d,e,a,b) < 0. + // NOTE: hull tets may be collected. See fig/dump-cavity-case2a(b).lua. + // Make sure that neither d nor e is dummypoint. + for (i = 0; i < crossedges->objects; i++) { + crosstet = * (triface *) fastlookup(crossedges, i); + // It may already be tested. + if (!edgemarked(crosstet)) { + // Collect all tets sharing at the edge. + pg = apex(crosstet); + spintet = crosstet; + while (1) { + // Mark this edge as tested. + markedge(spintet); + if (!infected(spintet)) { + infect(spintet); + crosstets->newindex((void **) &parytet); + *parytet = spintet; + } + // Go to the neighbor tet. + fnextself(spintet); + // Check the validity of the PLC. + tspivot(spintet, checksh); + if (checksh.sh != NULL) { + printf("Error: Invalid PLC! Two subfaces intersect.\n"); + printf(" 1st (#%4d): (%d, %d, %d)\n", getshellmark(*pssub), + pointmark(pa), pointmark(pb), pointmark(pc)); + printf(" 2nd (#%4d): (%d, %d, %d)\n", getshellmark(checksh), + pointmark(sorg(checksh)), pointmark(sdest(checksh)), + pointmark(sapex(checksh))); + terminatetetgen(1); + } + if (apex(spintet) == pg) break; + } + // Detect new cross edges. + while (1) { + // Remember: spintet is edge d->e, d lies below abc. + pf = apex(spintet); + if (pf != dummypoint) { // Do not grab a hull edge. + if (!pinfected(pf)) { + // There exist a crossing edge, either d->f, or f->e. + ori = orient3d(pa, pb, pc, pf); + if (ori == 0) { + printf("Error: Invalid PLC! Point and subface intersect.\n"); + printf(" Point %d, subface (#%4d): (%d, %d, %d)\n", + pointmark(pf), getshellmark(*pssub), pointmark(pa), + pointmark(pb), pointmark(pc)); + terminatetetgen(1); + } + if (ori < 0) { + // The edge d->f corsses the facet. + enext2fnext(spintet, neightet); + esymself(neightet); // d->f. + } else { + // The edge f->e crosses the face. + enextfnext(spintet, neightet); + esymself(neightet); // f->e. + } + if (!edgemarked(neightet)) { + // Add a new cross edge. + crossedges->newindex((void **) &parytet); + *parytet = neightet; + } + } + } + fnextself(spintet); + if (apex(spintet) == pg) break; + } + } + } + + // Unmark all facet vertices. + for (i = 0; i < facpoints->objects; i++) { + ppt = (point *) fastlookup(facpoints, i); + puninfect(*ppt); + } + + // Comments: Now no vertex is marked. Next we will mark vertices which + // belong to the top and bottom boundary faces of the cavity and put + // them in 'toppopints' and 'botpoints', respectively. + + // All cross tets are found. Unmark cross edges. + for (i = 0; i < crossedges->objects; i++) { + crosstet = * (triface *) fastlookup(crossedges, i); + if (edgemarked(crosstet)) { + // Add the vertices of the cross edge [d, e] in lists. It must be + // that d lies below the facet (i.e., its a bottom vertex). + // Note that a cross edge contains no dummypoint. + pf = org(crosstet); + assert(pf != dummypoint); // SELF_CHECK + if (!pinfected(pf)) { + pinfect(pf); + botpoints->newindex((void **) &ppt); // Add a bottom vertex. + *ppt = pf; + } + pf = dest(crosstet); + assert(pf != dummypoint); // SELF_CHECK + if (!pinfected(pf)) { + pinfect(pf); + toppoints->newindex((void **) &ppt); // Add a top vertex. + *ppt = pf; + } + // Unmark this edge in all tets containing it. + pg = apex(crosstet); + spintet = crosstet; + while (1) { + assert(edgemarked(spintet)); // SELF_CHECK + unmarkedge(spintet); + fnextself(spintet); // Go to the neighbor tet. + if (apex(spintet) == pg) break; + } + } + } + + if (b->verbose > 1) { + printf(" Formed cavity: %ld (%ld) cross tets (edges).\n", + crosstets->objects, crossedges->objects); + } + crossedges->restart(); + + // Find a pair of cavity boundary faces from the top and bottom sides of + // the facet each, and they share the same edge. Save them in the + // global variables: firsttopface, firstbotface. They will be used in + // fillcavity() for gluing top and bottom new tets. + for (i = 0; i < crosstets->objects; i++) { + crosstet = * (triface *) fastlookup(crosstets, i); + enextfnext(crosstet, spintet); + enextself(spintet); + symedge(spintet, neightet); + if (!infected(neightet)) { + // A top face. + firsttopface = neightet; + } else { + continue; // Go to the next cross tet. + } + enext2fnext(crosstet, spintet); + enext2self(spintet); + symedge(spintet, neightet); + if (!infected(neightet)) { + // A bottom face. + firstbotface = neightet; + } else { + continue; + } + break; + } + assert(i < crosstets->objects); // SELF_CHECK + + // Collect the top and bottom faces and the middle vertices. Since all top + // and bottom vertices have been marked in above. Unmarked vertices are + // middle vertices. + // NOTE 1: Hull tets may be collected. Process them as normal one. + // (see fig/dump-cavity-case2.lua.) + // NOTE 2: Some previously recovered subfaces may be completely + // contained in a cavity (see fig/dump-cavity-case6.lua). In such case, + // we create two faked tets to hold this subface, one at each side. + // The faked tets will be removed in fillcavity(). + for (i = 0; i < crosstets->objects; i++) { + crosstet = * (triface *) fastlookup(crosstets, i); + enextfnext(crosstet, spintet); + enextself(spintet); + symedge(spintet, neightet); + if (!infected(neightet)) { + // A top face. + topfaces->newindex((void **) &parytet); + *parytet = neightet; + } else { + // Check if this side is a subface. + tspivot(spintet, neighsh); + if (neighsh.sh != NULL) { + // Found a subface (inside the cavity)! + maketetrahedron(&faketet); // Create a faked tet. + setorg(faketet, org(spintet)); + setdest(faketet, dest(spintet)); + setapex(faketet, apex(spintet)); + setoppo(faketet, NULL); + tsbond(faketet, neighsh); // Let it hold the subface. + // Add a top face (at faked tet). + topfaces->newindex((void **) &parytet); + *parytet = faketet; + } + } + enext2fnext(crosstet, spintet); + enext2self(spintet); + symedge(spintet, neightet); + if (!infected(neightet)) { + // A bottom face. + botfaces->newindex((void **) &parytet); + *parytet = neightet; + } else { + tspivot(spintet, neighsh); + if (neighsh.sh != NULL) { + // Found a subface (inside the cavity)! + maketetrahedron(&faketet); // Create a faked tet. + setorg(faketet, org(spintet)); + setdest(faketet, dest(spintet)); + setapex(faketet, apex(spintet)); + setoppo(faketet, NULL); + tsbond(faketet, neighsh); // Let it hold the subface. + // Add a bottom face (at faked tet). + botfaces->newindex((void **) &parytet); + *parytet = faketet; + } + } + // Add middle vertices if there are (skip dummypoint). + pf = org(neightet); + if (!pinfected(pf)) { + if (pf != dummypoint) { + pinfect(pf); + botpoints->newindex((void **) &ppt); // Add a bottom vertex. + *ppt = pf; + toppoints->newindex((void **) &ppt); // Add a top vertex. + *ppt = pf; + } + } + pf = dest(neightet); + if (!pinfected(pf)) { + if (pf != dummypoint) { + pinfect(pf); + botpoints->newindex((void **) &ppt); // Add a bottom vertex. + *ppt = pf; + toppoints->newindex((void **) &ppt); // Add a top vertex. + *ppt = pf; + } + } + } + + // Unmark all collected top, bottom, and middle vertices. + for (i = 0; i < toppoints->objects; i++) { + ppt = (point *) fastlookup(toppoints, i); + puninfect(*ppt); + } + for (i = 0; i < botpoints->objects; i++) { + ppt = (point *) fastlookup(botpoints, i); + puninfect(*ppt); + } + // Comments: Now no vertex is marked. +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// delaunizecavity() Fill a cavity by Delaunay tetrahedra. // +// // +// The tetrahedralizing cavity is the half (top or bottom part) of the whole // +// cavity. The boundary faces of the half cavity are given in 'cavfaces', // +// the bounday faces of the internal facet are not given. These faces will // +// be recovered later in fillcavity(). // +// // +// This routine first constructs the DT of the vertices by the Bowyer-Watson // +// algorithm. Then it identifies the boundary faces of the cavity in DT. // +// The DT is returned in 'newtets'. // +// // +/////////////////////////////////////////////////////////////////////////////// + +bool tetgenmesh::delaunizecavity(arraypool *cavpoints, arraypool *cavfaces, + arraypool *cavshells, arraypool *newtets, arraypool *crosstets, + arraypool *misfaces) +{ + triface *parytet, searchtet, neightet, spintet, *parytet1; + face checksh, tmpsh, *parysh; + face checkseg; + point pa, pb, pc, pd, pt[3], *parypt; + // badface *newflipface; + enum intersection dir; + REAL ori; + // int miscount; + int i, j; + + tetrahedron ptr; + int *iptr, tver; + + if (b->verbose > 1) { + printf(" Delaunizing cavity: %ld points, %ld faces.\n", + cavpoints->objects, cavfaces->objects); + } + + // Get four non-coplanar points (no dummypoint). + parytet = (triface *) fastlookup(cavfaces, 0); + pa = org(*parytet); + pb = dest(*parytet); + pc = apex(*parytet); + pinfect(pa); + pinfect(pb); + pinfect(pc); + pd = NULL; + for (i = 1; i < cavfaces->objects; i++) { + parytet = (triface *) fastlookup(cavfaces, i); + pt[0] = org(*parytet); + pt[1] = dest(*parytet); + pt[2] = apex(*parytet); + for (j = 0; j < 3; j++) { + if (pt[j] != dummypoint) { // Do not include a hull point. + if (!pinfected(pt[j])) { + ori = orient3d(pa, pb, pc, pt[j]); + if (ori != 0) { + pd = pt[j]; + if (ori > 0) { // Swap pa and pb. + pt[j] = pa; pa = pb; pb = pt[j]; + } + break; + } + } + } + } + if (pd != NULL) break; + } + assert(i < cavfaces->objects); // SELF_CHECK + pinfect(pd); + + // Create an init DT. + initialDT(pa, pb, pc, pd); + + for (i = 0; i < cavpoints->objects; i++) { + pt[0] = * (point *) fastlookup(cavpoints, i); + assert(pt[0] != dummypoint); // SELF_CHECK + if (!pinfected(pt[0])) { + // pinfect(pt[0]); // Mark it as inserted. + searchtet = recenttet; + insertvertex(pt[0], &searchtet, true, false, false, false); + } else { + puninfect(pt[0]); // It is already inserted. + } + } + // Comment: All vertices of the cavity are NOT marked. + + while (1) { + + // Identify boundary faces. Mark interior tets. Save missing faces. + for (i = 0; i < cavfaces->objects; i++) { + parytet = (triface *) fastlookup(cavfaces, i); + // Skip an interior face (due to the enlargement of the cavity). + if (infected(*parytet)) continue; + // This face may contain dummypoint (See fig/dum-cavity-case2). + // If so, dummypoint must be its apex. + parytet->ver = 4; + pt[0] = org(*parytet); + pt[1] = dest(*parytet); + pt[2] = apex(*parytet); + // Create a temp subface. + makeshellface(subfacepool, &tmpsh); + setshvertices(tmpsh, pt[0], pt[1], pt[2]); + // Insert tmpsh in DT. + searchtet.tet = NULL; + dir = scoutsubface(&tmpsh, &searchtet); + if (dir == SHAREFACE) { + // Identify the inter and outer tets at tempsh. + stpivot(tmpsh, neightet); + // neightet and tmpsh refer to the same edge [pt[0], pt[1]]. + // Moreover, neightet is in 0th edge ring (see decode()). + if (org(neightet) != pt[1]) { + symedgeself(neightet); + assert(org(neightet) == pt[1]); // SELF_CHECK + // Make sure that tmpsh is connected with an interior tet. + tsbond(neightet, tmpsh); + } + assert(dest(neightet) == pt[0]); // SELF_CHECK + // The following step is now done in fillcavity(), 2009-04-24. + // // Mark neightet as interior. + // if (!infected(neightet)) { + // infect(neightet); + // } + } else if (dir == COLLISIONFACE) { + // A subface is already inserted (see fig/dum-cavity-case6). + assert(oppo(*parytet) == NULL); // It must be a faked tet. + // Searchtet's face collides it. Adjust to 0th edge ring. + if ((searchtet.ver & 01) != 0) esymself(searchtet); + // Let the subface remember its adjacent tet at its inside. + if (org(searchtet) != pt[1]) { + symedgeself(searchtet); + assert(org(searchtet) == pt[1]); // SELF_CHECK + } + assert(dest(searchtet) == pt[0]); // SELF_CHECK + tmpsh.sh[9] = (shellface) encode(searchtet); + } else { + if (b->verbose > 1) { + printf(" p:draw_subface(%d, %d, %d) -- %d is missing\n", + pointmark(pt[0]), pointmark(pt[1]), pointmark(pt[2]), i); + } + shellfacedealloc(subfacepool, tmpsh.sh); + // Save this face in list. + misfaces->newindex((void **) &parytet1); + *parytet1 = *parytet; + /*if (dir == EDGETRIINT) { + assert(0); // Face unmatched. Not process yet. + } + // Search an edge crossing this face. + dir = scoutcrosstet(&tmpsh, &searchtet, NULL); + assert(dir == ACROSSTET); // SELF_CHECK + // Save this pair of points. + newflipface = (badface *) flippool->alloc(); + newflipface->forg = apex(searchtet); + newflipface->fdest = oppo(searchtet); + newflipface->nextitem = futureflip; + futureflip = newflipface; + // if (b->verbose > 1) { + printf(" p:draw_subseg(%d, %d)\n", pointmark(newflipface->forg), + pointmark(newflipface->fdest)); + // } + miscount++;*/ + continue; + } + // Remember tmpsh (use the adjacent tet slot). + // parytet->tet[parytet->loc] = (tetrahedron) sencode(tmpsh); + tmpsh.sh[0] = (shellface) encode(*parytet); + // Save this subface. + cavshells->newindex((void **) &parysh); + *parysh = tmpsh; + } + + if (misfaces->objects > 0) { + // Removing tempoaray subfaces. + for (i = 0; i < cavshells->objects; i++) { + parysh = (face *) fastlookup(cavshells, i); + stpivot(*parysh, neightet); + uninfect(neightet); + tsdissolve(neightet); // Detach it from adj. tets. + symself(neightet); + tsdissolve(neightet); + shellfacedealloc(subfacepool, parysh->sh); + } + cavshells->restart(); + + // Infect the points which are of the cavity. + for (i = 0; i < cavpoints->objects; i++) { + pt[0] = * (point *) fastlookup(cavpoints, i); + pinfect(pt[0]); // Mark it as inserted. + } + + // Enlarge the cavity. + for (i = 0; i < misfaces->objects; i++) { + // Get a missing face. + parytet = (triface *) fastlookup(misfaces, i); + if (!infected(*parytet)) { + // Put it into crossing tet list. + infect(*parytet); + crosstets->newindex((void **) &parytet1); + *parytet1 = *parytet; + // Insert the opposite point if it is not in DT. + pd = oppo(*parytet); + if (!pinfected(pd)) { + if (b->verbose > 1) { + printf(" Insert the opposite point %d.\n", pointmark(pd)); + } + pinfect(pd); + cavpoints->newindex((void **) &parypt); + *parypt = pd; + searchtet = recenttet; + insertvertex(pd, &searchtet, true, false, false, false); + } + // Check for a missing subface. + tspivot(*parytet, checksh); + if (checksh.sh != NULL) { + if (b->verbose > 1) { + printf(" Queue a subface x%lx (%d, %d, %d).\n", + (unsigned long) checksh.sh, pointmark(sorg(checksh)), + pointmark(sdest(checksh)), pointmark(sapex(checksh))); + } + stdissolve(checksh); + subfacstack->newindex((void **) &parysh); + *parysh = checksh; + } + // Add three opposite faces into the boundary list. + for (j = 0; j < 3; j++) { + enext0fnext(*parytet, neightet); + symself(neightet); + if (!infected(neightet)) { + if (b->verbose > 1) { + printf(" Add a cavface (%d, %d, %d).\n", + pointmark(org(neightet)), pointmark(dest(neightet)), + pointmark(apex(neightet))); + } + cavfaces->newindex((void **) &parytet1); + *parytet1 = neightet; + } else { + // Check if a subface is missing again. + tspivot(neightet, checksh); + if (checksh.sh != NULL) { + if (b->verbose > 1) { + printf(" Queue a subface x%lx (%d, %d, %d).\n", + (unsigned long) checksh.sh, pointmark(sorg(checksh)), + pointmark(sdest(checksh)), pointmark(sapex(checksh))); + } + stdissolve(checksh); + subfacstack->newindex((void **) &parysh); + *parysh = checksh; + } + } + enextself(*parytet); + } // j + } // if (!infected(parytet)) + } + + // Uninfect the points which are of the cavity. + for (i = 0; i < cavpoints->objects; i++) { + pt[0] = * (point *) fastlookup(cavpoints, i); + puninfect(pt[0]); + } + + misfaces->restart(); + cavityexpcount++; + continue; + } + + break; + + } // while (1) + + // Collect all tets of the DT. All new tets are marktested. + marktest(recenttet); + newtets->newindex((void **) &parytet); + *parytet = recenttet; + for (i = 0; i < newtets->objects; i++) { + searchtet = * (triface *) fastlookup(newtets, i); + for (searchtet.loc = 0; searchtet.loc < 4; searchtet.loc++) { + sym(searchtet, neightet); + if (!marktested(neightet)) { + marktest(neightet); + newtets->newindex((void **) &parytet); + *parytet = neightet; + } + } + } + + cavpoints->restart(); + // Comment: Now no vertex is marked. + cavfaces->restart(); + + if (cavshells->objects > maxcavsize) { + maxcavsize = cavshells->objects; + } + + return true; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// fillcavity() Fill new tets into the cavity. // +// // +// The new tets are stored in two disjoint sets(which share the same facet). // +// 'topfaces' and 'botfaces' are the boundaries of these two sets, respect- // +// ively. 'midfaces' is empty on input, and will store faces in the facet. // +// // +/////////////////////////////////////////////////////////////////////////////// + +bool tetgenmesh::fillcavity(arraypool* topshells, arraypool* botshells, + arraypool* midfaces, arraypool* facpoints) +{ + arraypool *cavshells; + triface *parytet, bdrytet, toptet, bottet, neightet, midface; + face checksh, *parysh; + face checkseg; + point pa, pb, pc, pf, pg; + REAL ori, len, n[3]; + bool mflag, bflag; + int i, j, k; + + tetrahedron ptr; + int *iptr, tver; + + // Connect newtets to tets outside the cavity. + for (k = 0; k < 2; k++) { + cavshells = (k == 0 ? topshells : botshells); + if (cavshells != NULL) { + for (i = 0; i < cavshells->objects; i++) { + // Get a temp subface. + parysh = (face *) fastlookup(cavshells, i); + // Get the boundary tet outsode the cavity. + decode(parysh->sh[0], bdrytet); + pa = org(bdrytet); + pb = dest(bdrytet); + pc = apex(bdrytet); + // Get the adjacent new tet. + stpivot(*parysh, neightet); + assert(org(neightet) == pb); // SELF_CHECK + assert(dest(neightet) == pa); // SELF_CHECK + // Mark neightet as an interior tet of this cavity, 2009-04-24. + // Comment: We know neightet is an interior tet. + if (!infected(neightet)) { + infect(neightet); + } + if (oppo(bdrytet) != NULL) { + // Bond the two tets. + bond(bdrytet, neightet); // Also cleared the pointer to tmpsh. + } + // Bond a subface (if it exists). + tspivot(bdrytet, checksh); + if (checksh.sh != NULL) { + tsbond(neightet, checksh); // Also cleared the pointer to tmpsh. + } else { + tsdissolve(neightet); // No subface, clear the pointer to tmpsh. + } + // Update the point-to-tets map. + point2tet(pa) = encode(neightet); + point2tet(pb) = encode(neightet); + point2tet(pc) = encode(neightet); + // Delete the temp subface. + // shellfacedealloc(subfacepool, parysh->sh); + if (oppo(bdrytet) == NULL) { + // Delete a faked tet. + tetrahedrondealloc(bdrytet.tet); + } + } + } // if (cavshells != NULL) + } + + mflag = true; // Initialize it. + + if (midfaces != NULL) { + + // Mark all facet vertices for finding middle subfaces. + for (i = 0; i < facpoints->objects; i++) { + pf = * (point *) fastlookup(facpoints, i); + pinfect(pf); + } + + // The first pair of top and bottom tets share the same edge [a, b]. + // toptet = * (triface *) fastlookup(topfaces, 0); + if (infected(firsttopface)) { + // The cavity was enlarged. This tet is included in the interior + // (as those of a crossing tet). Find the updated top boundary face + // by rotating the faces around this edge (until an uninfect tet). + pa = apex(firsttopface); + while (1) { + fnextself(firsttopface); + if (!infected(firsttopface)) break; + assert(apex(firsttopface) != pa); // SELF_CHECK + } + } + toptet = firsttopface; + symedgeself(toptet); + // Search a subface from the top mesh. + while (1) { + enext0fnextself(toptet); // The next face in the same tet. + pc = apex(toptet); + if (pinfected(pc)) break; // [a,b,c] is a subface. + symedgeself(toptet); // Go to the same face in the adjacent tet. + } + // Search the subface [a,b,c] in the bottom mesh. + // bottet = * (triface *) fastlookup(botfaces, 0); + if (infected(firstbotface)) { + pa = apex(firstbotface); + while (1) { + fnextself(firstbotface); + if (!infected(firstbotface)) break; + assert(apex(firstbotface) != pa); // SELF_CHECK + } + } + bottet = firstbotface; + symedgeself(bottet); + while (1) { + enext0fnextself(bottet); // The next face in the same tet. + pf = apex(bottet); + if (pf == pc) break; // Face matched. + if (pinfected(pf)) { + mflag = false; break; // Not matched. + } + symedgeself(bottet); + } + if (mflag) { + // Connect the two tets together. + bond(toptet, bottet); + // Both are interior tets. + infect(toptet); + infect(bottet); + // Add this face into search list. + esymself(toptet); // Choose the 0th edge ring. + markface(toptet); + midfaces->newindex((void **) &parytet); + *parytet = toptet; + } + + // Match pairs of subfaces (middle faces), connect top and bottom tets. + for (i = 0; i < midfaces->objects && mflag; i++) { + // Get a matched middle face [a, b, c] + midface = * (triface *) fastlookup(midfaces, i); + // It is inside the cavity. + assert(marktested(midface)); // SELF_CHECK + // Check the neighbors at edges [b, c] and [c, a]. + for (j = 0; j < 2 && mflag; j++) { + enextself(midface); // [b, c] or [c, a]. + pg = apex(midface); + toptet = midface; + bflag = false; + while (1) { + // Go to the next face in the same tet. + enext0fnextself(toptet); + pc = apex(toptet); + if (pinfected(pc)) { + break; // Find a subface. + } + if (pc == dummypoint) { + break; // Find a subface. + } + /* if (pc == pg) { + // The adjacent face is not a middle face. + bflag = true; break; + }*/ + // Go to the same face in the adjacent tet. + symedgeself(toptet); + // Do we walk outside the cavity? + if (!marktested(toptet)) { + // Yes, the adjacent face is not a middle face. + bflag = true; break; + } + } + if (!bflag) { + // assert(marktested(toptet)); // SELF_CHECK + if (!facemarked(toptet)) { + symedge(midface, bottet); + while (1) { + enext0fnextself(bottet); + pf = apex(bottet); + if (pf == pc) break; // Face matched. + if (pinfected(pf)) { + mflag = false; break; // Not matched + } + symedgeself(bottet); + } + if (mflag) { + if (marktested(bottet)) { + // Connect two tets together. + bond(toptet, bottet); + // Both are interior tets. + infect(toptet); + infect(bottet); + // Add this face into list. + esymself(toptet); + markface(toptet); + midfaces->newindex((void **) &parytet); + *parytet = toptet; + } else { + // The 'bottet' is not inside the cavity! + // This case can happen when the cavity was enlarged, and the + // 'toptet' is a co-facet (sub)face adjacent to the missing + // region, and it is a boundary face of the top cavity. + // So the toptet and bottet should be bonded already through + // a temp subface. See fig/dump-cavity-case18. Check it. + symedge(toptet, neightet); + assert(neightet.tet == bottet.tet); // SELF_CHECK + assert(neightet.loc == bottet.loc); // SELF_CHECK + // Do not add this face into 'midfaces'. + } + } + } + } + } // j + } // i + + } // if (midfaces != NULL) + + if (mflag) { + if (midfaces != NULL) { + if (b->verbose > 1) { + printf(" Found %ld middle subfaces.\n", midfaces->objects); + } + if (midfaces->objects > maxregionsize) { + maxregionsize = midfaces->objects; + } + // Unmark middle faces. + for (i = 0; i < midfaces->objects; i++) { + // Get a matched middle face [a, b, c] + midface = * (triface *) fastlookup(midfaces, i); + assert(facemarked(midface)); // SELF_CHECK + unmarkface(midface); + } + } + // Bond subsegments to new tets. + // Comment: *** The following code does redundant job. Should be + // re-placed in the future. + for (k = 0; k < 2; k++) { + cavshells = (k == 0 ? topshells : botshells); + if (cavshells != NULL) { + for (i = 0; i < cavshells->objects; i++) { + parysh = (face *) fastlookup(cavshells, i); + decode(parysh->sh[0], bdrytet); + if (bdrytet.tet[4] != NULL) { + // Not a faked tet. Bond a subsegment (if it exists). + for (j = 0; j < 3; j++) { + tsspivot(bdrytet, checkseg); + if (checkseg.sh != NULL) { + symedge(bdrytet, neightet); + assert(marktested(neightet)); // SELF_CHECK + // Let the segment remember an adjacent tet. + sstbond(checkseg, neightet); + while (1) { + tssbond1(neightet, checkseg); + fnextself(neightet); + if (!marktested(neightet)) break; + } + } + enextself(bdrytet); + } + } else { + // A faked tet. There is an interior subface. Use it. + // See fig/dump-cavity-case19. + stpivot(*parysh, neightet); + assert(marktested(neightet)); // SELF_CHECK + tspivot(neightet, checksh); + assert(checksh.sh != NULL); // SELF_CHECK + assert(checksh.sh != parysh->sh); // // SELF_CHECK + // Align them at the same directed edge. + pa = org(neightet); + pb = dest(neightet); + for (j = 0; j < 3; j++) { + if (sorg(checksh) == pa) break; + senextself(checksh); + } + assert(j < 3); // SELF_CHECK + if (sdest(checksh) != pb) { + senext2self(checksh); + sesymself(checksh); + } + assert(sdest(checksh) == pb); // SELF_CHECK + // Bond a subsegment (if it exists). + for (j = 0; j < 3; j++) { + sspivot(checksh, checkseg); + if (checkseg.sh != NULL) { + // Let the segment remember an adjacent tet. + sstbond(checkseg, neightet); + toptet = neightet; + while (1) { + tssbond1(toptet, checkseg); + fnextself(toptet); + if (apex(toptet) == apex(neightet)) break; + } + } + senextself(checksh); + enextself(neightet); + } + } + } + } // if (cavshells != NULL) + } + } else { + // Faces at top and bottom are not matched. There exists non-Delaunay + // subedges. See fig/dump-cavity-case5.lua. + pa = org(toptet); + pb = dest(toptet); + pc = apex(toptet); + pf = apex(bottet); + if (b->verbose > 1) { + printf(" p:draw_tet(%d, %d, %d, %d) -- top tet.\n", pointmark(pa), + pointmark(pb), pointmark(pc), pointmark(oppo(toptet))); + printf(" p:draw_tet(%d, %d, %d, %d) -- bot tet.\n", + pointmark(org(bottet)), pointmark(dest(bottet)), + pointmark(apex(bottet)), pointmark(oppo(bottet))); + } + // Calculate a point above the faces. + facenormal(pa, pb, pc, n, 1); + len = sqrt(DOT(n, n)); + n[0] /= len; + n[1] /= len; + n[2] /= len; + len = DIST(pa, pb); + len += DIST(pb, pc); + len += DIST(pc, pa); + len /= 3.0; + dummypoint[0] = pa[0] + len * n[0]; + dummypoint[1] = pa[1] + len * n[1]; + dummypoint[2] = pa[2] + len * n[2]; + // Find the crossing edges. + ori = orient3d(pb, pc, dummypoint, pf); + assert(ori != 0); // SELF_CHECK + if (ori < 0) { + // The top edge [b, c] intersects the bot edge [a, f]. + enextself(toptet); + enextself(bottet); + } else { + // The top edge [c, a] intersects the bot edge [f, b]. + enext2self(toptet); + enext2self(bottet); + } + // Split one of the edges, choose the one has longer length. + n[0] = DIST(org(toptet), dest(toptet)); + n[1] = DIST(org(bottet), dest(bottet)); + if (n[0] > n[1]) { + pf = org(toptet); + pg = dest(toptet); + } else { + pf = org(bottet); + pg = dest(bottet); + } + if (b->verbose > 1) { + printf(" Found a non-Delaunay edge (%d, %d)\n", pointmark(pf), + pointmark(pg)); + } + // Create the midpoint of the non-Delaunay edge. + for (i = 0; i < 3; i++) { + dummypoint[i] = 0.5 * (pf[i] + pg[i]); + } + // Set a tet for searching the new point. + recenttet = firsttopface; + // dummypoint[0] = dummypoint[1] = dummypoint[2] = 0; + ndelaunayedgecount++; + } + + if (facpoints != NULL) { + // Unmark all facet vertices. + for (i = 0; i < facpoints->objects; i++) { + pf = * (point *) fastlookup(facpoints, i); + puninfect(pf); + } + } + + // Delete the temp subfaces. + for (k = 0; k < 2; k++) { + cavshells = (k == 0 ? topshells : botshells); + if (cavshells != NULL) { + for (i = 0; i < cavshells->objects; i++) { + parysh = (face *) fastlookup(cavshells, i); + shellfacedealloc(subfacepool, parysh->sh); + } + } + } + + topshells->restart(); + if (botshells != NULL) { + botshells->restart(); + } + if (midfaces != NULL) { + midfaces->restart(); + } + // Comment: Now no vertex is marked. + + return mflag; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// carvecavity() Delete old tets and outer new tets of the cavity. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::carvecavity(arraypool *crosstets, arraypool *topnewtets, + arraypool *botnewtets) +{ + arraypool *newtets; + triface *parytet, *pnewtet, neightet; + face checkseg, *parysh; + int i, j, k; + + // NOTE: Some subsegments may contained inside the cavity. They must be + // queued for recovery. See fig/dump-cavity-case20. + // Comment: This check should be avoided in the future. Do the check in + // routine delaunizecavity() is NOT enough. (2009-04-24). + for (i = 0; i < crosstets->objects; i++) { + parytet = (triface *) fastlookup(crosstets, i); + assert(infected(*parytet)); // SELF_CHECK + if (parytet->tet[8] != NULL) { + for (j = 0; j < 6; j++) { + parytet->loc = edge2locver[j][0]; + parytet->ver = edge2locver[j][1]; + tsspivot(*parytet, checkseg); + if (checkseg.sh != NULL) { + if (!sinfected(checkseg)) { + // It is not queued yet. + neightet = *parytet; + while (1) { + fnextself(neightet); + if (!infected(neightet)) break; + if (apex(neightet) == apex(*parytet)) break; + } + if (infected(neightet)) { + if (b->verbose > 1) { + printf(" Queue a missing segment (%d, %d).\n", + pointmark(sorg(checkseg)), pointmark(sdest(checkseg))); + } + // Clean the seg-to-tet pointer. + stdissolve(checkseg); + sinfect(checkseg); + subsegstack->newindex((void **) &parysh); + *parysh = checkseg; + } + } + } + } + } + } + + // Delete the old tets in cavity. + for (i = 0; i < crosstets->objects; i++) { + parytet = (triface *) fastlookup(crosstets, i); + tetrahedrondealloc(parytet->tet); + } + crosstets->restart(); // crosstets will be re-used. + + // Collect infected new tets in cavity. + for (k = 0; k < 2; k++) { + newtets = (k == 0 ? topnewtets : botnewtets); + if (newtets != NULL) { + for (i = 0; i < newtets->objects; i++) { + parytet = (triface *) fastlookup(newtets, i); + if (infected(*parytet)) { + crosstets->newindex((void **) &pnewtet); + *pnewtet = *parytet; + } + } + } + } + // Collect all new tets in cavity. + for (i = 0; i < crosstets->objects; i++) { + parytet = (triface *) fastlookup(crosstets, i); + if (i == 0) { + recenttet = *parytet; // Remember a live handle. + } + for (j = 0; j < 4; j++) { + decode(parytet->tet[j], neightet); + if (marktested(neightet)) { // Is it a new tet? + if (!infected(neightet)) { + // Find an interior tet. + assert((point) neightet.tet[7] != dummypoint); // SELF_CHECK + infect(neightet); + crosstets->newindex((void **) &pnewtet); + *pnewtet = neightet; + } + } + } + } + + // Delete outer new tets. + for (k = 0; k < 2; k++) { + newtets = (k == 0 ? topnewtets : botnewtets); + if (newtets != NULL) { + for (i = 0; i < newtets->objects; i++) { + parytet = (triface *) fastlookup(newtets, i); + if (infected(*parytet)) { + // This is an interior tet. + uninfect(*parytet); + unmarktest(*parytet); + } else { + // An outer tet. Delete it. + tetrahedrondealloc(parytet->tet); + } + } + } + } + + crosstets->restart(); + topnewtets->restart(); + if (botnewtets != NULL) { + botnewtets->restart(); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// restorecavity() Reconnect old tets and delete new tets of the cavity. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::restorecavity(arraypool *crosstets, arraypool *topnewtets, + arraypool *botnewtets) +{ + triface *parytet, neightet; + face checksh; + point *ppt; + int i, j; + + // Reconnect crossing tets to cavity boundary. + for (i = 0; i < crosstets->objects; i++) { + parytet = (triface *) fastlookup(crosstets, i); + assert(infected(*parytet)); // SELF_CHECK + if (i == 0) { + recenttet = *parytet; // Remember a live handle. + } + parytet->ver = 0; + for (parytet->loc = 0; parytet->loc < 4; parytet->loc++) { + symedge(*parytet, neightet); + if (!infected(neightet)) { + bond(*parytet, neightet); + tspivot(*parytet, checksh); + if (checksh.sh != NULL) { + tsbond(*parytet, checksh); + } + } + } + // Update the point-to-tet map. + parytet->loc = 0; + ppt = (point *) &(parytet->tet[4]); + for (j = 0; j < 4; j++) { + point2tet(ppt[j]) = encode(*parytet); + } + } + + // Uninfect all crossing tets. + for (i = 0; i < crosstets->objects; i++) { + parytet = (triface *) fastlookup(crosstets, i); + uninfect(*parytet); + } + + // Delete new tets. + for (i = 0; i < topnewtets->objects; i++) { + parytet = (triface *) fastlookup(topnewtets, i); + tetrahedrondealloc(parytet->tet); + } + + if (botnewtets != NULL) { + for (i = 0; i < botnewtets->objects; i++) { + parytet = (triface *) fastlookup(botnewtets, i); + tetrahedrondealloc(parytet->tet); + } + } + + crosstets->restart(); + topnewtets->restart(); + if (botnewtets != NULL) { + botnewtets->restart(); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// splitsubedge() Split a non-Delaunay edge (not a segment) in the // +// surface mesh of a facet. // +// // +// The new point 'newpt' will be inserted in the tetrahedral mesh if it does // +// not cause any existing (sub)segments become non-Delaunay. Otherwise, the // +// new point is not inserted and one of such subsegments will be split. // +// // +// Next,the actual inserted new point is also inserted into the surface mesh.// +// Non-Delaunay segments and newly created subfaces are queued for recovery. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::splitsubedge(point newpt, face *searchsh, arraypool *facfaces, + arraypool *facpoints) +{ + triface searchtet; + face *psseg, sseg; + point pa, pb; + enum location loc; + int s, i; + + // Try to insert the point. Do not insert if it will encroach any segment + // (noencsegflag is TRUE). Queue encroacged subfaces. + assert(subsegstack->objects == 0l); // SELF_CHECK + searchtet = recenttet; // Start search it from recentet + loc = insertvertex(newpt, &searchtet, true, true, true, false); + + if (loc == ENCSEGMENT) { + // Some segments are encroached. Randomly pick one to split. + assert(subsegstack->objects > 0l); + s = randomnation(subsegstack->objects); + psseg = (face *) fastlookup(subsegstack, s); + sseg = *psseg; + pa = sorg(sseg); + pb = sdest(sseg); + for (i = 0; i < 3; i++) newpt[i] = 0.5 * (pa[i] + pb[i]); + // Uninfect all queued segments. + for (i = 0; i < subsegstack->objects; i++) { + psseg = (face *) fastlookup(subsegstack, i); + suninfect(*psseg); + } + subsegstack->restart(); // Clear the queue. + // Split the segment. Two subsegments are queued. + sinsertvertex(newpt, searchsh, &sseg, true, false); + // Insert the point. Missing segments are queued. + searchtet = recenttet; // Start search it from recentet + insertvertex(newpt, &searchtet, true, true, false, false); + } else { + // Calc an above point for point location in surface triangulation. + calculateabovepoint(facpoints, NULL, NULL, NULL); + // Insert the new point on facet. New subfaces are queued for reocvery. + loc = sinsertvertex(newpt, searchsh, NULL, true, false); + if (loc == OUTSIDE) { + assert(0); // Not handled yet. + } + // Clear the above point. + dummypoint[0] = dummypoint[1] = dummypoint[2] = 0; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// constrainedfacets() Recover subfaces saved in 'subfacestack'. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::constrainedfacets() +{ + /* + arraypool *crosstets, *topnewtets, *botnewtets; + arraypool *topfaces, *botfaces, *midfaces; + arraypool *topshells, *botshells, *facfaces; + arraypool *toppoints, *botpoints, *facpoints; + */ + triface *parytet, searchtet, neightet; + face *pssub, ssub, neighsh; + face checkseg; + point *ppt, pt, newpt; + enum intersection dir; + bool success, delaunayflag; + int facetcount; + int bakhullsize; + int s, i, j; + + /* // Initialize arrays. + crosstets = new arraypool(sizeof(triface), 10); + topnewtets = new arraypool(sizeof(triface), 10); + botnewtets = new arraypool(sizeof(triface), 10); + topfaces = new arraypool(sizeof(triface), 10); + botfaces = new arraypool(sizeof(triface), 10); + midfaces = new arraypool(sizeof(triface), 10); + toppoints = new arraypool(sizeof(point), 8); + botpoints = new arraypool(sizeof(point), 8); + facpoints = new arraypool(sizeof(point), 8); + facfaces = new arraypool(sizeof(face), 10); + topshells = new arraypool(sizeof(face), 10); + botshells = new arraypool(sizeof(face), 10); + */ + + facetcount = 0; + + // Loop until 'subfacstack' is empty. + while (subfacstack->objects > 0l) { + subfacstack->objects--; + pssub = (face *) fastlookup(subfacstack, subfacstack->objects); + ssub = *pssub; + + if (ssub.sh[3] == NULL) continue; // Skip a dead subface. + + stpivot(ssub, neightet); + if (neightet.tet == NULL) { + // Find an unrecovered subface. + smarktest(ssub); + tg_facfaces->newindex((void **) &pssub); + *pssub = ssub; + // Get all subfaces and vertices of the same facet. + for (i = 0; i < tg_facfaces->objects; i++) { + ssub = * (face *) fastlookup(tg_facfaces, i); + for (j = 0; j < 3; j++) { + sspivot(ssub, checkseg); + if (checkseg.sh == NULL) { + spivot(ssub, neighsh); + assert(neighsh.sh != NULL); // SELF_CHECK + if (!smarktested(neighsh)) { + // It may be already recovered. + stpivot(neighsh, neightet); + if (neightet.tet == NULL) { + smarktest(neighsh); + tg_facfaces->newindex((void **) &pssub); + *pssub = neighsh; + } + } + } + pt = sorg(ssub); + if (!pinfected(pt)) { + pinfect(pt); + tg_facpoints->newindex((void **) &ppt); + *ppt = pt; + } + senextself(ssub); + } // j + } // i + // Have found all facet subfaces (vertices). Uninfect them. + for (i = 0; i < tg_facfaces->objects; i++) { + pssub = (face *) fastlookup(tg_facfaces, i); + sunmarktest(*pssub); + } + for (i = 0; i < tg_facpoints->objects; i++) { + ppt = (point *) fastlookup(tg_facpoints, i); + puninfect(*ppt); + } + if (b->verbose > 1) { + printf(" Recover facet #%d: %ld subfaces, %ld vertices.\n", + facetcount + 1, tg_facfaces->objects, tg_facpoints->objects); + } + facetcount++; + + // Loop until 'tg_facfaces' is empty. + while (tg_facfaces->objects > 0l) { + // Get the last subface of this array. + tg_facfaces->objects--; + pssub = (face *) fastlookup(tg_facfaces, tg_facfaces->objects); + ssub = *pssub; + + stpivot(ssub, neightet); + if (neightet.tet != NULL) continue; // Not a missing subface. + + // Insert the subface. + searchtet.tet = NULL; + dir = scoutsubface(&ssub, &searchtet); + if (dir == SHAREFACE) continue; // The subface is inserted. + assert(dir != COLLISIONFACE); // SELF_CHECK + + // Not exist. Push the subface back into stack. + s = randomnation(tg_facfaces->objects + 1); + tg_facfaces->newindex((void **) &pssub); + *pssub = * (face *) fastlookup(tg_facfaces, s); + * (face *) fastlookup(tg_facfaces, s) = ssub; + + if (dir == EDGETRIINT) continue; // All three edges are missing. + + // Search for a crossing tet. + dir = scoutcrosstet(&ssub, &searchtet, tg_facpoints); + + if (dir == ACROSSTET) { + // Recover subfaces by local retetrahedralization. + cavitycount++; + bakhullsize = hullsize; + checksubsegs = checksubfaces = 0; + tg_crosstets->newindex((void **) &parytet); + *parytet = searchtet; + // Form a cavity of crossing tets. + formcavity(&ssub, tg_crosstets, tg_topfaces, tg_botfaces, + tg_toppoints, tg_botpoints, tg_facpoints); + delaunayflag = true; + // Tetrahedralize the top part. Re-use 'tg_midfaces'. + success = delaunizecavity(tg_toppoints, tg_topfaces, tg_topshells, + tg_topnewtets, tg_crosstets, tg_midfaces); + if (success) { + // Tetrahedralize the bottom part. Re-use 'tg_midfaces'. + success = delaunizecavity(tg_botpoints, tg_botfaces, tg_botshells, + tg_botnewtets, tg_crosstets, tg_midfaces); + if (success) { + // Fill the cavity with new tets. + success = fillcavity(tg_topshells, tg_botshells, tg_midfaces, + tg_facpoints); + if (success) { + // Delete old tets and outer new tets. + carvecavity(tg_crosstets, tg_topnewtets, tg_botnewtets); + } + } else { + delaunayflag = false; + } + } else { + delaunayflag = false; + } + if (!success) { + // Restore old tets and delete new tets. + restorecavity(tg_crosstets, tg_topnewtets, tg_botnewtets); + } + /*if (!delaunayflag) { + dump_facetof(&ssub, "facet1.lua"); + while (futureflip != NULL) { + formedgecavity(futureflip->forg, futureflip->fdest, tg_crosstets, + tg_topfaces, tg_toppoints); + tg_crosstets->restart(); + tg_topfaces->restart(); + tg_toppoints->restart(); + futureflip = futureflip->nextitem; + } + flippool->restart(); + outnodes(0); + checkmesh(); + checkshells(1); + assert(0); // Stop the program. + }*/ + hullsize = bakhullsize; + checksubsegs = checksubfaces = 1; + } else if (dir == ACROSSFACE) { + // Recover subfaces by flipping edges in surface mesh. + recoversubfacebyflips(&ssub, &searchtet, tg_facfaces); + success = true; + } else { // dir == TOUCHFACE + assert(0); + } + if (!success) break; + } // while + + if (tg_facfaces->objects > 0l) { + // Found a non-Delaunay edge, split it (or a segment close to it). + // Create a new point at the middle of this edge, its coordinates + // were saved in dummypoint in 'fillcavity()'. + makepoint(&newpt); + for (i = 0; i < 3; i++) newpt[i] = dummypoint[i]; + setpointtype(newpt, STEINERVERTEX); + dummypoint[0] = dummypoint[1] = dummypoint[2] = 0; + // Insert the new point. Starting search it from 'ssub'. + splitsubedge(newpt, &ssub, tg_facfaces, tg_facpoints); + tg_facfaces->restart(); + } + // Clear the list of facet vertices. + tg_facpoints->restart(); + + // Some subsegments may be queued, recover them. + if (subsegstack->objects > 0l) { + delaunizesegments(); + } + // Now the mesh should be constrained Delaunay. + } // if (neightet.tet == NULL) + } + + /* // Delete arrays. + delete crosstets; + delete topnewtets; + delete botnewtets; + delete topfaces; + delete botfaces; + delete midfaces; + delete toppoints; + delete botpoints; + delete facpoints; + delete facfaces; + delete topshells; + delete botshells; + */ +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// scoutsegment2() Search a segment in tetrahedralization. // +// // +// Search an edge in tetrahedralization that matches the given segmment. If // +// such an edge exists, the segment is 'locked' at that edge. 'searchtet' // +// returns this (constrained) edge. Otherwise, the segment is missing. // +// // +// If the segment is missing, and the array 'crosstets' is given, it returns // +// all crossing tetrahedra by this segment. // +// // +// The returned value indicates one of the following cases: // +// - SHAREEDGE, the segment exists and is inserted in T; // +// - ACROSSVERT, the segment passes a vertex (the origin of 'searchtet'). // +// - ACROSSEDGE, the segment intersects an edge (in 'searchtet'). // +// - ACROSSFACE, the segment crosses a face (in 'searchtet'). // +// - ACROSSSUBSEG, the segment intersects a segment (in 'searchtet'). // +// - ACROSSSUBFACE, the segment crosses a subface (in 'searchtet'). // +// - ACROSSTET, the segment is missing and the cavity has formed. // +// // +/////////////////////////////////////////////////////////////////////////////// + +enum tetgenmesh::intersection tetgenmesh::scoutsegment2(face* sseg, + triface* searchtet, arraypool* crosstets) +{ + triface neightet, spintet, *parytet; + face checksh, checkseg; + point pa, pb, pc, pd, pe, pf;// *ppt, *parypt; + enum intersection dir; + int types[2], poss[4]; + int pos, i; + + tetrahedron ptr; + int *iptr, tver; + + pa = sorg(*sseg); + pb = sdest(*sseg); + + if (b->verbose > 1) { + printf(" Search edge (%d, %d).\n", pointmark(pa), pointmark(pb)); + } + + // Search a tet whose origin is pa. + point2tetorg(pa, *searchtet); + + // Search the line segment [pa, pb]. + dir = finddirection(searchtet, pb); + if (dir == ACROSSVERT) { + if (dest(*searchtet) == pb) { + // Found! Insert the segment. + tsspivot(*searchtet, checkseg); // SELF_CHECK + if (checkseg.sh == NULL) { + // Let the segment remember an adjacent tet. + sstbond(*sseg, *searchtet); + neightet = *searchtet; + do { + tssbond1(neightet, *sseg); + fnextself(neightet); + } while (neightet.tet != searchtet->tet); + } else { + // Collision! This can happy during facet recovery. + // See fig/dump-cavity-case19, -case20. + assert(checkseg.sh == sseg->sh); // SELF_CHECK + } + return SHAREEDGE; // The edge is not missing. + } else { + enextself(*searchtet); + return ACROSSVERT; // The edge intersects a vertex. + } + } + + // The edge is missing, shall we form the edge cavity? + if (crosstets == NULL) { + // Go to the face/edge it crosses. + enextfnextself(*searchtet); + return dir; // ACROSSFACE and ACROSSEDGE + } + + // The possible cases are: ACROSSFACE and ACROSSEDGE. + // Go to the opposite (intersect) face. + enextfnextself(*searchtet); + // Add this tet into list. + infect(*searchtet); + crosstets->newindex((void **) &parytet); + *parytet = *searchtet; + /*// Add all vertices of this tet into list. + ppt = (point *) &(searchtet->tet[4]); + for (i = 0; i < 4; i++) { + pinfect(ppt[i]); + cavpoints->newindex((void **) &parypt); + *parypt = ppt[i]; + }*/ + + // Collect all crossing tets of the edge [a, b]. + while (1) { + + // Enter the next crossing tet. + symedgeself(*searchtet); + pf = oppo(*searchtet); + + if (dir == ACROSSFACE) { + if (!infected(*searchtet)) { // Add this tet into list. + infect(*searchtet); + crosstets->newindex((void **) &parytet); + *parytet = *searchtet; + } + /*if (!pinfected(pf)) { // Add the opposite point into list. + pinfect(pf); + cavpoints->newindex((void **) &parypt); + *parypt = pf; + }*/ + tspivot(*searchtet, checksh); // Check if a subface is crossed. + if (checksh.sh != NULL) { + // The edge intersect a subface. + dir = ACROSSSUBFACE; + break; + } + } else { // dir == ACROSSEDGE + // Add all tets containing this edge into list. + pc = apex(*searchtet); + spintet = *searchtet; + while (1) { + fnextself(spintet); + if (!infected(spintet)) { // Add this tet into list. + infect(spintet); + crosstets->newindex((void **) &parytet); + *parytet = spintet; + } + pd = oppo(spintet); + /*if (!pinfected(pd)) { // Add the opposite point into list. + pinfect(pd); + cavpoints->newindex((void **) &parypt); + *parypt = pd; + }*/ + if (apex(spintet) == pc) break; + } + tsspivot(spintet, checkseg); // Check if a segment is crossed. + if (checkseg.sh != NULL) { + // The edge intersects a subsegment. + *searchtet = spintet; + dir = ACROSSSUBSEG; + break; + } + } + + // Stop if we reach the endpoint. + if (pf == pb) break; + + // Search the next tet crossing by [a, b]. + if (dir == ACROSSFACE) { + // One of the 3 opposite faces in 'searchtet' must intersect [a, b]. + neightet.tet = searchtet->tet; + neightet.ver = 0; + for (i = 0; i < 3; i++) { + neightet.loc = locpivot[searchtet->loc][i]; + pc = org(neightet); + pd = dest(neightet); + pe = apex(neightet); + pf = oppo(neightet); // The above point. + // Test if face [c, d, e] intersects edge [a, b]? Report their + // intersection type ('level' = 1). + if (tri_edge_test(pc, pd, pe, pa, pb, pf, 1, types, poss)) { + dir = (enum intersection) types[0]; + pos = poss[0]; + break; + } else { + dir = DISJOINT; + pos = 0; + } + } + assert(dir != DISJOINT); // SELF_CHECK + } else { // dir == ACROSSEDGE + // Find a face (or edge) intersecting with [a, b]. + spintet = *searchtet; // Backup the starting tet. + while (1) { + // Check the two opposite faces (of the edge) in 'searchtet'. + neightet.tet = searchtet->tet; + neightet.ver = 0; + for (i = 0; i < 2; i++) { + neightet.loc = locverpivot[searchtet->loc][searchtet->ver][i]; + pc = org(neightet); + pd = dest(neightet); + pe = apex(neightet); + pf = oppo(neightet); // The above point. + // Test if face [c, d, e] intersects edge [a, b]? Report their + // intersection type ('level' = 1). + if (tri_edge_test(pc, pd, pe, pa, pb, pf, 1, types, poss)) { + dir = (enum intersection) types[0]; + pos = poss[0]; + break; + } else { + dir = DISJOINT; + pos = 0; + } + } + if (dir == DISJOINT) { + // No intersection. Go to the next tet. + fnextself(*searchtet); + // Should NOT return to the starting tet. + assert(searchtet->tet != spintet.tet); // SELF_CHECK + continue; // Continue the search. + } + break; // Found! + } // while (1) + } + + // Go to the intersect face or edge. + if (dir != ACROSSFACE) { + // 'dir' is either ACROSSFACE or ACROSSEDGE. + assert(dir == ACROSSEDGE); // SELF_CHECK + for (i = 0; i < pos; i++) { + enextself(neightet); + } + } + *searchtet = neightet; + + } // while (1) + + if ((dir == ACROSSSUBSEG) || (dir == ACROSSSUBFACE)) { + // Uninfect the collected crossing tets and vertices. + for (i = 0; i < crosstets->objects; i++) { + parytet = (triface *) fastlookup(crosstets, i); + uninfect(*parytet); + } + /*for (i = 0; i < cavpoints->objects; i++) { + parypt = (point *) fastlookup(cavpoints, i); + puninfect(*parypt); + }*/ + crosstets->restart(); + // cavpoints->restart(); + return dir; + } + + /*//We can form the edge cavity. + for (i = 0; i < crosstets->objects; i++) { + parytet = (triface *) fastlookup(crosstets, i); + *searchtet = *parytet; + for (searchtet->loc = 0; searchtet->loc < 4; searchtet->loc++) { + sym(*searchtet, neightet); + if (!infected(neightet)) { + // A cavity bounday face. + cavfaces->newindex((void **) &parytet); + *parytet = neightet; + } + } + } + // Uninfect the vertices. + for (i = 0; i < cavpoints->objects; i++) { + parypt = (point *) fastlookup(cavpoints, i); + puninfect(*parypt); + } + // Comment: All crossing tets are infected. + */ + + if (b->verbose > 1) { + printf(" Formed edge cavity: %ld tets.\n", crosstets->objects); + } + + return ACROSSTET; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// tetrasegcavity() Tetrahedralize a cavity for recovering a segment. // +// // +/////////////////////////////////////////////////////////////////////////////// + +bool tetgenmesh::tetrasegcavity(face* sseg, arraypool* crosstets, + arraypool* cavpoints, arraypool* cavfaces, arraypool* cavshells, + arraypool* newtets, arraypool* misfaces, arraypool* missegs, + arraypool* fixedseglist) +{ + triface searchtet, neightet, spintet, *parytet, *parytet1; + face tmpsh, *parysh; + face checkseg, *paryseg; + point pt[4], pswap, *parypt; + enum intersection dir; + REAL ori; + bool success; + int i, j; + + tetrahedron ptr; + int *iptr, tver; + + // cavpoints are collected. + for (i = 0; i < crosstets->objects; i++) { + parytet = (triface *) fastlookup(crosstets, i); + for (j = 0; j < 4; j++) { + pt[0] = (point) parytet->tet[4 + j]; + if (!pinfected(pt[0])) { + pinfect(pt[0]); + cavpoints->newindex((void **) &parypt); + *parypt = pt[0]; + } + } + } + for (i = 0; i < cavpoints->objects; i++) { + parypt = (point *) fastlookup(cavpoints, i); + puninfect(*parypt); + } + + if (b->verbose > 1) { + printf(" Tetrahedralizing segment cavity: %ld points.\n", + cavpoints->objects, cavfaces->objects); + } + + // Form an initial constrained tetrahedralization (CT) which has only one + // tetrahedron containing the given segment. + + // The first two points are the endpoints of the segment. + pt[0] = sorg(*sseg); + pt[1] = sdest(*sseg); + pinfect(pt[0]); + pinfect(pt[1]); + + // Get the third and fourth points. + for (i = 0; i < cavpoints->objects; i++) { + parypt = (point *) fastlookup(cavpoints, i); + if (!pinfected(*parypt)) { + pt[2] = *parypt; + i++; + for (; i < cavpoints->objects; i++) { + parypt = (point *) fastlookup(cavpoints, i); + if (!pinfected(*parypt)) { + ori = orient3d(pt[0], pt[1], pt[2], *parypt); + if (ori != 0) { + pt[3] = *parypt; + if (ori > 0) { // Swap pa and pb. + pswap = pt[0]; pt[0] = pt[1]; pt[1] = pswap; + } + break; + } + } + } // i + break; + } + } // i + assert(i < cavpoints->objects); // SELF_CHECK + pinfect(pt[2]); + pinfect(pt[3]); + + // Create an initial DT. + initialDT(pt[0], pt[1], pt[2], pt[3]); + + // Insert the segment (turns a DT into a CT). + scoutsegment2(sseg, &searchtet, NULL); + + // Insert the other points into the CT (the segment is respected). + for (i = 0; i < cavpoints->objects; i++) { + parypt = (point *) fastlookup(cavpoints, i); + if (!pinfected(*parypt)) { + // pinfect(*parypt); + searchtet = recenttet; // No random samples. + // Insert the point by flips. Set 'flipflag' = 3, do not flip a segment. + flipinsertvertex(*parypt, &searchtet, 3); + } else { + puninfect(*parypt); + } + } + // Comment: All vertices of the cavity are NOT marked. + + success = true; + + while (1) { + + // cavfaces are collected. + for (i = 0; i < crosstets->objects; i++) { + parytet = (triface *) fastlookup(crosstets, i); + searchtet = *parytet; + for (searchtet.loc = 0; searchtet.loc < 4; searchtet.loc++) { + sym(searchtet, neightet); + if (!infected(neightet)) { + // A cavity bounday face. + cavfaces->newindex((void **) &parytet); + *parytet = neightet; + } + } + } + + // Identify boundary faces. Mark interior tets. Save missing faces. + for (i = 0; i < cavfaces->objects; i++) { + parytet = (triface *) fastlookup(cavfaces, i); + // The tet of this face is exteriorly adjacent to the cavity. + assert(!infected(*parytet)); // SELF_CHECK + // This face may contain dummypoint (See fig/dump-cavity-case2). + // If so, dummypoint must be its apex. + parytet->ver = 4; + pt[0] = org(*parytet); + pt[1] = dest(*parytet); + pt[2] = apex(*parytet); + // Create a temp subface. + makeshellface(subfacepool, &tmpsh); + setshvertices(tmpsh, pt[0], pt[1], pt[2]); + // Insert tmpsh in CT. + searchtet.tet = NULL; + dir = scoutsubface(&tmpsh, &searchtet); + if (dir == SHAREFACE) { + // Identify the inter and outer tets at tempsh. + stpivot(tmpsh, neightet); + // neightet and tmpsh refer to the same edge [pt[0], pt[1]]. + // Moreover, neightet is in 0th edge ring (see decode()). + if (org(neightet) != pt[1]) { + symedgeself(neightet); + assert(org(neightet) == pt[1]); // SELF_CHECK + // Make sure that tmpsh is connected with an interior tet. + tsbond(neightet, tmpsh); + } + assert(dest(neightet) == pt[0]); // SELF_CHECK + } else if (dir == COLLISIONFACE) { + // A subface is already inserted. + assert(0); // Not handled yet. + } else { + if (b->verbose > 1) { + printf(" p:draw_subface(%d, %d, %d) -- %d is missing\n", + pointmark(pt[0]), pointmark(pt[1]), pointmark(pt[2]), i); + } + shellfacedealloc(subfacepool, tmpsh.sh); + // Save this face in list. + misfaces->newindex((void **) &parytet1); + *parytet1 = *parytet; + continue; + } + // Remember tmpsh (use the adjacent tet slot). + // parytet->tet[parytet->loc] = (tetrahedron) sencode(tmpsh); + tmpsh.sh[0] = (shellface) encode(*parytet); + // Save this subface. + cavshells->newindex((void **) &parysh); + *parysh = tmpsh; + } // for (i) + + if (misfaces->objects > 0) { + // Removing tempoaray subfaces. + for (i = 0; i < cavshells->objects; i++) { + parysh = (face *) fastlookup(cavshells, i); + stpivot(*parysh, neightet); + uninfect(neightet); + tsdissolve(neightet); // Detach it from adj. tets. + symself(neightet); + tsdissolve(neightet); + shellfacedealloc(subfacepool, parysh->sh); + } + cavshells->restart(); + + for (i = 0; i < cavpoints->objects; i++) { + parypt = (point *) fastlookup(cavpoints, i); + pinfect(*parypt); + } + + // Enlarge the cavity. + for (i = 0; i < misfaces->objects; i++) { + // Get a missing face. + parytet = (triface *) fastlookup(misfaces, i); + // For this routine we do not check subface(s). + if (!infected(*parytet)) { + // This face should not be on the hull. + assert((point) parytet->tet[7] != dummypoint); // SELF_CHECK + // Check if we can enclose this tet into our cavity. + infect(*parytet); + // Check its six edges for enclosed segments. + neightet.tet = parytet->tet; + for (j = 0; j < 6; j++) { + neightet.loc = edge2locver[j][0]; + neightet.ver = edge2locver[j][1]; + tsspivot(neightet, checkseg); + if (checkseg.sh != NULL) { + // Check if this segment is inside our cavity. + spintet = neightet; + while (1) { + fnextself(spintet); + if (!infected(spintet)) break; // Not inside. + if (apex(spintet) == apex(neightet)) { + if (b->verbose > 1) { + printf(" p:draw_subseg(%d, %d) -- is inside.\n", + pointmark(sorg(checkseg)), pointmark(sdest(checkseg))); + } + if (!smarktested(checkseg)) { + missegs->newindex((void **) &parysh); + *parysh = checkseg; + } else { + if (b->verbose > 1) { + printf(" !! A fixed segment.\n"); + } + success = false; + } + break; + } + } // while (1) + if (!success) break; + } + } // j + if (success) { + // We can enlarge the cavity. + if (b->verbose > 1) { + printf(" Add a crosstet (%d, %d, %d, %d).\n", + pointmark(org(*parytet)), pointmark(dest(*parytet)), + pointmark(apex(*parytet)), pointmark(oppo(*parytet))); + } + crosstets->newindex((void **) &parytet1); + *parytet1 = *parytet; + // Insert the opposite point if it is not in CT. + pt[0] = oppo(*parytet); + if (!pinfected(pt[0])) { + if (b->verbose > 1) { + printf(" Insert oppo-point %d.\n", pointmark(pt[0])); + } + pinfect(pt[0]); + cavpoints->newindex((void **) &parypt); + *parypt = pt[0]; + searchtet = recenttet; + flipinsertvertex(pt[0], &searchtet, 3); + } + /*// Add three opposite faces into the boundary list. + for (j = 0; j < 3; j++) { + enext0fnext(*parytet, neightet); + symself(neightet); + if (!infected(neightet)) { + if (b->verbose > 1) { + printf(" Add a cavface (%d, %d, %d).\n", + pointmark(org(neightet)), pointmark(dest(neightet)), + pointmark(apex(neightet))); + } + cavfaces->newindex((void **) &parytet1); + *parytet1 = neightet; + } else { + // It is an interior face, we do not check subface + // for this routine. + } + enextself(*parytet); + } // j */ + } else { + // Do not enlarge it due to an existing segment. + uninfect(*parytet); + } + } // if (!infected(*parytet)) + if (!success) break; + } // i + + for (i = 0; i < cavpoints->objects; i++) { + parypt = (point *) fastlookup(cavpoints, i); + puninfect(*parypt); + } + + misfaces->restart(); + cavfaces->restart(); + + if (success && (missegs->objects > 0l)) { + // The cavity has been enlarged, but some segments are enclosed. + if (!smarktested(*sseg)) { + smarktest(*sseg); + fixedseglist->newindex((void **) &paryseg); + *paryseg = *sseg; + } + // SELF_CHECK + assert(cavshells->objects == 0l); + assert(newtets->objects == 0l); + // Recover the missing segments inside the enlarged cavity. + success = recoversegments(fixedseglist, missegs, cavfaces, cavshells, + misfaces, newtets); + // SELF_CHECK + assert(missegs->objects == 0l); + assert(cavshells->objects == 0l); + assert(newtets->objects == 0l); + assert(cavfaces->objects == 0l); + } + + if (success) { + // Cavity has enlarged. Continue to recover the segment. + cavityexpcount++; + continue; + } + } // if (misfaces->objects > 0) + + break; // Leave the loop. + + } // while (1) + + // Collect all tets of the DT. + marktest(recenttet); + newtets->newindex((void **) &parytet); + *parytet = recenttet; + for (i = 0; i < newtets->objects; i++) { + searchtet = * (triface *) fastlookup(newtets, i); + for (searchtet.loc = 0; searchtet.loc < 4; searchtet.loc++) { + sym(searchtet, neightet); + if (!marktested(neightet)) { + marktest(neightet); + newtets->newindex((void **) &parytet); + *parytet = neightet; + } + } + } + // Comment: All new tets are marktested. + + cavpoints->restart(); // Comment: Now no vertex is marked. + cavfaces->restart(); + + return success; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// recoversegments() Recover segments by local remeshing. // +// // +/////////////////////////////////////////////////////////////////////////////// + +bool tetgenmesh::recoversegments(arraypool* fixedseglist, arraypool *recosegs, + arraypool* cavfaces, arraypool* cavshells, arraypool* misfaces, + arraypool* newtets) +{ + arraypool *crosstets; + arraypool *missegs; + arraypool *cavpoints; + triface searchtet; + face *psseg, sseg; + enum intersection dir; + bool success; + long bakhullsize; + long cavitycount; + int i; + + if (b->verbose > 1) { + printf(" Recovering %ld segments (%ld fixed segments).\n", + recosegs->objects, fixedseglist->objects); + } + + // Initialize arrays. + crosstets = new arraypool(sizeof(triface), 10); + missegs = new arraypool(sizeof(face), 8); + cavpoints = new arraypool(sizeof(point), 8); + + success = true; + + // Loop until 'recosegs' is empty. + while (recosegs->objects > 0l) { + // seglist is used as a stack. + recosegs->objects--; + psseg = (face *) fastlookup(recosegs, recosegs->objects); + sseg = *psseg; + + // It is not a global missing segment. + assert(!sinfected(sseg)); + + // Form the segment cavity. + searchtet.tet = NULL; + dir = scoutsegment2(&sseg, &searchtet, crosstets); + + // The segment is missing. + assert(dir != SHAREEDGE); // SELF_CHECK + + if (dir == ACROSSTET) { + // Recover the segment by local re-meshing. + bakhullsize = hullsize; + success = tetrasegcavity(&sseg, crosstets, cavpoints, cavfaces, + cavshells, newtets, misfaces, missegs, fixedseglist); + // Do not clean 'fixedseglist'. + hullsize = bakhullsize; + if (success) { + // The segment is recovered. + fillcavity(cavshells, NULL, NULL, NULL); + carvecavity(crosstets, newtets, NULL); + } else { + // Unable to recover the segment. + restorecavity(crosstets, newtets, NULL); + break; + } + } else { + // Unexpected return type. + assert(0); // Not handled yet. + } + } + + if (recosegs->objects > 0l) { + recosegs->restart(); + } + + delete crosstets; + delete missegs; + delete cavpoints; + + return success; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// constrainedsegments() Recover segments in a constrained // +// tetrahedralization. // +// // +// 'recoverseglist' contains a list of recovering segments, 'fixedseglist' // +// is an accumulated list of segments which are inside current tetrahedrali- // +// zation and must "fix" at their places (do not remove them). All segments // +// in 'fixedseglist' are smarktested. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::constrainedsegments() +{ + arraypool *crosstets, *newtets, *misfaces; + arraypool *cavfaces; + arraypool *cavpoints; + arraypool *cavshells; + arraypool *fixedseglist, *missegs; + triface searchtet; + face splitsh; + face *psseg, sseg; + point newpt, pa, pb; + enum intersection dir; + bool success; + long bakhullsize; + long cavitycount; + long steinptcount; + int i; + + if (b->verbose) { + printf(" Recovering %ld segments.\n", subsegstack->objects); + } + + // Initialize arrays. + crosstets = new arraypool(sizeof(triface), 10); + newtets = new arraypool(sizeof(triface), 10); + misfaces = new arraypool(sizeof(triface), 10); + cavfaces = new arraypool(sizeof(triface), 10); + cavshells = new arraypool(sizeof(face), 10); + cavpoints = new arraypool(sizeof(point), 8); + fixedseglist = new arraypool(sizeof(face), 8); + missegs = new arraypool(sizeof(face), 8); + + cavitycount = 0l; + steinptcount = 0l; + + // Loop until 'subsegstack' is empty. + while (subsegstack->objects > 0l) { + // seglist is used as a stack. + subsegstack->objects--; + psseg = (face *) fastlookup(subsegstack, subsegstack->objects); + sseg = *psseg; + + if (!sinfected(sseg)) continue; // Not a missing segment. + suninfect(sseg); + + // Insert the segment. + searchtet.tet = NULL; + dir = scoutsegment2(&sseg, &searchtet, crosstets); + + if (dir != SHAREEDGE) { + // The segment is missing. + if (dir == ACROSSTET) { + // Recover the segment by local re-meshing. + bakhullsize = hullsize; + success = tetrasegcavity(&sseg, crosstets, cavpoints, cavfaces, + cavshells, newtets, misfaces, missegs, fixedseglist); + hullsize = bakhullsize; + // Unmark the testmarked segments. + for (i = 0; i < fixedseglist->objects; i++) { + psseg = (face *) fastlookup(fixedseglist, i); + assert(smarktested(*psseg)); + sunmarktest(*psseg); + } + fixedseglist->restart(); // Clear this list. + if (success) { + // The segment is recovered. + fillcavity(cavshells, NULL, NULL, NULL); + carvecavity(crosstets, newtets, NULL); + cavitycount++; + } else { + // Unable to recover the segment. + restorecavity(crosstets, newtets, NULL); + /*// Split the segment at its middle. + makepoint(&newpt); + pa = sorg(sseg); + pb = sdest(sseg); + for (i = 0; i < 3; i++) { + newpt[i] = 0.5 * (pa[i] + pb[i]); + } + setpointtype(newpt, STEINERVERTEX); + // Insert the point into the surface mesh. + spivot(sseg, splitsh); + // Two subsegments are queued in 'subsegstack' for recovery. + sinsertvertex(newpt, &splitsh, &sseg, true, false); + // Insert the point into the CT. + point2tetorg(pa, searchtet); + // Set 'flipflag' = 3, do not flip a segment. + flipinsertvertex(newpt, &searchtet, 3); + */ + steinptcount++; + } + } else if (dir == ACROSSVERT) { + printf("Error: Invalid PLC! A point and a segment intersect.\n"); + pa = farsorg(sseg); + pb = farsdest(sseg); + printf(" Point: %d. Segment: (%d, %d).\n", pointmark(org(searchtet)), + pointmark(pa), pointmark(pb)); + terminatetetgen(1); + } else if (dir == ACROSSSUBSEG) { + printf("Error: Invalid PLC! Two segments intersect.\n"); + pa = farsorg(sseg); + pb = farsdest(sseg); + printf(" 1st: (%d, %d)", pointmark(pa), pointmark(pb)); + tsspivot(searchtet, sseg); + assert(sseg.sh != NULL); + pa = farsorg(sseg); + pb = farsdest(sseg); + printf(" 2nd: (%d, %d).\n", pointmark(pa), pointmark(pb)); + terminatetetgen(1); + } else if (dir == ACROSSSUBFACE) { + printf("Error: Invalid PLC! A segment and a subface intersect.\n"); + pa = farsorg(sseg); + pb = farsdest(sseg); + printf(" Segment: (%d, %d)", pointmark(pa), pointmark(pb)); + tspivot(searchtet, splitsh); + printf(" Subface: (%d, %d, %d)", pointmark(sorg(splitsh)), + pointmark(sdest(splitsh)), pointmark(sapex(splitsh))); + terminatetetgen(1); + } + } + } + + if (b->verbose) { + printf(" %ld cavities remeshed.\n", cavitycount); + printf(" %ld Steiner points inserted.\n", steinptcount); + } + + delete crosstets; + delete newtets; + delete misfaces; + delete cavfaces; + delete cavshells; + delete cavpoints; + delete fixedseglist; + delete missegs; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// formskeleton() Form a constrained tetrahedralization. // +// // +// The segments and facets of a PLS will be recovered. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::formskeleton() +{ + face *pssub, ssub; + REAL bakeps; + long bakflip22count; + long bakcavitycount; + int s, i; + + if (!b->quiet) { + printf("Recovering boundaries.\n"); + } + + // Bakup the epsilon. + bakeps = b->epsilon; + b->epsilon = 0; + + // Put all segments into the list. + if (b->order == 4) { // '-o4' option (for debug) + // The sequential order. + subsegpool->traversalinit(); + for (i = 0; i < subsegpool->items; i++) { + ssub.sh = shellfacetraverse(subsegpool); + sinfect(ssub); // Only save it once. + subsegstack->newindex((void **) &pssub); + *pssub = ssub; + } + } else { + // Randomly order the segments. + subsegpool->traversalinit(); + for (i = 0; i < subsegpool->items; i++) { + s = randomnation(i + 1); + // Move the s-th seg to the i-th. + subsegstack->newindex((void **) &pssub); + *pssub = * (face *) fastlookup(subsegstack, s); + // Put i-th seg to be the s-th. + ssub.sh = shellfacetraverse(subsegpool); + sinfect(ssub); // Only save it once. + pssub = (face *) fastlookup(subsegstack, s); + *pssub = ssub; + } + } + + // Segments will be introduced. + checksubsegs = 1; + + // Recover segments. + if (b->nobisect == 0) { + if (b->verbose) { + printf(" Delaunizing segments.\n"); + } + + delaunizesegments(); + + if (b->verbose) { + printf(" %d protecting points.\n", r1count + r2count + r3count); + } + } else { + // -Y option, constrained recover. + constrainedsegments(); + } + + // Randomly order the subfaces. + subfacepool->traversalinit(); + for (i = 0; i < subfacepool->items; i++) { + s = randomnation(i + 1); + // Move the s-th subface to the i-th. + subfacstack->newindex((void **) &pssub); + *pssub = * (face *) fastlookup(subfacstack, s); + // Put i-th subface to be the s-th. + ssub.sh = shellfacetraverse(subfacepool); + pssub = (face *) fastlookup(subfacstack, s); + *pssub = ssub; + } + + // Subfaces will be introduced. + checksubfaces = 1; + bakflip22count = flip22count; + bakcavitycount = cavitycount; + + if (b->verbose) { + printf(" Constraining facets.\n"); + } + + // Initialize arrays. + tg_crosstets = new arraypool(sizeof(triface), 10); + tg_topnewtets = new arraypool(sizeof(triface), 10); + tg_botnewtets = new arraypool(sizeof(triface), 10); + tg_topfaces = new arraypool(sizeof(triface), 10); + tg_botfaces = new arraypool(sizeof(triface), 10); + tg_midfaces = new arraypool(sizeof(triface), 10); + tg_toppoints = new arraypool(sizeof(point), 8); + tg_botpoints = new arraypool(sizeof(point), 8); + tg_facpoints = new arraypool(sizeof(point), 8); + tg_facfaces = new arraypool(sizeof(face), 10); + tg_topshells = new arraypool(sizeof(face), 10); + tg_botshells = new arraypool(sizeof(face), 10); + + // Recover facets. + constrainedfacets(); + + // Delete arrays. + delete tg_crosstets; + delete tg_topnewtets; + delete tg_botnewtets; + delete tg_topfaces; + delete tg_botfaces; + delete tg_midfaces; + delete tg_toppoints; + delete tg_botpoints; + delete tg_facpoints; + delete tg_facfaces; + delete tg_topshells; + delete tg_botshells; + + if (b->verbose) { + printf(" %ld subedge flips.\n", flip22count - bakflip22count); + printf(" %ld cavities remeshed.\n", cavitycount - bakcavitycount); + } + + // checksubsegs = 0; + b->epsilon = bakeps; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// carveholes() Remove tetrahedra not in the mesh domain. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::carveholes() +{ + arraypool *tetarray; + triface tetloop, neightet, hulltet, *parytet, *parytet1, fliptets[3]; + triface openface, casface; + triface *regiontets; + face checksh, neighsh, flipshs[2]; + face checkseg; + point *ppt, pa, pb, pc; + enum location loc; + REAL volume; + int attrnum, attr, maxattr; + int flatcount; + int i, j, k; + + tetrahedron ptr; + int *iptr, tver; + + if (!b->quiet) { + printf("Removing exterior tetrahedra.\n"); + } + + // Initialize the pool of exterior tets. + tetarray = new arraypool(sizeof(triface), 10); + + maxattr = 0; // Choose a small number here. + attrnum = in->numberoftetrahedronattributes; + + // Mark as infected any unprotected hull tets. + tetrahedronpool->traversalinit(); + tetloop.loc = 0; + tetloop.tet = alltetrahedrontraverse(); + while (tetloop.tet != (tetrahedron *) NULL) { + if ((point) tetloop.tet[7] == dummypoint) { + // Is this side protected by a subface? + tspivot(tetloop, checksh); + if (checksh.sh == NULL) { + infect(tetloop); + tetarray->newindex((void **) &parytet); + *parytet = tetloop; + } + } + tetloop.tet = alltetrahedrontraverse(); + } + + hullsize -= tetarray->objects; + + if (in->numberofholes > 0) { + // Mark as infected any tets inside volume holes. + for (i = 0; i < 3 * in->numberofholes; i += 3) { + // Search a tet containing the i-th hole point. + neightet.tet = NULL; + randomsample(&(in->holelist[i]), &neightet); + loc = locate(&(in->holelist[i]), &neightet); + if (loc != OUTSIDE) { + infect(neightet); + tetarray->newindex((void **) &parytet); + *parytet = neightet; + } + } + } + + if (b->regionattrib && (in->numberofregions > 0)) { // If has -A option. + // Record the tetrahedra that contains the region points for assigning + // region attributes after the holes have been carved. + regiontets = new triface[in->numberofregions]; + // Mark as marktested any tetrahedra inside volume regions. + for (i = 0; i < 5 * in->numberofregions; i += 5) { + // Search a tet containing the i-th hole point. + neightet.tet = NULL; + randomsample(&(in->regionlist[i]), &neightet); + loc = locate(&(in->regionlist[i]), &neightet); + if (loc != OUTSIDE) { + regiontets[i/5] = neightet; + if ((int) in->regionlist[i + 3] > maxattr) { + maxattr = (int) in->regionlist[i + 3]; + } + } else { + if (b->verbose) { + printf("Warning: The %d-th region point is in outside.\n", i/5+1); + } + regiontets[i/5].tet = NULL; + } + } + } + + // Find and infect all exterior tets (in concave place and in holes). + for (i = 0; i < tetarray->objects; i++) { + parytet = (triface *) fastlookup(tetarray, i); + tetloop = *parytet; + for (tetloop.loc = 0; tetloop.loc < 4; tetloop.loc++) { + symedge(tetloop, neightet); + // Is this side protected by a subface? + tspivot(tetloop, checksh); + if (checksh.sh == NULL) { + // Not protected. Infect it if it is not a hull tet. + if ((point) neightet.tet[7] != dummypoint) { + if (!infected(neightet)) { + infect(neightet); + tetarray->newindex((void **) &parytet); + *parytet = neightet; + } + } + } else { + // Its adjacent tet is protected. + if ((point) neightet.tet[7] == dummypoint) { + // A hull tet. It is dead. + assert(!infected(neightet)); + infect(neightet); + tetarray->newindex((void **) &parytet); + *parytet = neightet; + // Both sides of this subface are exterior. + stdissolve(checksh); + hullsize--; + } else { + if (!infected(neightet)) { + // Let the subface connect to the "live" tet. + tsbond(neightet, checksh); + } else { + // Both sides of this subface are exterior. + stdissolve(checksh); + } + } + } + } + } + + if (b->regionattrib && (in->numberofregions > 0)) { + // Re-check saved region tets to see if they lie outside. + for (i = 0; i < in->numberofregions; i++) { + if (infected(regiontets[i])) { + if (b->verbose) { + printf("Warning: The %d-th region point is in outside.\n", i+1); + } + regiontets[i].tet = NULL; + } + } + } + + // Remove all exterior tetrahedra (including infected hull tets). + for (i = 0; i < tetarray->objects; i++) { + parytet = (triface *) fastlookup(tetarray, i); + tetloop = *parytet; + for (tetloop.loc = 0; tetloop.loc < 4; tetloop.loc++) { + symedge(tetloop, neightet); + if (!infected(neightet)) { + // A "live" tet (may be a hull tet). Clear its adjacent tet. + neightet.tet[neightet.loc] = NULL; + } + } + tetrahedrondealloc(parytet->tet); + } + + tetarray->restart(); // Re-use it for new hull tets. + + // Create new hull tets. + // Update point-to-tet map, segment-to-tet map, and subface-to-tet map. + tetrahedronpool->traversalinit(); + tetloop.ver = 0; + tetloop.tet = tetrahedrontraverse(); + while (tetloop.tet != (tetrahedron *) NULL) { + for (tetloop.loc = 0; tetloop.loc < 4; tetloop.loc++) { + if (tetloop.tet[tetloop.loc] == NULL) { + tspivot(tetloop, checksh); + assert(checksh.sh != NULL); // SELF_CHECK + // Create a new hull tet. + maketetrahedron(&hulltet); + pa = org(tetloop); + pb = dest(tetloop); + pc = apex(tetloop); + setvertices(hulltet, pb, pa, pc, dummypoint); + bond(tetloop, hulltet); + // Update subface-to-tet map. + tsbond(hulltet, checksh); + // Update segment-to-tet map. + for (i = 0; i < 3; i++) { + tsspivot(tetloop, checkseg); + if (checkseg.sh != NULL) { + tssbond1(hulltet, checkseg); + sstbond(checkseg, hulltet); + } + enextself(tetloop); + enext2self(hulltet); + } + // Save this hull tet in list. + tetarray->newindex((void **) &parytet); + *parytet = hulltet; + } + } + tetloop.loc = 0; + ptr = encode(tetloop); + ppt = (point *) tetloop.tet; + for (i = 4; i < 8; i++) { + point2tet(ppt[i]) = ptr; + } + tetloop.tet = tetrahedrontraverse(); + } + + // Update the hull size. + hullsize += tetarray->objects; + + // Connect new hull tets. + for (i = 0; i < tetarray->objects; i++) { + parytet = (triface *) fastlookup(tetarray, i); + hulltet = *parytet; + assert(oppo(hulltet) == dummypoint); // SELF_CHECK + hulltet.ver = 0; + for (j = 0; j < 3; j++) { + enext0fnext(hulltet, neightet); + if (neightet.tet[neightet.loc] == NULL) { + esym(hulltet, casface); + while (1) { + symedgeself(casface); + enext0fnextself(casface); + if (apex(casface) == dummypoint) break; + } + bond(neightet, casface); + } + enextself(hulltet); + } + } + + ////////////////////////////////////////////////////////////////////// + // Peel off "flat" tetrahedra at boundary. + // + // A tet is flat if it contains two subfaces of the same facet. + // Flat tets are possible when a facet is defined by non-exactly + // coplanar vertices. + + tetarray->restart(); // Re-use this array. + flatcount = 0; + + // Queue flat tets. + tetrahedronpool->traversalinit(); + tetloop.ver = 0; + tetloop.tet = tetrahedrontraverse(); + while (tetloop.tet != (tetrahedron *) NULL) { + // Does this tet contain subfaces? + if (tetloop.tet[9] != NULL) { + // Look at shared subface at its 6 edges. + for (i = 0; i < 6; i++) { + tetloop.loc = edge2locver[i][0]; + tetloop.ver = edge2locver[i][1]; + // Is this edge a segment? + tsspivot(tetloop, checkseg); + if (checkseg.sh == NULL) { + // No segment. Is this edge shared by two subfaces? + tspivot(tetloop, checksh); + if (checksh.sh != NULL) { + enext0fnext(tetloop, neightet); + tspivot(neightet, neighsh); + if (neighsh.sh != NULL) { + if (b->verbose > 1) { + ppt = (point *) &tetloop.tet[4]; + printf(" p:draw_tet(%d, %d, %d, %d) -- flat\n", + pointmark(ppt[0]), pointmark(ppt[1]), pointmark(ppt[2]), + pointmark(ppt[3])); + } + tetarray->newindex((void **) &parytet); + *parytet = tetloop; + break; + } // neighsh.sh != NULL + } // checksh.sh != NULL + } // checkseg.sh != NULL + } // i + } + tetloop.tet = tetrahedrontraverse(); + } + + if (tetarray->objects > 0) { + if (b->verbose) { + printf(" Removing flat boundary tetrahedra.\n"); + } + } + + // Remove flat tets, new flat tets are queued. + for (i = 0; i < tetarray->objects; i++) { + parytet = (triface *) fastlookup(tetarray, i); + assert(parytet->tet[4] != NULL); // SELF_CHECK + sym(*parytet, neightet); + if ((point) neightet.tet[7] != dummypoint) { + continue; // An internal face. Can't be peeled off. + } + + if (b->verbose > 1) { + printf(" i = %d.\n", i); + } + + enext0fnext(*parytet, neightet); + pa = org(*parytet); + pb = dest(*parytet); + tspivot(*parytet, flipshs[0]); // [0] abc + for (j = 0; j < 3; j++) { + if (sorg(flipshs[0]) == pa) break; + senextself(flipshs[0]); + } + assert(j < 3); // SELF_CHECK + if (sdest(flipshs[0]) != pb) { + senext2self(flipshs[0]); + sesymself(flipshs[0]); + } + assert(sdest(flipshs[0]) == pb); // SELF_CHECK + tspivot(neightet, flipshs[1]); // [1] bda + for (j = 0; j < 3; j++) { + if (sorg(flipshs[1]) == pb) break; + senextself(flipshs[1]); + } + assert(j < 3); // SELF_CHECK + if (sdest(flipshs[1]) != pa) { + senext2self(flipshs[1]); + sesymself(flipshs[1]); + } + assert(sdest(flipshs[1]) == pa); // SELF_CHECK + + // Detach abc and bad. + sym(*parytet, casface); + tsdissolve(*parytet); + tsdissolve(casface); + sym(neightet, casface); + tsdissolve(neightet); + tsdissolve(casface); + + // flip [0]abc,[1]bad to [0]cdb, [1]dca + flip22(flipshs, 0); + + for (k = 0; k < 2; k++) { + if (k == 0) { + // Insert flipshs[0] [c,d,b] to adjacent tets. + enextfnext(*parytet, neightet); // face [b,c,d]. + enextself(neightet); // edge [c,d] in face [c,d,b]. + } else { + // Insert flipshs[1] [d,c,a] to adjacent tets. + enext2fnext(*parytet, neightet); // face [c,a,d]. + enext2self(neightet); // edge [d,c] in face [d,c,a]. + } + symedge(neightet, casface); + assert((point) casface.tet[7] != dummypoint); // SELF_CHECK + tspivot(neightet, checksh); // SELF_CHECK + assert(checksh.sh == NULL); // SELF_CHECK + tsbond(neightet, flipshs[k]); + tsbond(casface, flipshs[k]); + // Check for new invalid tet(s) (at edge [d,b] and [b,c]). + for (j = 0; j < 2; j++) { + enextself(casface); // edges [d,b], [b,c]. + tsspivot(casface, checkseg); + if (checkseg.sh == NULL) { + enext0fnext(casface, openface); + tspivot(openface, checksh); + if (checksh.sh != NULL) { + if (b->verbose > 1) { + ppt = (point *) &casface.tet[4]; + printf(" p:draw_tet(%d, %d, %d, %d) -- flat\n", + pointmark(ppt[0]), pointmark(ppt[1]), pointmark(ppt[2]), + pointmark(ppt[3])); + } + tetarray->newindex((void **) &parytet1); + *parytet1 = casface; + break; + } + } + } // j + } // k + + // Peel the flat boundary tet by a flip32. + fliptets[0] = *parytet; + fnext(fliptets[0], fliptets[1]); + fnext(fliptets[1], fliptets[2]); + assert(apex(fliptets[2]) == dummypoint); // SELF_CHECK + assert(oppo(fliptets[2]) == apex(fliptets[0])); // SELF_CHECK + + // Flip the tets (with hull tets, do not propagate). + flip32(fliptets, 1, 0); + // Now the flat boundary tet is removed. + flatcount++; + } // i + + if (tetarray->objects > 0) { + if (b->verbose) { + printf(" %d flat tets are removed.\n", flatcount); + } + } + + ///////////////////////////////////////////////////////////////////////// + + // Set region attributes (when has -A and -AA options). + if (b->regionattrib) { + + if (!b->quiet) { + printf("Spreading region attributes.\n"); + } + + // If has user-defined region attributes. + if (in->numberofregions > 0) { + // Spread region attributes. + for (i = 0; i < 5 * in->numberofregions; i += 5) { + if (regiontets[i/5].tet != NULL) { + attr = (int) in->regionlist[i + 3]; + volume = in->regionlist[i + 4]; + tetarray->restart(); // Re-use this array. + infect(regiontets[i/5]); + tetarray->newindex((void **) &parytet); + *parytet = regiontets[i/5]; + // Collect and set attrs for all tets of this region. + for (j = 0; j < tetarray->objects; j++) { + parytet = (triface *) fastlookup(tetarray, j); + tetloop = *parytet; + setelemattribute(tetloop.tet, attrnum, attr); + if (b->varvolume) { // If has -a option. + setvolumebound(tetloop.tet, volume); + } + for (tetloop.loc = 0; tetloop.loc < 4; tetloop.loc++) { + sym(tetloop, neightet); + // Is this side protected by a subface? + tspivot(tetloop, checksh); + if (checksh.sh == NULL) { + // Not protected. It must not be a hull tet. + // assert((point) neightet.tet[7] != dummypoint); + if ((point) neightet.tet[7] == dummypoint) { + assert(0); + } + if (!infected(neightet)) { + infect(neightet); + tetarray->newindex((void **) &parytet); + *parytet = neightet; + } + } else { + // Protected. Set attribute for hull tet as well. + if ((point) neightet.tet[7] == dummypoint) { + setelemattribute(neightet.tet, attrnum, attr); + if (b->varvolume) { // If has -a option. + setvolumebound(neightet.tet, volume); + } + } + } + } // loc + } // j + } + } // i + delete [] regiontets; + } + + if (b->regionattrib > 1) { // If has -AA option. + // Set attributes for all tetrahedra. + attr = maxattr + 1; + tetrahedronpool->traversalinit(); + tetloop.tet = tetrahedrontraverse(); + while (tetloop.tet != (tetrahedron *) NULL) { + if (!infected(tetloop)) { + // An unmarked region. + tetarray->restart(); // Re-use this array. + infect(tetloop); + tetarray->newindex((void **) &parytet); + *parytet = tetloop; + // Find and mark all tets. + for (j = 0; j < tetarray->objects; j++) { + parytet = (triface *) fastlookup(tetarray, j); + tetloop = *parytet; + setelemattribute(tetloop.tet, attrnum, attr); + for (tetloop.loc = 0; tetloop.loc < 4; tetloop.loc++) { + sym(tetloop, neightet); + // Is this side protected by a subface? + tspivot(tetloop, checksh); + if (checksh.sh == NULL) { + // Not protected. It must not be a hull tet. + assert((point) neightet.tet[7] != dummypoint); + if (!infected(neightet)) { + infect(neightet); + tetarray->newindex((void **) &parytet); + *parytet = neightet; + } + } else { + // Protected. Set attribute for hull tet as well. + if ((point) neightet.tet[7] == dummypoint) { + setelemattribute(neightet.tet, attrnum, attr); + } + } + } // loc + } + attr++; // Increase the attribute. + } + tetloop.tet = tetrahedrontraverse(); + } + // Until here, every tet has a region attribute. + } + + // Uninfect processed tets. + tetrahedronpool->traversalinit(); + tetloop.tet = tetrahedrontraverse(); + while (tetloop.tet != (tetrahedron *) NULL) { + uninfect(tetloop); + tetloop.tet = tetrahedrontraverse(); + } + + // Mesh elements contain region attributes now. + in->numberoftetrahedronattributes++; + + } // if (b->regionattrib) + + delete tetarray; +} + +#endif // #ifndef constrainCXX diff --git a/contrib/TetgenNew/delaunay.cxx b/contrib/TetgenNew/delaunay.cxx new file mode 100644 index 0000000000..a023c94b20 --- /dev/null +++ b/contrib/TetgenNew/delaunay.cxx @@ -0,0 +1,1464 @@ +#ifndef delaunayCXX +#define delaunayCXX + +#include "tetgen.h" + +/////////////////////////////////////////////////////////////////////////////// +// // +// randomnation() Generate a random number between 0 and 'choices' - 1. // +// // +/////////////////////////////////////////////////////////////////////////////// + +unsigned long tetgenmesh::randomnation(unsigned long choices) +{ + unsigned long newrandom; + + if (choices >= 714025l) { + newrandom = (randomseed * 1366l + 150889l) % 714025l; + randomseed = (newrandom * 1366l + 150889l) % 714025l; + newrandom = newrandom * (choices / 714025l) + randomseed; + if (newrandom >= choices) { + return newrandom - choices; + } else { + return newrandom; + } + } else { + randomseed = (randomseed * 1366l + 150889l) % 714025l; + return randomseed % choices; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// randomsample() Randomly sample the tetrahedra for point loation. // +// // +// This routine implements Muecke's Jump-and-walk point location algorithm. // +// It improves the simple walk-through by "jumping" to a good starting point // +// via random sampling. Searching begins from one of handles: the input // +// 'searchtet', a recently encountered tetrahedron 'recenttet', or from one // +// chosen from a random sample. The choice is made by determining which one // +// 's origin is closest to the point we are searcing for. Having chosen the // +// starting tetrahedron, the simple Walk-through algorithm is executed. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::randomsample(point searchpt, triface *searchtet) +{ + tetrahedron *firsttet, *tetptr; + point torg; + void **sampleblock; + long sampleblocks, samplesperblock, samplenum; + unsigned long alignptr; + REAL searchdist, dist; + int tetblocks, i, j; + + if ((searchtet->tet != NULL) && (searchtet->tet[4] != NULL)) { + // Get the distance from the suggested starting tet to the search point. + if ((point) searchtet->tet[7] != dummypoint) { + torg = org(*searchtet); + } else { + torg = (point) searchtet->tet[4]; + } + searchdist = NORM2(searchpt[0] - torg[0], searchpt[1] - torg[1], + searchpt[2] - torg[2]); + } else { + searchdist = NORM2(xmax - xmin, ymax - ymin, zmax - zmin); + } + + // If a recently encountered tetrahedron has been recorded and has not + // been deallocated, test it as a good starting point. + if ((recenttet.tet != NULL) && (recenttet.tet[4] != NULL)) { + if ((point) recenttet.tet[7] != dummypoint) { + torg = org(recenttet); + } else { + torg = (point) recenttet.tet[4]; + } + dist = NORM2(searchpt[0] - torg[0], searchpt[1] - torg[1], + searchpt[2] - torg[2]); + if (dist <= searchdist) { + *searchtet = recenttet; + searchdist = dist; + } + } + + // Select "good" candidate using k random samples, taking the closest one. + // The number of random samples taken is proportional to the fourth root + // of the number of tetrahedra in the mesh. + while (samples * samples * samples * samples < tetrahedronpool->items) { + samples++; + } + // Find how much blocks in current tet pool. + tetblocks = (tetrahedronpool->maxitems + ELEPERBLOCK - 1) / ELEPERBLOCK; + // Find the average samles per block. Each block at least have 1 sample. + samplesperblock = (samples + tetblocks - 1) / tetblocks; + sampleblocks = samples / samplesperblock; + sampleblock = tetrahedronpool->firstblock; + for (i = 0; i < sampleblocks; i++) { + alignptr = (unsigned long) (sampleblock + 1); + firsttet = (tetrahedron *) + (alignptr + (unsigned long) tetrahedronpool->alignbytes + - (alignptr % (unsigned long) tetrahedronpool->alignbytes)); + for (j = 0; j < samplesperblock; j++) { + if (i == tetblocks - 1) { + // This is the last block. + samplenum = randomnation((int) + (tetrahedronpool->maxitems - (i * ELEPERBLOCK))); + } else { + samplenum = randomnation(ELEPERBLOCK); + } + tetptr = (tetrahedron *) + (firsttet + (samplenum * tetrahedronpool->itemwords)); + if (tetptr[4] != (tetrahedron) NULL) { + torg = (point) tetptr[4]; + dist = NORM2(searchpt[0] - torg[0], searchpt[1] - torg[1], + searchpt[2] - torg[2]); + if (dist < searchdist) { + searchtet->tet = tetptr; + searchtet->loc = 0; + searchtet->ver = 0; + searchdist = dist; + } + } else { + if (i != tetblocks - 1) j--; + } + } + sampleblock = (void **) *sampleblock; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// locate() Find a simplex containing a given point. // +// // +// This routine implements the simple Walk-through point location algorithm. // +// Begins its search from 'searchtet', assume there is a line segment L from // +// the origin of 'searchtet' to the query point 'searchpt', and simply walk // +// towards 'searchpt' by traversing all faces intersected by L. // +// // +// On completion, 'searchtet' is a tetrahedron that contains 'searchpt'. The // +// returned value indicates one of the following cases: // +// - ONVERTEX, the search point lies on the origin of 'searchtet'. // +// - ONEDGE, the search point lies on an edge of 'searchtet'. // +// - ONFACE, the search point lies on a face of 'searchtet'. // +// - INTET, the search point lies in the interior of 'searchtet'. // +// - OUTSIDE, the search point lies outside the mesh. 'searchtet' is a // +// hull tetrahedron whose base face is visible by the search point. // +// // +// WARNING: This routine is designed for convex triangulations, and will not // +// generally work after the holes and concavities have been carved. // +// // +/////////////////////////////////////////////////////////////////////////////// + +enum tetgenmesh::location tetgenmesh::locate(point searchpt,triface* searchtet) +{ + triface neightet; + point torg, tdest, tapex, toppo, ntoppo; + enum {ORGMOVE, DESTMOVE, APEXMOVE} nextmove; + REAL ori, oriorg, oridest, oriapex; + REAL searchdist, dist; + + tetrahedron ptr; + int *iptr; + + if ((point) searchtet->tet[7] == dummypoint) { + // A hull tet. Choose the neighbor of its base face. + searchtet->loc = 0; + symself(*searchtet); + } else { + // Stay in the 0th edge ring. + if (searchtet->ver & 01) esymself(*searchtet); + } + // Let searchtet be the face such that 'searchpt' lies above to it. + for (; ; searchtet->loc = (searchtet->loc + 1) % 4) { + torg = org(*searchtet); + tdest = dest(*searchtet); + tapex = apex(*searchtet); + ori = orient3d(torg, tdest, tapex, searchpt); orient3dcount++; + if (ori < 0) { + // searchpt lies above searchtet's face. + break; + } else if (ori > 0) { + // searchpt lies below searchtet's face. + symself(*searchtet); + torg = org(*searchtet); + tdest = dest(*searchtet); + tapex = apex(*searchtet); + break; + } + // searchpt is coplanar with searchtet's face. Go to the next face. + } + + // Walk through tetrahedra to locate the point. + while (true) { + + ptloc_count++; // Algorithimic count. + + toppo = oppo(*searchtet); + + // Check if we have walked out of the domain. + if (toppo == dummypoint) { + return OUTSIDE; + } + + // Check if the vertex is we seek. + if (toppo == searchpt) { + // Adjust the origin of searchtet to be searchpt. + enext0fnextself(*searchtet); + esymself(*searchtet); + enext2self(*searchtet); + return ONVERTEX; + } + + // We enter from serarchtet's base face. There are three other faces in + // searchtet (all connecting to toppo), which one is the exit? + oriorg = orient3d(tdest, tapex, toppo, searchpt); + oridest = orient3d(tapex, torg, toppo, searchpt); + oriapex = orient3d(torg, tdest, toppo, searchpt); + orient3dcount+=3; + + // Now decide which face to move. It is possible there are more than one + // faces are viable moves. Use the opposite points of thier neighbors + // to discriminate, i.e., we choose the face whose opposite point has + // the shortest distance to searchpt. + if (oriorg < 0) { + if (oridest < 0) { + if (oriapex < 0) { + // Any of the three faces is a viable move. + nextmove = ORGMOVE; + enextfnext(*searchtet, neightet); + symself(neightet); + ntoppo = oppo(neightet); + if (ntoppo != dummypoint) { + searchdist = NORM2(searchpt[0] - ntoppo[0], + searchpt[1] - ntoppo[1], + searchpt[2] - ntoppo[2]); + } else { + searchdist = NORM2(xmax - xmin, ymax - ymin, zmax - zmin); + } + enext2fnext(*searchtet, neightet); + symself(neightet); + ntoppo = oppo(neightet); + if (ntoppo != dummypoint) { + dist = NORM2(searchpt[0] - ntoppo[0], searchpt[1] - ntoppo[1], + searchpt[2] - ntoppo[2]); + } else { + dist = searchdist; + } + if (dist < searchdist) { + nextmove = DESTMOVE; + searchdist = dist; + } + enext0fnext(*searchtet, neightet); + symself(neightet); + ntoppo = oppo(neightet); + if (ntoppo != dummypoint) { + dist = NORM2(searchpt[0] - ntoppo[0], searchpt[1] - ntoppo[1], + searchpt[2] - ntoppo[2]); + } else { + dist = searchdist; + } + if (dist < searchdist) { + nextmove = APEXMOVE; + searchdist = dist; + } + } else { + // Two faces, opposite to origin and destination, are viable. + nextmove = ORGMOVE; + enextfnext(*searchtet, neightet); + symself(neightet); + ntoppo = oppo(neightet); + if (ntoppo != dummypoint) { + searchdist = NORM2(searchpt[0] - ntoppo[0], + searchpt[1] - ntoppo[1], + searchpt[2] - ntoppo[2]); + } else { + searchdist = NORM2(xmax - xmin, ymax - ymin, zmax - zmin); + } + enext2fnext(*searchtet, neightet); + symself(neightet); + ntoppo = oppo(neightet); + if (ntoppo != dummypoint) { + dist = NORM2(searchpt[0] - ntoppo[0], searchpt[1] - ntoppo[1], + searchpt[2] - ntoppo[2]); + } else { + dist = searchdist; + } + if (dist < searchdist) { + nextmove = DESTMOVE; + searchdist = dist; + } + } + } else { + if (oriapex < 0) { + // Two faces, opposite to origin and apex, are viable. + nextmove = ORGMOVE; + enextfnext(*searchtet, neightet); + symself(neightet); + ntoppo = oppo(neightet); + if (ntoppo != dummypoint) { + searchdist = NORM2(searchpt[0] - ntoppo[0], + searchpt[1] - ntoppo[1], + searchpt[2] - ntoppo[2]); + } else { + searchdist = NORM2(xmax - xmin, ymax - ymin, zmax - zmin); + } + enext0fnext(*searchtet, neightet); + symself(neightet); + ntoppo = oppo(neightet); + if (ntoppo != dummypoint) { + dist = NORM2(searchpt[0] - ntoppo[0], searchpt[1] - ntoppo[1], + searchpt[2] - ntoppo[2]); + } else { + dist = searchdist; + } + if (dist < searchdist) { + nextmove = APEXMOVE; + searchdist = dist; + } + } else { + // Only the face opposite to origin is viable. + nextmove = ORGMOVE; + } + } + } else { + if (oridest < 0) { + if (oriapex < 0) { + // Two faces, opposite to destination and apex, are viable. + nextmove = DESTMOVE; + enext2fnext(*searchtet, neightet); + symself(neightet); + ntoppo = oppo(neightet); + if (ntoppo != dummypoint) { + searchdist = NORM2(searchpt[0] - ntoppo[0], + searchpt[1] - ntoppo[1], + searchpt[2] - ntoppo[2]); + } else { + searchdist = NORM2(xmax - xmin, ymax - ymin, zmax - zmin); + } + enext0fnext(*searchtet, neightet); + symself(neightet); + ntoppo = oppo(neightet); + if (ntoppo != dummypoint) { + dist = NORM2(searchpt[0] - ntoppo[0], searchpt[1] - ntoppo[1], + searchpt[2] - ntoppo[2]); + } else { + dist = searchdist; + } + if (dist < searchdist) { + nextmove = APEXMOVE; + searchdist = dist; + } + } else { + // Only the face opposite to destination is viable. + nextmove = DESTMOVE; + } + } else { + if (oriapex < 0) { + // Only the face opposite to apex is viable. + nextmove = APEXMOVE; + } else { + // The point we seek must be on the boundary of or inside this + // tetrahedron. Check for boundary cases. + if (oriorg == 0) { + // Go to the face opposite to origin. + enextfnextself(*searchtet); + if (oridest == 0) { + enextself(*searchtet); // edge apex->oppo + if (oriapex == 0) { + enextself(*searchtet); // oppo is duplicated with p. + return ONVERTEX; + } + return ONEDGE; + } + if (oriapex == 0) { + enext2self(*searchtet); + return ONEDGE; + } + return ONFACE; + } + if (oridest == 0) { + // Go to the face opposite to destination. + enext2fnextself(*searchtet); + if (oriapex == 0) { + enextself(*searchtet); + return ONEDGE; + } + return ONFACE; + } + if (oriapex == 0) { + // Go to the face opposite to apex + enext0fnextself(*searchtet); + return ONFACE; + } + return INTET; + } + } + } + + // Move to the selected face. + if (nextmove == ORGMOVE) { + enextfnextself(*searchtet); + } else if (nextmove == DESTMOVE) { + enext2fnextself(*searchtet); + } else { + enext0fnextself(*searchtet); + } + // Move to the adjacent tetrahedron (maybe a hull tetrahedron). + symself(*searchtet); + // Retreat the three vertices of the base face. + torg = org(*searchtet); + tdest = dest(*searchtet); + tapex = apex(*searchtet); + + } // while (true) +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// initialDT() Create an initial Delaunay tetrahedralization. // +// // +// The tetrahedralization contains only one tetrahedron abcd, and four hull // +// tetrahedra. The points pa, pb, pc, and pd must be linearly independent. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::initialDT(point pa, point pb, point pc, point pd) +{ + triface firsttet, tetopa, tetopb, tetopc, tetopd; + triface worktet, worktet1; + int *iptr; + + if (b->verbose > 1) { + printf(" Create init tet (%d, %d, %d, %d)\n", pointmark(pa), + pointmark(pb), pointmark(pc), pointmark(pd)); + } + + // Create the first tetrahedron. + maketetrahedron(&firsttet); + setvertices(firsttet, pa, pb, pc, pd); + // Create four hull tetrahedra. + maketetrahedron(&tetopa); + setvertices(tetopa, pb, pc, pd, dummypoint); + maketetrahedron(&tetopb); + setvertices(tetopb, pc, pa, pd, dummypoint); + maketetrahedron(&tetopc); + setvertices(tetopc, pa, pb, pd, dummypoint); + maketetrahedron(&tetopd); + setvertices(tetopd, pb, pa, pc, dummypoint); + hullsize += 4; + + // Connect hull tetrahedra to firsttet (at four faces of firsttet). + bond(firsttet, tetopd); // ab + enext0fnext(firsttet, worktet); + bond(worktet, tetopc); // ab + enextfnext(firsttet, worktet); + bond(worktet, tetopa); // bc + enext2fnext(firsttet, worktet); + bond(worktet, tetopb); // ca + + // Connect hull tetrahedra together (at six edges of firsttet). + enext0fnext(tetopc, worktet); + enext0fnext(tetopd, worktet1); + bond(worktet, worktet1); // ab + enext0fnext(tetopa, worktet); + enext2fnext(tetopd, worktet1); + bond(worktet, worktet1); // bc + enext0fnext(tetopb, worktet); + enextfnext(tetopd, worktet1); + bond(worktet, worktet1); // ca + enext2fnext(tetopc, worktet); + enextfnext(tetopb, worktet1); + bond(worktet, worktet1); // da + enext2fnext(tetopa, worktet); + enextfnext(tetopc, worktet1); + bond(worktet, worktet1); // db + enext2fnext(tetopb, worktet); + enextfnext(tetopa, worktet1); + bond(worktet, worktet1); // dc + + // Set the vertex type. + if (getpointtype(pa) == UNUSEDVERTEX) { + setpointtype(pa, VOLVERTEX); + } + if (getpointtype(pb) == UNUSEDVERTEX) { + setpointtype(pb, VOLVERTEX); + } + if (getpointtype(pc) == UNUSEDVERTEX) { + setpointtype(pc, VOLVERTEX); + } + if (getpointtype(pd) == UNUSEDVERTEX) { + setpointtype(pd, VOLVERTEX); + } + + // Update the point-to-tet map. + // if (checksubsegs || checksubfaces) { + point2tet(pa) = encode(firsttet); + point2tet(pb) = encode(firsttet); + point2tet(pc) = encode(firsttet); + point2tet(pd) = encode(firsttet); + // } + + // Remember the first tetrahedron. + recenttet = firsttet; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// insertvertex() Insert a point (p) into tetrahedralization (T). // +// // +// The point p will be first located in T. 'searchtet' is a suggested start- // +// tetrahedron, it can be NULL. Note that p may lies outside T. In such case,// +// the convex hull of T will be updated to include p as a vertex. // +// // +// If 'bwflag' is TRUE, the Bowyer-Watson algorithm is used to recover the // +// Delaunayness of T. Otherwise, do nothing with regard to the Delaunayness // +// T (T may be non-Delaunay after this function). // +// // +// If 'visflag' is TRUE, force to check the visibility of the boundary faces // +// of cavity. This is needed when T is not Delaunay. // +// // +// If 'noencsegflag' is TRUE, only insert the point if it does not encroach // +// on any existing segment of the mesh. Otherwise, do not insert the point, // +// and all encroaching segments are returned in subsegstack. // +// // +/////////////////////////////////////////////////////////////////////////////// + +enum tetgenmesh::location tetgenmesh::insertvertex(point insertpt, + triface *searchtet, bool bwflag, bool visflag, bool noencsegflag, + bool noencsubflag) +{ + triface *cavetet, *parytet, spintet, neightet, newtet, neineitet; + face *pssub, checksh; + face *psseg, sseg; + point *pts, pa, pb, pc; + enum location loc; + REAL sign, ori; + long tetcount; + bool enqflag; + int i, j, k; + + badface *newflip, *lastflip; // for bowyerwatson + triface fliptets[5], baktets[2]; + + tetrahedron ptr; + int *iptr, tver; + + arraypool *swaplist; // for updating cavity. + long updatecount; + + // clock_t loc_start, loc_end; + + if (b->verbose > 1) { + printf(" Insert point %d\n", pointmark(insertpt)); + } + + // loc_start = clock(); + + tetcount = ptloc_count; + updatecount = 0l; + + if (searchtet->tet == NULL) { + randomsample(insertpt, searchtet); + } + loc = locate(insertpt, searchtet); + + // loc_end = clock(); + // tloctime += ((REAL) (loc_end - loc_start)) / CLOCKS_PER_SEC; + + if (b->verbose > 1) { + printf(" Walk distance (# tets): %ld\n", ptloc_count - tetcount); + } + + if (ptloc_max_count < (ptloc_count - tetcount)) { + ptloc_max_count = (ptloc_count - tetcount); + } + + if (b->verbose > 1) { + printf(" Located (%d) tet (%d, %d, %d, %d).\n", (int) loc, + pointmark(org(*searchtet)), pointmark(dest(*searchtet)), + pointmark(apex(*searchtet)), pointmark(oppo(*searchtet))); + } + + if (loc == ONVERTEX) { + // The point already exists. Mark it and do nothing on it. + if (b->object != tetgenbehavior::STL) { + if (!b->quiet) { + printf("Warning: Point #%d is duplicated with Point #%d. Ignored!\n", + pointmark(insertpt), pointmark(org(*searchtet))); + } + } + point2ppt(insertpt) = org(*searchtet); + setpointtype(insertpt, DUPLICATEDVERTEX); + dupverts++; + return loc; + } + + // loc_start = clock(); + + tetcount = 0l; // The number of deallocated tets. + + // Create the initial boundary of the cavity. + if (loc == INTET || loc == OUTSIDE) { + // Add four adjacent boundary tets into list. + for (i = 0; i < 4; i++) { + decode(searchtet->tet[i], neightet); + cavetetlist->newindex((void **) &parytet); + *parytet = neightet; + } + if ((point) searchtet->tet[7] == dummypoint) hullsize--; + // tetrahedrondealloc(searchtet->tet); + infect(*searchtet); + caveoldtetlist->newindex((void **) &parytet); + *parytet = *searchtet; + tetcount = 1; + flip14count++; + } else if (loc == ONFACE) { + // Add six adjacent boundary tets into list. + for (i = 0; i < 3; i++) { + decode(searchtet->tet[locpivot[searchtet->loc][i]], neightet); + cavetetlist->newindex((void **) &parytet); + *parytet = neightet; + } + decode(searchtet->tet[searchtet->loc], spintet); + for (i = 0; i < 3; i++) { + decode(spintet.tet[locpivot[spintet.loc][i]], neightet); + cavetetlist->newindex((void **) &parytet); + *parytet = neightet; + } + if ((point) spintet.tet[7] == dummypoint) hullsize--; + if ((point) searchtet->tet[7] == dummypoint) hullsize--; + // tetrahedrondealloc(spintet.tet); + infect(spintet); + caveoldtetlist->newindex((void **) &parytet); + *parytet = spintet; + // tetrahedrondealloc(searchtet->tet); + infect(*searchtet); + caveoldtetlist->newindex((void **) &parytet); + *parytet = *searchtet; + tetcount = 2; + flip26count++; + } else if (loc == ONEDGE) { + // Add all adjacent boundary tets into list. + spintet = *searchtet; + tetcount = 0; + do { + fnextself(spintet); + tetcount++; + decode(spintet.tet[locverpivot[spintet.loc][spintet.ver][0]], neightet); + cavetetlist->newindex((void **) &parytet); + *parytet = neightet; + decode(spintet.tet[locverpivot[spintet.loc][spintet.ver][1]], neightet); + cavetetlist->newindex((void **) &parytet); + *parytet = neightet; + } while (spintet.tet != searchtet->tet); + // Delete old tets in the cavity. + spintet = *searchtet; + for (i = 0; i < tetcount; i++) { + fnext(spintet, neightet); + if ((point) spintet.tet[7] == dummypoint) hullsize--; + // tetrahedrondealloc(spintet.tet); + infect(spintet); + caveoldtetlist->newindex((void **) &parytet); + *parytet = spintet; + spintet = neightet; + } + flipn2ncount++; + } + + // Form the cavity by including tets from initial boundary. + for (i = 0; i < cavetetlist->objects; i++) { + // 'cavetet' is actually an adjacent tet to the cavity. + cavetet = (triface *) fastlookup(cavetetlist, i); + // Do check if it is not infected (not deleted yet). + if (!infected(*cavetet)) { // if (cavetet->tet[4] != NULL) { + // Check for two possible cases for this tet: + // (1) It is a cavity tet, or + // (2) it is a cavity boundary face. + // In case (1), the three other faces of this tet are added into + // 'cavetetlist' for later checking (we use a bread-first search), + // and this tet gets deleted (infected). + enqflag = false; + if (!marktested(*cavetet)) { + pts = (point *) cavetet->tet; + if (pts[7] != dummypoint) { + // A volume tet. Operate on it if it has not been tested yet. + if (bwflag) { + // Use Bowyer-Watson algorithm, do Delaunay check. + sign = insphere_sos(pts[4], pts[5], pts[6], pts[7], insertpt); + enqflag = (sign < 0.0); + } + } else { + // It is a hull tet. Check if its base face is visible by p. + // This happens when p lies outside the hull face. + ori = orient3d(pts[4], pts[5], pts[6], insertpt); orient3dcount++; + enqflag = (ori < 0.0); + // Check if this face is coplanar with p. This case may create + // a degenerate tet (zero volume). + // Note: for convex domain, it can only happen at a hull face. + if (bwflag && (ori == 0.0)) { + newflip = (badface *) flippool->alloc(); + newflip->tt = *cavetet; // Queue the adjacent tet (not in cavity). + newflip->tt.loc = 0; // Must be at the base face. + newflip->nextitem = NULL; + if (futureflip == NULL) { + lastflip = futureflip = newflip; + } else { + lastflip->nextitem = newflip; + lastflip = newflip; + } + } + } // if (pts[7] != dummypoint) + marktest(*cavetet); // Only test it once. + } + /*// Validation is needed when T is not a Delaunay triangulation. The + // default cavity may not be star-shaped (fig/dump-cavity-case8). + if (visflag && !enqflag) { + if ((point) cavetet->tet[7] != dummypoint) { + // A non-hull cavity boundary face. Validate it. + cavetet->ver = 4; + pa = org(*cavetet); + pb = dest(*cavetet); + pc = apex(*cavetet); + ori = orient3d(pa, pb, pc, insertpt); orient3dcount++; + assert(ori != 0.0); // SELF_CHECK + enqflag = (ori < 0.0); + if (enqflag) { + updatecount++; // Cavity is updated. + } + } + }*/ + if (enqflag) { + // Found a tet in the cavity. Put other three faces in check list. + for (j = 0; j < 3; j++) { + decode(cavetet->tet[locpivot[cavetet->loc][j]], neightet); + cavetetlist->newindex((void **) &parytet); + *parytet = neightet; + } + if ((point) cavetet->tet[7] == dummypoint) hullsize--; + // tetrahedrondealloc(cavetet->tet); + infect(*cavetet); + caveoldtetlist->newindex((void **) &parytet); + *parytet = *cavetet; + tetcount++; + } else { + // Found a boundary face of the cavity. It may be a face of a hull + // tet which contains 'dummypoint'. Choose the edge in the face + // such that its endpoints are not 'dummypoint', while its apex + // may be 'dummypoint' (see Fig. 1.4). + cavetet->ver = 4; + cavebdrylist->newindex((void **) &parytet); + *parytet = *cavetet; + } + } // if (cavetet->tet[4] != NULL) + } + + if (b->verbose > 1) { + printf(" Size of the cavity: %d faces %d tets.\n", + cavebdrylist->objects, tetcount); + } + + totaldeadtets += tetcount; + totalbowatcavsize += cavebdrylist->objects; + if (maxbowatcavsize < cavebdrylist->objects) { + maxbowatcavsize = cavebdrylist->objects; + } + + if (checksubsegs) { + // Check if some (sub)segments are inside the cavity. Such segments + // are queued in 'subsegstack'. + for (i = 0; i < caveoldtetlist->objects; i++) { + cavetet = (triface *) fastlookup(caveoldtetlist, i); + for (j = 0; j < 6; j++) { + cavetet->loc = edge2locver[j][0]; + cavetet->ver = edge2locver[j][1]; + tsspivot(*cavetet, sseg); + if ((sseg.sh != NULL) && !sinfected(sseg)) { + // Check if this segment is inside the cavity. + spintet = *cavetet; + pa = apex(spintet); + enqflag = true; + while (1) { + fnextself(spintet); + if (!infected(spintet)) { + enqflag = false; break; // It is not inside. + } + if (apex(spintet) == pa) break; + } + if (enqflag) { + if (b->verbose > 1) { + printf(" Queue a missing segment (%d, %d).\n", + pointmark(sorg(sseg)), pointmark(sdest(sseg))); + } + // All tets containing this segment will be dead, clean the + // seg-to-tet pointer. + stdissolve(sseg); + sinfect(sseg); // Only save it once. + subsegstack->newindex((void **) &psseg); + *psseg = sseg; + } + } + } + } + if (noencsegflag) { + // Check for encroaching segment on the boundary of the cavity. + // Encroached segments are queued in 'subsegstack'. + for (i = 0; i < cavebdrylist->objects; i++) { + cavetet = (triface *) fastlookup(cavebdrylist, i); + // 'cavetet' is an exterior tet adjacent to the cavity. + assert(cavetet->ver == 4); // SELF_CHECK + for (j = 0; j < 3; j++) { + tsspivot(*cavetet, sseg); + if (sseg.sh != NULL) { + // Found a segment. Check it if it is not queued yet. + if (!sinfected(sseg)) { + if (checkedge4encroach(sseg, insertpt, 0)) { + if (b->verbose > 1) { + printf(" Queue an encroaching segment (%d, %d).\n", + pointmark(sorg(sseg)), pointmark(sdest(sseg))); + } + // This segment will still be connected to a tet after the + // insertion. + sinfect(sseg); // Only save it once. + subsegstack->newindex((void **) &psseg); + *psseg = sseg; + } + } + } + enextself(*cavetet); + } + } + } + } + + if (noencsegflag && (subsegstack->objects > 0)) { + // Found encroached subsegments! Do not insert this point. + for (i = 0; i < caveoldtetlist->objects; i++) { + cavetet = (triface *) fastlookup(caveoldtetlist, i); + uninfect(*cavetet); + unmarktest(*cavetet); + } + for (i = 0; i < cavebdrylist->objects; i++) { + cavetet = (triface *) fastlookup(cavebdrylist, i); + unmarktest(*cavetet); // Unmark it. + } + if (bwflag && (futureflip != NULL)) { + flippool->restart(); + futureflip = NULL; + } + cavetetlist->restart(); + cavebdrylist->restart(); + caveoldtetlist->restart(); + return ENCSEGMENT; + } + + if (checksubfaces) { + // Check if some subfaces are inside the cavity. Such subfaces + // are queued in 'subfacstack'. + for (i = 0; i < caveoldtetlist->objects; i++) { + cavetet = (triface *) fastlookup(caveoldtetlist, i); + neightet.tet = cavetet->tet; + for (neightet.loc = 0; neightet.loc < 4; neightet.loc++) { + tspivot(neightet, checksh); + if (checksh.sh != NULL) { + sym(neightet, neineitet); + // Do not check it if it is a hull tet. + if (infected(neineitet)) { + if (b->verbose > 1) { + printf(" Queue a missing subface (%d, %d, %d).\n", + pointmark(sorg(checksh)), pointmark(sdest(checksh)), + pointmark(sapex(checksh))); + } + tsdissolve(neineitet); // Disconnect a tet-sub bond. + stdissolve(checksh); // Disconnect the sub-tet bond. + // Add the missing subface into list. + subfacstack->newindex((void **) &pssub); + *pssub = checksh; + } + } + } + } + if (noencsubflag) { + // Check for encroaching subface on the boundary of the cavity. + // Encroached subfaces are queued in 'subfacstack'. + for (i = 0; i < cavebdrylist->objects; i++) { + cavetet = (triface *) fastlookup(cavebdrylist, i); + // 'cavetet' is an exterior tet adjacent to the cavity. + assert(cavetet->ver == 4); // SELF_CHECK + tspivot(*cavetet, checksh); + if (checksh.sh != NULL) { + // checkface4encroach(); + } + } + } + } + + if (noencsubflag && (subfacstack->objects > 0)) { + // Found encroached subfaces! Do not insert this point. + for (i = 0; i < caveoldtetlist->objects; i++) { + cavetet = (triface *) fastlookup(caveoldtetlist, i); + uninfect(*cavetet); + unmarktest(*cavetet); + } + for (i = 0; i < cavebdrylist->objects; i++) { + cavetet = (triface *) fastlookup(cavebdrylist, i); + unmarktest(*cavetet); // Unmark it. + } + if (bwflag && (futureflip != NULL)) { + flippool->restart(); + futureflip = NULL; + } + cavetetlist->restart(); + cavebdrylist->restart(); + caveoldtetlist->restart(); + return ENCFACE; + } + + if (visflag) { + // If T is not a Delaunay triangulation, the formed cavity may not be + // star-shaped (fig/dump-cavity-case8). Validation is needed. + // Comment: The validation is done by removing tets from the cavity + // until the cavity is star-shaped. + cavetetlist->restart(); // Re-use it. + for (i = 0; i < cavebdrylist->objects; i++) { + cavetet = (triface *) fastlookup(cavebdrylist, i); + // 'cavetet' is an exterior tet adjacent to the cavity. + assert(cavetet->ver == 4); // SELF_CHECK + symedge(*cavetet, neightet); + if (infected(neightet)) { + if ((point) cavetet->tet[7] != dummypoint) { + pa = org(*cavetet); + pb = dest(*cavetet); + pc = apex(*cavetet); + ori = orient3d(pa, pb, pc, insertpt); orient3dcount++; + // assert(ori != 0.0); // SELF_CHECK + enqflag = (ori > 0.0); + } else { + enqflag = true; // A hull face. + } + if (enqflag) { + // This face is valid, save it. + cavetetlist->newindex((void **) &parytet); + *parytet = *cavetet; + } else { + if (b->verbose > 1) { + printf(" Cut tet (%d, %d, %d, %d)\n", pointmark(pb), + pointmark(pa), pointmark(pc), pointmark(oppo(neightet))); + } + uninfect(neightet); + unmarktest(neightet); + updatecount++; + // Add three new faces to find new boundaries. + for (j = 0; j < 3; j++) { + enext0fnext(neightet, neineitet); + neineitet.ver = 4; + cavebdrylist->newindex((void **) &parytet); + *parytet = neineitet; + enextself(neightet); + } + } + } else { + // This face is not on the cavity boundary anymore. + unmarktest(*cavetet); + } + } + if (updatecount > 0) { + // Update the cavity boundary faces (fig/dump-cavity-case9). + cavebdrylist->restart(); + for (i = 0; i < cavetetlist->objects; i++) { + cavetet = (triface *) fastlookup(cavetetlist, i); + // 'cavetet' was an exterior tet adjacent to the cavity. + assert(cavetet->ver == 4); // SELF_CHECK + symedge(*cavetet, neightet); + if (infected(neightet)) { + // It is a cavity boundary face. + cavebdrylist->newindex((void **) &parytet); + *parytet = *cavetet; + } else { + // Not a cavity boundary face. + unmarktest(*cavetet); + } + } + // Update the list of old tets. + cavetetlist->restart(); + for (i = 0; i < caveoldtetlist->objects; i++) { + cavetet = (triface *) fastlookup(caveoldtetlist, i); + if (infected(*cavetet)) { + cavetetlist->newindex((void **) &parytet); + *parytet = *cavetet; + } + } + assert(cavetetlist->objects < i); + // Swap 'cavetetlist' and 'caveoldtetlist'. + swaplist = caveoldtetlist; + caveoldtetlist = cavetetlist; + cavetetlist = swaplist; + if (b->verbose > 1) { + printf(" Size of the updated cavity: %d faces %d tets.\n", + cavebdrylist->objects, caveoldtetlist->objects); + } + } + } + + // Re-use this list for new cavity faces. + cavetetlist->restart(); + + // Create new tetrahedra in the Bowyer-Watson cavity and Connect them. + for (i = 0; i < cavebdrylist->objects; i++) { + cavetet = (triface *) fastlookup(cavebdrylist, i); + neightet = *cavetet; + unmarktest(neightet); // Unmark it. + // Get the oldtet (inside the cavity). + symedge(neightet, neineitet); + if (apex(neightet) != dummypoint) { + // Create a new tet in the cavity (see Fig. bowyerwatson 1 or 3). + maketetrahedron(&newtet); + setorg(newtet, dest(neightet)); + setdest(newtet, org(neightet)); + setapex(newtet, apex(neightet)); + setoppo(newtet, insertpt); + } else { + // Create a new hull tet (see Fig. bowyerwatson 2). + hullsize++; + maketetrahedron(&newtet); + setorg(newtet, org(neightet)); + setdest(newtet, dest(neightet)); + setapex(newtet, insertpt); + setoppo(newtet, dummypoint); + // Note: the cavity boundary face is at the enext0fnext place. + enext0fnextself(newtet); + } + // Connect newtet <==> neightet, this also disconnect the old bond. + bond(newtet, neightet); + // Let the oldtet knows newtet (for connecting adjacent new tets). + if (org(newtet) != org(neineitet)) esymself(newtet); + neineitet.tet[neineitet.loc] = encode(newtet); + // Replace the old boundary face with the old tet in list. + *cavetet = neineitet; // *cavetet = newtet; + if (checksubsegs) { + newtet.ver &= ~1; // Keep in 0th edge ring. + for (j = 0; j < 3; j++) { + tsspivot(neightet, sseg); + if (sseg.sh != NULL) { + if (sinfected(sseg)) { + // This case is only possible when the cavity has been updated. + assert(updatecount > 0); // SELF_CHECK + suninfect(sseg); // Dequeue a non-missing segment. + } + tssbond1(newtet, sseg); + sstbond(sseg, newtet); + // Do we need to care about encroached segments? + if ((badsegpool != NULL) && !smarktested(sseg)) { + // Queue the subsegment if it is encroached by the new point. + checkedge4encroach(sseg, insertpt, 1); + } + } + enextself(neightet); + enext2self(newtet); + } + } + if (checksubfaces) { + tspivot(neightet, checksh); + if (checksh.sh != NULL) { + tsbond(newtet, checksh); // Also disconnect the old bond. + // Do we need to care about encroached segments? + if ((badsubpool != NULL) && !smarktested(checksh)) { + // Queue the subface if it is encroached by the new point. + // checkface4encroach(checksh, insertpt, 1); + } + } + } + if (updatecount > 0l) { + // Save this face for locally Delaunay test. + cavetetlist->newindex((void **) &parytet); + *parytet = newtet; + } + } + + // Set a handle for speeding point location. + recenttet = newtet; + point2tet(insertpt) = encode(newtet); + + /*// Connect the set of new tetrahedra together. + for (i = 0; i < cavebdrylist->objects; i++) { + cavetet = (triface *) fastlookup(cavebdrylist, i); + cavetet->ver = 0; + for (j = 0; j < 3; j++) { + enext0fnext(*cavetet, newtet); // Go to the face. + // Operate on it if it is open. + if (newtet.tet[newtet.loc] == NULL) { + // Find its adjacent face by rotating faces around the edge of + // cavetet. The rotating direction is opposite to newtet. + // Stop the rotate at a face which is open. + esym(*cavetet, neightet); // Set the rotate dir. + do { + fnextself(neightet); // Go to the face in the adjacent tet. + } while (neightet.tet[neightet.loc] != NULL); + bond(newtet, neightet); // Connect newtet <==> neightet. + } + if (checksubsegs || checksubfaces) { + point2tet(org(*cavetet)) = encode(*cavetet); + } + enextself(*cavetet); + } + }*/ + + // Connect adjacent new tetrahedra together. + for (i = 0; i < cavebdrylist->objects; i++) { + cavetet = (triface *) fastlookup(cavebdrylist, i); + decode(cavetet->tet[cavetet->loc], newtet); + // assert(org(newtet) == org(*cavetet)); // SELF_CHECK + for (j = 0; j < 3; j++) { + enext0fnext(newtet, neightet); // Go to the face. + if (neightet.tet[neightet.loc] == NULL) { + spintet = *cavetet; + while (1) { + enext0fnextself(spintet); + decode(spintet.tet[spintet.loc], neineitet); + if (!infected(neineitet)) break; + symedgeself(spintet); + } + // Find the corresponding edge in neineitet. + pa = dest(newtet); + for (k = 0; k < 3; k++) { + if (org(neineitet) == pa) break; + enextself(neineitet); + } + assert(k < 3); // SELF_CHECK + assert(dest(neineitet) == org(newtet)); // SELF_CHECK + enext0fnextself(neineitet); + bond(neightet, neineitet); + // Queue the internal face if the visflag is set. + // See also fig/dump-cavity-case13. + if (visflag) { + cavetetlist->newindex((void **) &parytet); + *parytet = neightet; + } + } + point2tet(org(newtet)) = encode(newtet); + enextself(newtet); + enextself(*cavetet); + } + } + + // Delete the old cavity tets. + for (i = 0; i < caveoldtetlist->objects; i++) { + cavetet = (triface *) fastlookup(caveoldtetlist, i); + tetrahedrondealloc(cavetet->tet); + } + + // loc_end = clock(); + // tinserttime += ((REAL) (loc_end - loc_start)) / CLOCKS_PER_SEC; + + if (bwflag && (futureflip != NULL)) { + // There may exist degenerate tets. Check and remove them. + while (futureflip != NULL) { + // Dequeue an adjacent tet to the cavity. + fliptets[0] = futureflip->tt; + futureflip = futureflip->nextitem; + + // Skip it if it is dead (by previous flip32s). + if (fliptets[0].tet[4] == NULL) continue; + + // The possible degenerate tet, check it. + symself(fliptets[0]); + // Skip it if its oppo is not 'p'. + if (oppo(fliptets[0]) != insertpt) continue; + // This must be a new tet. + assert(oppo(fliptets[0]) == insertpt); // SELF_CHECK + + pts = (point *) fliptets[0].tet; + ori = orient3d(pts[4], pts[5], pts[6], pts[7]); orient3dcount++; + + if (ori == 0) { + if (b->verbose > 1) { + printf(" Removing tet (%d, %d, %d, %d).\n", pointmark(pts[4]), + pointmark(pts[5]), pointmark(pts[6]), pointmark(pts[7])); + } + // Find the hull edge in cavetet. + fliptets[0].ver = 0; + for (j = 0; j < 3; j++) { + enext0fnext(fliptets[0], neightet); + symself(neightet); + if ((point) neightet.tet[7] == dummypoint) break; + enextself(fliptets[0]); + } + // Because of existing multiple degenerate cases. It is possible + // that the other hull face is not pop yet. + if (j < 3) { + // Collect tets for flipping the edge. + for (j = 0; j < 3; j++) { + fnext(fliptets[j], fliptets[j + 1]); + } + if (fliptets[3].tet != fliptets[0].tet) { + printf("Internal error in insertvertex(): Unknown flip case.\n"); + terminatetetgen(1); + } + // Do a 3-to-2 flip to remove the degenerate tet. + flip32(fliptets, 1, 0); + // Rememebr the new tet. + recenttet = fliptets[0]; + } else { + // Put the face back into queue. + symself(fliptets[0]); + newflip = (badface *) flippool->alloc(); + newflip->tt = fliptets[0]; // the adjacent tet (not in cavity). + newflip->nextitem = NULL; + if (futureflip == NULL) { + lastflip = futureflip = newflip; + } else { + lastflip->nextitem = newflip; + lastflip = newflip; + } + } // if (j < 3) + } // if (ori == 0) + } + flippool->restart(); + } + + if (bwflag && visflag) { + // Some new faces may be locally non-Delaunay. Check and fix them. + for (i = 0; i < cavetetlist->objects; i++) { + // Get a new face (whose opposite is p). + parytet = (triface *) fastlookup(cavetetlist, i); + if ((point) parytet->tet[7] == dummypoint) continue; // A hull face. + pa = oppo(*parytet); + futureflip = flippush(futureflip, parytet, pa); + } + // Recover Delaunay faces. + // Set 'flipflag' = 2, s.t. all non-flippable faces are not ignoring, + // they are queued in the flip queue for the future flips. One key + // is that we also flip non-Delaunay segments and subfaces. This way + // we are able to recover all non-Delaunay edges/faces (no proof yet), + // i.e., the flip will terminate. + // The flipped segments and subfaces are queued in 'subsegstack' and + // 'subfacstack' for recovery. + lawsonflip3d(2); + } + + // Set the point type. + if (getpointtype(insertpt) == UNUSEDVERTEX) { + setpointtype(insertpt, VOLVERTEX); + } + + cavetetlist->restart(); + cavebdrylist->restart(); + caveoldtetlist->restart(); + return loc; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// flipinsertvertex() Insert a vertex (p) into tetrahedralization (T). // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::flipinsertvertex(point insertpt, triface* searchtet, + int flipflag) +{ + enum location loc; + long tetcount; + + if (b->verbose > 1) { + printf(" Insert point %d\n", pointmark(insertpt)); + } + + tetcount = ptloc_count; + + if (searchtet->tet == NULL) { + randomsample(insertpt, searchtet); + } + loc = locate(insertpt, searchtet); + + if (b->verbose > 1) { + printf(" Walk distance (# tets): %ld\n", ptloc_count - tetcount); + } + + if (ptloc_max_count < (ptloc_count - tetcount)) { + ptloc_max_count = (ptloc_count - tetcount); + } + + if (b->verbose > 1) { + printf(" Located (%d) tet (%d, %d, %d, %d).\n", (int) loc, + pointmark(org(*searchtet)), pointmark(dest(*searchtet)), + pointmark(apex(*searchtet)), pointmark(oppo(*searchtet))); + } + + if (loc == ONVERTEX) { + // The point already exists. Mark it and do nothing on it. + // In a STL mesh, duplicated points are implicitly included. + if (b->object != tetgenbehavior::STL) { + if (!b->quiet) { + printf("Warning: Point #%d is duplicated with Point #%d. Ignored!\n", + pointmark(insertpt), pointmark(org(*searchtet))); + } + } + point2ppt(insertpt) = org(*searchtet); + setpointtype(insertpt, DUPLICATEDVERTEX); + dupverts++; + return; + } + + // Clear flip stack. + futureflip = (badface *) NULL; + + // Insert the new point by flipping. + if (loc == ONFACE) { + flip26(insertpt, searchtet, flipflag); + } else if (loc == ONEDGE) { + flipn2n(insertpt, searchtet, flipflag); + } else { // (loc == INTET) || (loc == OUTSIDE) + flip14(insertpt, searchtet, flipflag); + } + + recenttet = *searchtet; // Remember a handle. + + // Set the point type. + if (getpointtype(insertpt) == UNUSEDVERTEX) { + setpointtype(insertpt, VOLVERTEX); + } + + // If flipflag > 0, do Delaunay flip. + if (flipflag > 0) { + lawsonflip3d(flipflag); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// incrementaldelaunay() Form a Delaunay tetrahedralization by increment- // +// ally inserting vertices. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::incrementaldelaunay() +{ + triface searchtet; + point *permutarray, swapvertex; + REAL v1[3], v2[3], n[3]; + REAL bboxsize, bboxsize2, bboxsize3, ori; + int randindex, i, j; + + if (!b->quiet) { + printf("Delaunizing vertices.\n"); + } + + // Form a random permuation (uniformly at random) of the set of vertices. + permutarray = new point[in->numberofpoints]; + pointpool->traversalinit(); + if (b->order == 3) { // '-o3' option (for debug) + for (i = 0; i < in->numberofpoints; i++) { + permutarray[i] = (point) pointpool->traverse(); + } + } else { + for (i = 0; i < in->numberofpoints; i++) { + randindex = randomnation(i + 1); + permutarray[i] = permutarray[randindex]; + permutarray[randindex] = (point) pointpool->traverse(); + } + } + + // Calculate the diagonal size of its bounding box. + bboxsize = sqrt(NORM2(xmax - xmin, ymax - ymin, zmax - zmin)); + bboxsize2 = bboxsize * bboxsize; + bboxsize3 = bboxsize2 * bboxsize; + + // Make sure the second vertex is not identical with the first one. + i = 1; + while ((DIST(permutarray[0], permutarray[i]) / bboxsize) < b->epsilon) { + i++; + if (i == in->numberofpoints - 1) { + printf("Exception: All vertices are (nearly) identical (Tol = %g).\n", + b->epsilon); + terminatetetgen(1); + } + } + if (i > 1) { + // Swap to move the non-indetical vertex from index i to index 1. + swapvertex = permutarray[i]; + permutarray[i] = permutarray[1]; + permutarray[1] = swapvertex; + } + + // Make sure the third vertex is not collinear with the first two. + i = 2; + for (j = 0; j < 3; j++) { + v1[j] = permutarray[1][j] - permutarray[0][j]; + v2[j] = permutarray[i][j] - permutarray[0][j]; + } + CROSS(v1, v2, n); + while ((sqrt(NORM2(n[0], n[1], n[2])) / bboxsize2) < b->epsilon) { + i++; + if (i == in->numberofpoints - 1) { + printf("Exception: All vertices are (nearly) collinear (Tol = %g).\n", + b->epsilon); + terminatetetgen(1); + } + for (j = 0; j < 3; j++) { + v2[j] = permutarray[i][j] - permutarray[0][j]; + } + CROSS(v1, v2, n); + } + if (i > 2) { + // Swap to move the non-indetical vertex from index i to index 1. + swapvertex = permutarray[i]; + permutarray[i] = permutarray[2]; + permutarray[2] = swapvertex; + } + + // Make sure the fourth vertex is not coplanar with the first three. + i = 3; + ori = orient3d(permutarray[0], permutarray[1], permutarray[2], + permutarray[i]); + while ((fabs(ori) / bboxsize3) < b->epsilon) { + i++; + if (i == in->numberofpoints) { + printf("Exception: All vertices are coplanar (Tol = %g).\n", + b->epsilon); + terminatetetgen(1); + } + ori = orient3d(permutarray[0], permutarray[1], permutarray[2], + permutarray[i]); + } + if (i > 3) { + // Swap to move the non-indetical vertex from index i to index 1. + swapvertex = permutarray[i]; + permutarray[i] = permutarray[3]; + permutarray[3] = swapvertex; + } + + // Orient the first four vertices in permutarray so that they follow the + // right-hand rule. + if (ori > 0.0) { + // Swap the first two vertices. + swapvertex = permutarray[0]; + permutarray[0] = permutarray[1]; + permutarray[1] = swapvertex; + } + + // Create the initial Delaunay tetrahedralization. + initialDT(permutarray[0], permutarray[1], permutarray[2], permutarray[3]); + + if (b->verbose) { + printf(" Incremental inserting vertices.\n"); + } + + if (b->bowyerwatson) { + // Use incremental Bowyer-Watson algorithm. + for (i = 4; i < in->numberofpoints; i++) { + if (b->verbose > 1) printf(" #%d", i); + searchtet.tet = NULL; // Randomly sample tetrahedra. + insertvertex(permutarray[i], &searchtet, true, false, false, false); + } + } else { + // Use incremental flip algorithm. + for (i = 4; i < in->numberofpoints; i++) { + if (b->verbose > 1) printf(" #%d", i); + searchtet.tet = NULL; // Randomly sample tetrahedra. + flipinsertvertex(permutarray[i], &searchtet, 1); + } + } + + delete [] permutarray; +} + +#endif // #ifndef delaunayCXX \ No newline at end of file diff --git a/contrib/TetgenNew/flip.cxx b/contrib/TetgenNew/flip.cxx new file mode 100644 index 0000000000..fbaf010acf --- /dev/null +++ b/contrib/TetgenNew/flip.cxx @@ -0,0 +1,2128 @@ +#ifndef flipCXX +#define flipCXX + +#include "tetgen.h" + +/////////////////////////////////////////////////////////////////////////////// +// // +// flipshpush() Push a subface edge into flip stack. // +// // +/////////////////////////////////////////////////////////////////////////////// + +tetgenmesh::badface* tetgenmesh::flipshpush(badface* flipstack, face* flipedge) +{ + badface *newflipface; + + newflipface = (badface *) flippool->alloc(); + newflipface->ss = *flipedge; + newflipface->forg = sorg(*flipedge); + newflipface->fdest = sdest(*flipedge); + newflipface->nextitem = flipstack; + + return newflipface; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// flip13() Insert a vertex by transforming 1-to-3 subfaces. // +// // +// 'newpt' (p) lies in the interior of 'splitface' (abc). This routine del- // +// ete abc and replaces it by three subfaces: abp, bcp, and cap, respective- // +// ly. Return abp in 'splitface'. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::flip13(point newpt, face* splitface, int flipflag) +{ + face newfaces[3], bdedge, casout, casin; + face checkface, checkseg; + point pa, pb, pc; + int i; + + REAL area; + int shmark; + + splitface->shver &= ~1; // Stay in the 0th edge ring. + + pa = sorg(*splitface); + pb = sdest(*splitface); + pc = sapex(*splitface); + + if (b->verbose > 1) { + printf(" flip 1-to-3: %d, (%d, %d, %d)\n", pointmark(newpt), + pointmark(pa), pointmark(pb), pointmark(pc)); + } + flip13count++; + + // We need two new subfaces. + newfaces[0] = *splitface; + makeshellface(subfacepool, &(newfaces[1])); + makeshellface(subfacepool, &(newfaces[2])); + + // Insert the new point p. + setsapex(newfaces[0], newpt); // abc->abp. + setshvertices(newfaces[1], pb, pc, newpt); // bcp. + setshvertices(newfaces[2], pc, pa, newpt); // cap. + + shmark = getshellmark(newfaces[0]); + setshellmark(newfaces[1], shmark); + setshellmark(newfaces[2], shmark); + if (checkconstraints) { + area = areabound(newfaces[0]); + areabound(newfaces[1]) = area; + areabound(newfaces[2]) = area; + } + + // Connect outer boundary faces to new faces. + senext(newfaces[0], bdedge); + for (i = 1; i < 3; i++) { + spivot(bdedge, casout); + sspivot(bdedge, checkseg); + if (casout.sh != NULL) { + casin = casout; + if (checkseg.sh != NULL) { + spivot(casin, checkface); + while (checkface.sh != bdedge.sh) { + casin = checkface; + spivot(casin, checkface); + } + } + sbond1(newfaces[i], casout); + sbond1(casin, newfaces[i]); + } + if (checkseg.sh != NULL) { + ssdissolve(bdedge); + ssbond(newfaces[i], checkseg); + } + senextself(bdedge); + } + + // Connect the three subfaces together. Reuse casout, casin. + for (i = 0; i < 3; i++) { + senext(newfaces[i], casout); + senext2(newfaces[(i + 1) % 3], casin); + sbond2(casout, casin); + } + + if (flipflag) { + // Put the boundary edges into flip stack. + for (i = 0; i < 3; i++) { + futureflip = flipshpush(futureflip, &(newfaces[i])); + } + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// flipn2nf() Insert a vertex by transforming n-to-2n subfaces. // +// // +// The 'newpt'(p) lies on the edge (ab) of 'splitedge'(abc). Let the n faces // +// containing ab be: abp[0], ..., abp[n-1], use an array 'flipfaces': flip- // +// faces[0], ..., flipfaces[n-1], to store them. This routine removes the n // +// subfaces and replaces them with 2n new subfaces: app[0], ..., app[n-1] ( // +// (at top), pbp[0], ..., pbp[n-1] (at bottom), respectively. On return, // +// 'splitedge' is apc. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::flipn2nf(point newpt, face* splitedges, int flipflag) +{ + face *abdedges, *bbdedges, *boutfaces, *binfaces; + face aseg, bseg, aoutseg, boutseg; + face checkface, checkseg; + point pa, pb, *pt; + int n, i; + + shellface sptr; + + splitedges[0].shver &= ~1; // Stay in the 0th edge ring. + + // Count the number of faces at ab. + n = 0; + checkface = splitedges[0]; + do { + spivotself(checkface); + n++; + } while ((checkface.sh != NULL) && (checkface.sh != splitedges[0].sh)); + + // Allocate spaces. + abdedges = new face[n]; + bbdedges = new face[n]; + boutfaces = new face[n]; + binfaces = new face[n]; + pt = new point[n]; + + // Collect the faces, abp[0], ..., abp[n-1]. + abdedges[0] = splitedges[0]; + pt[0] = sapex(abdedges[0]); + for (i = 0; i < n - 1; i++) { + spivot(abdedges[i], abdedges[i + 1]); + if (sorg(abdedges[i + 1]) != sorg(splitedges[0])) { + sesymself(abdedges[i + 1]); + } + pt[i + 1] = sapex(abdedges[i + 1]); + } + + pa = sorg(splitedges[0]); + pb = sdest(splitedges[0]); + + if (b->verbose > 1) { + printf(" flip n-to-2n: %d, (%d, %d) %d ... (%d faces) \n", + pointmark(newpt), pointmark(pa), pointmark(pb), pointmark(pt[0]), n); + } + flipn2nfcount++; + + // Get the old boundary edges: p[i]a, bp[i], i = 0, ..., n-1 + for (i = 0; i < n; i++) { + senext(abdedges[i], bbdedges[i]); + senext2self(abdedges[i]); + } + + // Collect outer boundary faces at bp[i]. + for (i = 0; i < n; i++) { + spivot(bbdedges[i], boutfaces[i]); + binfaces[i] = boutfaces[i]; + if (boutfaces[i].sh != NULL) { + sspivot(bbdedges[i], checkseg); + if (checkseg.sh != NULL) { + spivot(binfaces[i], checkface); + while (checkface.sh != bbdedges[i].sh) { + binfaces[i] = checkface; + spivot(binfaces[i], checkface); + } + } + } + } + + // Insert the new point p. + for (i = 0; i < n; i++) { + setsapex(abdedges[i], newpt); // ap[i]b->ap[i]p. + makeshellface(subfacepool, &(bbdedges[i])); + setshvertices(bbdedges[i], pb, pt[i], newpt); // bp[i]p. + setshellmark(bbdedges[i], getshellmark(abdedges[i])); + if (checkconstraints) { + areabound(bbdedges[i]) = areabound(abdedges[i]); + } + } + + // Connect new boundary edges to outer faces. + for (i = 0; i < n; i++) { + if (boutfaces[i].sh != NULL) { + sbond1(bbdedges[i], boutfaces[i]); + sbond1(binfaces[i], bbdedges[i]); + } + senext2(abdedges[i], checkface); + sspivot(checkface, checkseg); // Check subsegment. + if (checkseg.sh != NULL) { + ssdissolve(checkface); // Clear the old bond. + ssbond(bbdedges[i], checkseg); + } + } + + // Connect new subfaces to updated subfaces. (Reuse boutfaces, binfaces). + for (i = 0; i < n; i++) { + senext2(abdedges[i], boutfaces[i]); + senext(bbdedges[i], binfaces[i]); + sbond2(boutfaces[i], binfaces[i]); + } + + // Create new subfaces ring at edge pb. + if (n > 1) { + for (i = 0; i < n; i++) { + senext2(bbdedges[i], boutfaces[i]); + senext2(bbdedges[(i + 1) % n], boutfaces[(i + 1) % n]); + sbond1(boutfaces[i], boutfaces[(i + 1) % n]); + } + } + + // Check edge ab to see if it is a subsegment. + sspivot(splitedges[0], aseg); + if (aseg.sh != NULL) { + // Split a subsegment ab to ap and pb. + // if (sorg(aseg) != pa) sesymself(aseg); + aseg.shver = 0; + if (b->verbose > 1) { + printf(" flip 1-to-2: %d, (%d, %d).\n", pointmark(newpt), + pointmark(sorg(aseg)), pointmark(sdest(aseg))); + } + // Insert the new point p. + makeshellface(subsegpool, &bseg); + setshvertices(bseg, newpt, sdest(aseg), NULL); + setsdest(aseg, newpt); + setshellmark(bseg, getshellmark(aseg)); + if (checkconstraints) { + areabound(bseg) = areabound(aseg); + } + // Connect pb<->b#. + senext(aseg, aoutseg); + spivotself(aoutseg); + if (aoutseg.sh != NULL) { + senext(bseg, boutseg); + sbond2(boutseg, aoutseg); + } + // Connect ap <-> pb. + senext(aseg, aoutseg); + senext2(bseg, boutseg); + sbond2(aoutseg, boutseg); + // Connect seg ap to face ring of splitshs[0], this will disconnect the + // old bonds as well. *** Because we mixed the edge versions *** + if (sorg(aseg) == sorg(splitedges[0])) { + for (i = 0; i < n; i++) { + senext(abdedges[i], boutfaces[i]); + ssbond(boutfaces[i], aseg); + } + // Connect seg pb to subfaces having it. + for (i = 0; i < n; i++) { + senext2(bbdedges[i], boutfaces[i]); + ssbond(boutfaces[i], bseg); + } + } else { + // The edge version is reversed. + assert(sdest(bseg) == sorg(splitedges[0])); + for (i = 0; i < n; i++) { + senext(abdedges[i], boutfaces[i]); + ssbond(boutfaces[i], bseg); + } + for (i = 0; i < n; i++) { + senext2(bbdedges[i], boutfaces[i]); + ssbond(boutfaces[i], aseg); + } + } + } + + if (flipflag) { + // Put the boundary edges into flip stack. + for (i = 0; i < n; i++) { + futureflip = flipshpush(futureflip, &abdedges[i]); + futureflip = flipshpush(futureflip, &bbdedges[i]); + } + } + + // Return apc = app[0] = splitedges[0]. + senext2(bbdedges[0], splitedges[1]); // return pbp[0]. + + delete [] abdedges; + delete [] bbdedges; + delete [] boutfaces; + delete [] binfaces; + delete [] pt; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// flip22() Remove an edge by transforming 2-to-2 subfaces. // +// // +// 'flipfaces' contains two faces: abc and bad. This routine removes these 2 // +// faces and replaces them by two new faces: cdb and dca. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::flip22(face* flipfaces, int flipflag) +{ + face bdedges[4], outfaces[4], infaces[4], bdsegs[4]; + face checkface, checkseg; + point pa, pb, pc, pd; + int i; + + // Orient the two faces properly: abc and bad. + if (sorg(flipfaces[0]) == sorg(flipfaces[1])) { + sesymself(flipfaces[1]); + } + + pa = sorg(flipfaces[0]); + pb = sdest(flipfaces[0]); + pc = sapex(flipfaces[0]); + pd = sapex(flipfaces[1]); + + if (b->verbose > 1) { + printf(" flip 2-to-2: (%d, %d, %d, %d)\n", pointmark(pa), + pointmark(pb), pointmark(pc), pointmark(pd)); + } + flip22count++; + + // Collect the four boundary edges. + senext(flipfaces[0], bdedges[0]); + senext2(flipfaces[0], bdedges[1]); + senext(flipfaces[1], bdedges[2]); + senext2(flipfaces[1], bdedges[3]); + + // Collect outer boundary faces. + for (i = 0; i < 4; i++) { + spivot(bdedges[i], outfaces[i]); + infaces[i] = outfaces[i]; + sspivot(bdedges[i], bdsegs[i]); + if (outfaces[i].sh != NULL) { + sspivot(bdedges[i], checkseg); + if (checkseg.sh != NULL) { + spivot(infaces[i], checkface); + while (checkface.sh != bdedges[i].sh) { + infaces[i] = checkface; + spivot(infaces[i], checkface); + } + } + } + } + + // Transform abc -> cdb. + setshvertices(flipfaces[0], pc, pd, pb); + // Transform bad -> dca. + setshvertices(flipfaces[1], pd, pc, pa); + + // Reconnect boundary edges to outer boundary faces. + for (i = 0; i < 4; i++) { + if (outfaces[(3 + i) % 4].sh != NULL) { + sbond1(bdedges[i], outfaces[(3 + i) % 4]); + sbond1(infaces[(3 + i) % 4], bdedges[i]); + } else { + sdissolve(bdedges[i]); + } + if (bdsegs[(3 + i) % 4].sh != NULL) { + ssbond(bdedges[i], bdsegs[(3 + i) % 4]); + } else { + ssdissolve(bdedges[i]); + } + } + + recentsh = flipfaces[0]; + + if (flipflag) { + // Put the boundary edges into flip stack. + for (i = 0; i < 4; i++) { + futureflip = flipshpush(futureflip, &bdedges[i]); + } + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// lawsonflip() Flip non-locally Delaunay edges. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::lawsonflip() +{ + face flipfaces[2]; + face checkseg; + point pa, pb, pc, pd; + REAL sign; + long flipcount; + + if (b->verbose > 1) { + printf(" Lawson flip %ld edges.\n", flippool->items); + } + flipcount = flip22count; + + while (futureflip != (badface *) NULL) { + // Pop an edge from the stack. + flipfaces[0] = futureflip->ss; + pa = futureflip->forg; + pb = futureflip->fdest; + futureflip = futureflip->nextitem; + + // Skip it if it is dead. + if (flipfaces[0].sh[3] == NULL) continue; + // Skip it if it is not the same edge as we saved. + if ((sorg(flipfaces[0]) != pa) || (sdest(flipfaces[0]) != pb)) continue; + // Skip it if it is a subsegment. + sspivot(flipfaces[0], checkseg); + if (checkseg.sh != NULL) continue; + + // Get the adjacent face. + spivot(flipfaces[0], flipfaces[1]); + if (flipfaces[1].sh == NULL) continue; // Skip a hull edge. + pc = sapex(flipfaces[0]); + pd = sapex(flipfaces[1]); + + sign = incircle3d(pa, pb, pc, pd); + + if (sign < 0) { + // It is non-locally Delaunay. Flip it. + flip22(flipfaces, 1); + } + } + + if (b->verbose > 1) { + printf(" %ld edges stacked, %ld flips.\n", flippool->items, + flip22count - flipcount); + } + + flippool->restart(); +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// flippush() Push a face (possibly will be flipped) into stack. // +// // +/////////////////////////////////////////////////////////////////////////////// + +tetgenmesh::badface* tetgenmesh::flippush(badface* flipstack, + triface* flipface, point pushpt) +{ + badface *newflipface; + + newflipface = (badface *) flippool->alloc(); + newflipface->tt = *flipface; + newflipface->foppo = pushpt; + newflipface->nextitem = flipstack; + + return newflipface; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// flip14() Insert a vertex by transforming 1-to-4 tetrahedra. // +// // +// 'newpt' (p) lies in the interior of 'splittet' (abcd). This routine abcd // +// abcd and replaces it by 4 new tets: abpd, bcpd, capd, and abcp, resepcti- // +// vely. Return abcp in 'splittet'. // +// // +// If abcd is a hull tet, we adjust it and let d be the dummypoint. In this // +// case, the three new tets: abpd, bcpd, and capd are hull tets. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::flip14(point newpt, triface* splittet, int flipflag) +{ + triface fliptets[3], castets[3]; + triface newface, casface; + face checksh, checkseg; + point pa, pb, pc, pd; + int i; + + int *iptr; + + // Check if the original tet is a hull tet. + if ((point) splittet->tet[7] == dummypoint) { + splittet->loc = 0; + } + splittet->ver = 0; + + pa = org(*splittet); + pb = dest(*splittet); + pc = apex(*splittet); + pd = oppo(*splittet); + + if (b->verbose > 1) { + printf(" flip 1-to-4: %d (%d, %d, %d, %d)\n", pointmark(newpt), + pointmark(pa), pointmark(pb), pointmark(pc), pointmark(pd)); + } + flip14count++; + + // Get the outer boundary faces. + for(i = 0; i < 3; i++) { + fnext(*splittet, castets[i]); + enextself(*splittet); + } + + if (checksubsegs) { + // Dealloc the space to subsegments. + if (splittet->tet[8] != NULL) { + tet2segpool->dealloc((shellface *) splittet->tet[8]); + } + splittet->tet[8] = NULL; + } + if (checksubfaces) { + // Dealloc the space to subfaces. + if (splittet->tet[9] != NULL) { + tet2subpool->dealloc((shellface *) splittet->tet[9]); + } + splittet->tet[9] = NULL; + } + + // Update the number of hull tets. + if (pd == dummypoint) { + // We remove one old hull tet (abcp), but add three new hull tets. + hullsize += 2; + } + + // Create three new tets for abpd, bcpd, and capd. + maketetrahedron(&(fliptets[0])); + maketetrahedron(&(fliptets[1])); + maketetrahedron(&(fliptets[2])); + // Set the vertices. + setvertices(fliptets[0], pa, pb, newpt, pd); + setvertices(fliptets[1], pb, pc, newpt, pd); + setvertices(fliptets[2], pc, pa, newpt, pd); + // Update the old tet to abcp. + setoppo(*splittet, newpt); + + // Bond the new tets to outer boundary tets. + for (i = 0; i < 3; i++) { + enext0fnext(fliptets[i], newface); + bond(newface, castets[i]); + } + // Bond the new tets together (at six interior faces). + for (i = 0; i < 3; i++) { + enext0fnext(*splittet, newface); + bond(newface, fliptets[i]); + enextself(*splittet); + } + for (i = 0; i < 3; i++) { + enextfnext(fliptets[i], newface); + enext2fnext(fliptets[(i + 1) % 3], casface); + bond(newface, casface); + } + + if (checksubsegs) { + // Bond segments to new tets. Each new tet has three edges to be + // checked, e.g., abpd has [a,b], [b,d], and [d,a]. The base tet + // abcd has three edges [a,b], [b,c] and [c,a], total 12 edges. + for (i = 0; i < 3; i++) { + enext0fnext(fliptets[i], newface); // [a,b], [b,c], [c,a]. + tsspivot(castets[i], checkseg); + if (checkseg.sh != NULL) { + tssbond1(*splittet, checkseg); + tssbond1(newface, checkseg); + // Let the segment remember an adjacent tet. + sstbond(checkseg, *splittet); + } + enextself(newface); // [b,d], [c,d], [a,d] + enext(castets[i], casface); + tsspivot(casface, checkseg); + if (checkseg.sh != NULL) { + tssbond1(newface, checkseg); + sstbond(checkseg, newface); + } + enextself(newface); // [d,a], [d,b], [d,c] + enext2(castets[i], casface); + tsspivot(casface, checkseg); + if (checkseg.sh != NULL) { + tssbond1(newface, checkseg); + sstbond(checkseg, newface); + } + enextself(*splittet); + } + } + + if (checksubfaces) { + // Bond subfaces to new tets. + sym(*splittet, casface); + tspivot(casface, checksh); + if (checksh.sh != NULL) { + tsbond(*splittet, checksh); + } + for (i = 0; i < 3; i++) { + enext0fnext(fliptets[i], newface); + tspivot(castets[i], checksh); + if (checksh.sh != NULL) { + tsbond(newface, checksh); + } + } + } + + // Update the point-to-tet map. + point2tet(newpt) = encode(*splittet); + // The values in pa, pb, and pc are not changed. + point2tet(pd) = encode(fliptets[0]); + + if (flipflag > 0) { + // Queue faces which may be locally non-Delaunay. + for (i = 0; i < 3; i++) { + enext0fnext(fliptets[i], newface); + futureflip = flippush(futureflip, &newface, newpt); + } + futureflip = flippush(futureflip, splittet, newpt); + } + + // Return abcp in 'splittet'. +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// flip26() Insert a vertex by transforming 2-to-6 tetrahedra. // +// // +// The 'newpt' (p) lies in the face 'splitface' (abc). This routine removes // +// the two tets sharing at abc: abcd and bace, replaces them with 6 new tets // +// : abpd, bcpd, capd (at top), bape, cbpe, and acpe (at bottom). On return, // +// 'splitface' is abpd. // +// // +// On input, a, b, or c should not be dummypoint. If d is dummypoint, the 3 // +// top new tets are hull tets. If e is dummypoint, we reconfigure e to d, // +// i.e., turn the two tets up-side down. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::flip26(point newpt, triface* splitface, int flipflag) +{ + triface fliptets[4], castets[4]; + triface symface, newface, casface; + face checksh, checkseg; + point pa, pb, pc, pd, pe; + int i, j; + + int *iptr; + + splitface->ver &= ~1; + symedge(*splitface, symface); + + if (oppo(symface) == dummypoint) { + // Swap the two old tets. + newface = *splitface; + *splitface = symface; + symface = newface; + } + + pa = org(*splitface); + pb = dest(*splitface); + pc = apex(*splitface); + pd = oppo(*splitface); + pe = oppo(symface); + + if (b->verbose > 1) { + printf(" flip 2-to-6: (%d, %d, %d, %d, %d)\n", pointmark(pa), + pointmark(pb), pointmark(pc), pointmark(pd), pointmark(pe)); + } + flip26count++; + + // Get the outer boundary faces. + enext(*splitface, fliptets[0]); + enext2(*splitface, fliptets[1]); + enext2(symface, fliptets[2]); + enext(symface, fliptets[3]); + + for (i = 0; i < 4; i++) { + fnext(fliptets[i], castets[i]); + } + + if (checksubsegs) { + // Dealloc the space to subsegments. + if (splitface->tet[8] != NULL) { + tet2segpool->dealloc((shellface *) splitface->tet[8]); + } + splitface->tet[8] = NULL; + if (symface.tet[8] != NULL) { + tet2segpool->dealloc((shellface *) symface.tet[8]); + } + symface.tet[8] = NULL; + } + if (checksubfaces) { + // Dealloc the space to subfaces. + if (splitface->tet[9] != NULL) { + tet2subpool->dealloc((shellface *) splitface->tet[9]); + } + splitface->tet[9] = NULL; + if (symface.tet[9] != NULL) { + tet2subpool->dealloc((shellface *) symface.tet[9]); + } + symface.tet[9] = NULL; + } + + // Update the number of hull tets. + if (pd == dummypoint) { + // We remove one old hull tet (abcp), but add three new hull tets. + hullsize += 2; + } + + // Create new tets. + maketetrahedron(&(fliptets[0])); // bcpd + maketetrahedron(&(fliptets[1])); // capd + maketetrahedron(&(fliptets[2])); // cbpe + maketetrahedron(&(fliptets[3])); // acpe + + // Set new vertices. + setapex(*splitface, newpt); + setvertices(fliptets[0], pb, pc, newpt, pd); + setvertices(fliptets[1], pc, pa, newpt, pd); + setapex(symface, newpt); + setvertices(fliptets[2], pc, pb, newpt, pe); + setvertices(fliptets[3], pa, pc, newpt, pe); + + // Bond the new tets to outer boundary faces. + for (i = 0; i < 4; i++) { + enext0fnext(fliptets[i], newface); + bond(newface, castets[i]); + } + + // Bond the top and bottom new tets together. + bond(fliptets[0], fliptets[2]); + bond(fliptets[1], fliptets[3]); + + // Bond the new tets together. + enextfnext(*splitface, newface); + enext2fnext(fliptets[0], casface); + bond(newface, casface); + enextfnext(fliptets[0], newface); + enext2fnext(fliptets[1], casface); + bond(newface, casface); + enextfnext(fliptets[1], newface); + enext2fnext(*splitface, casface); + bond(newface, casface); + + enext2fnext(symface, newface); + enextfnext(fliptets[2], casface); + bond(newface, casface); + enext2fnext(fliptets[2], newface); + enextfnext(fliptets[3], casface); + bond(newface, casface); + enext2fnext(fliptets[3], newface); + enextfnext(symface, casface); + bond(newface, casface); + + if (checksubsegs) { + // Bond segments. For each tet there are three edges to be checked, + // total there are 18 edges. + for (i = 0; i < 3; i++) { + if (i < 2) { + enext0fnext(fliptets[i], newface); + casface = castets[i]; + } else { + enext0fnext(*splitface, newface); + symedge(newface, casface); + } + for (j = 0; j < 3; j++) { + tsspivot(casface, checkseg); + if (checkseg.sh != NULL) { + tssbond1(newface, checkseg); + sstbond(checkseg, newface); + } + enextself(casface); + enextself(newface); + } + } + for (i = 0; i < 3; i++) { + if (i < 2) { + enext0fnext(fliptets[i + 2], newface); + casface = castets[i + 2]; + } else { + enext0fnext(symface, newface); + symedge(newface, casface); + } + for (j = 0; j < 3; j++) { + tsspivot(casface, checkseg); + if (checkseg.sh != NULL) { + tssbond1(newface, checkseg); + sstbond(checkseg, newface); + } + enextself(casface); + enextself(newface); + } + } + } + + if (checksubfaces) { + // Bond subfaces. There are total 6 faces to be checked. + for (i = 0; i < 3; i++) { + if (i < 2) { + enext0fnext(fliptets[i], newface); + casface = castets[i]; + } else { + enext0fnext(*splitface, newface); + symedge(newface, casface); + } + tspivot(casface, checksh); + if (checksh.sh != NULL) { + tsbond(newface, checksh); + } + } + for (i = 0; i < 3; i++) { + if (i < 2) { + enext0fnext(fliptets[i + 2], newface); + casface = castets[i + 2]; + } else { + enext0fnext(symface, newface); + symedge(newface, casface); + } + tspivot(casface, checksh); + if (checksh.sh != NULL) { + tsbond(newface, checksh); + } + } + } + + // Update point-to-tet map. + point2tet(newpt) = encode(*splitface); + // The values in pa and pb are not changed. + point2tet(pc) = encode(fliptets[0]); + point2tet(pd) = encode(fliptets[0]); + point2tet(pe) = encode(fliptets[2]); + + if (flipflag > 0) { + enext0fnext(*splitface, newface); + futureflip = flippush(futureflip, &newface, newpt); + enext0fnext(symface, newface); + futureflip = flippush(futureflip, &newface, newpt); + for (i = 0; i < 4; i++) { + enext0fnext(fliptets[i], newface); + futureflip = flippush(futureflip, &newface, newpt); + } + } + + // Return abpd in 'splitface'. +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// flipn2n() Insert a vertex by transforming n-to-2n tetrahedra. // +// // +// The 'newpt'(p) lies on the edge (ab) of 'splitedge'(abcd). Let the n tets // +// containing ab be: abp[0]p[1], ..., abp[n-1]p[0], use an array 'fliptets': // +// 'fliptets[0]', ..., 'fliptets[n-1]', to store them. This routine removes // +// the n tets and replaces them with 2n new tets: app[0]p[1], ..., app[n-1]- // +// p[0] (at top), pbp[0]p[1], ..., pbp[n-1]p[0] (at bottom) in 'fliptets[0]',// +// ..., 'fliptets[2n-1]', respectively. On return, 'splitedge' is apcd. // +// // +// On input, a and b should not be dummypoint. If p[0] is dummypoint, the 2 // +// new tets connecting to p[0] are hull tets. If p[i], i != 0 is dummypoint, // +// we reconfigure p[i] to p[0], i.e., up-shift the tets i times. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::flipn2n(point newpt, triface* splitedge, int flipflag) +{ + triface *fliptets, *bfliptets, *castets; + triface newface, casface; + face checksh, checkseg; + point pa, pb, *pt; + int dummyflag; // 0 or 1. + int n, i, j; + + int *iptr; + + n = 0; + dummyflag = 0; + + // First count the number n of tets having ab. + splitedge->ver &= ~1; + casface = *splitedge; + do { + n++; + if (apex(casface) == dummypoint) { + // Remeber this face (it's apex is dummypoint). + newface = casface; + dummyflag = n; + } + fnextself(casface); + } while (casface.tet != splitedge->tet); + + // Allocate spaces for fliptets. + fliptets = new triface[n]; + bfliptets = new triface[n]; + castets = new triface[n]; + pt = new point[n]; + + // Get the n old tets. + fliptets[0] = (dummyflag == 0 ? *splitedge : newface); + for (i = 0; i < n - 1; i++) { + fnext(fliptets[i], fliptets[i + 1]); + } + // Get the n apexes. + for (i = 0; i < n; i++) { + pt[i] = apex(fliptets[i]); + } + pa = org(fliptets[0]); + pb = dest(fliptets[0]); + + if (b->verbose > 1) { + printf(" flip n-to-2n: (%d, %d) %d %d ..., (n = %d)\n", pointmark(pa), + pointmark(pb), pointmark(pt[0]), pointmark(pt[1]), n); + } + flipn2ncount++; + + // Get the outer boundary faces. + for (i = 0; i < n; i++) { + enext(fliptets[i], casface); // at edge bp[i]. + fnext(casface, castets[i]); + } + + if (checksubsegs) { + for (i = 0; i < n; i++) { + if (fliptets[i].tet[8] != NULL) { + tet2segpool->dealloc((shellface *) fliptets[i].tet[8]); + } + fliptets[i].tet[8] = NULL; + } + } + if (checksubfaces) { + for (i = 0; i < n; i++) { + if (fliptets[i].tet[9] != NULL) { + tet2segpool->dealloc((shellface *) fliptets[i].tet[9]); + } + fliptets[i].tet[9] = NULL; + } + } + + // Update the number of hull tets. + if (pt[0] == dummypoint) { + // We remove 2 old hull tet (abcp), but add 4 new hull tets. + hullsize += 2; + } + + // Create n new tets. + if (pt[0] != dummypoint) { + setdest(fliptets[0], newpt); // app[0]p[1] + setdest(fliptets[n - 1], newpt); // app[n-1]p[0] + maketetrahedron(&(bfliptets[0])); // pbp[0]p[1] + maketetrahedron(&(bfliptets[n - 1])); // pbp[n-1]p[0] + setvertices(bfliptets[0], newpt, pb, pt[0], pt[1]); + setvertices(bfliptets[n - 1], newpt, pb, pt[n - 1], pt[0]); + } else { + // NOTE: the base face must contain no 'dummypoint'. + setdest(fliptets[0], newpt); // app[0]p[1] + setdest(fliptets[n - 1], newpt); // app[n-1]p[0] + maketetrahedron(&(bfliptets[0])); // pbp[0]p[1] + maketetrahedron(&(bfliptets[n - 1])); + setvertices(bfliptets[0], pb, newpt, pt[1], pt[0]); + setvertices(bfliptets[n - 1], newpt, pb, pt[n - 1], pt[0]); + // Adjust the face of and bfliptets[0]. + enext0fnextself(bfliptets[0]); + esymself(bfliptets[0]); + } + for (i = 1; i < n - 1; i++) { + setdest(fliptets[i], newpt); // app[i]p[i+1] + maketetrahedron(&(bfliptets[i])); // pbp[i]p[i+1] + setvertices(bfliptets[i], newpt, pb, pt[i], pt[i + 1]); + } + + // Bond new tets to outer boundary faces. + for (i = 0; i < n; i++) { + enextfnext(bfliptets[i], newface); + bond(newface, castets[i]); + } + // Bond new tets together. + for (i = 0; i < n; i++) { + enext0fnext(bfliptets[i], newface); + bond(newface, bfliptets[(i + 1) % n]); + } + // Bond top and bottom new tets togther + for (i = 0; i < n; i++) { + enextfnext(fliptets[i], newface); + enext2fnext(bfliptets[i], casface); + bond(newface, casface); + } + + if (checksubsegs) { + // Bond segments. There are total n x 3 edges. + for (i = 0; i < n; i++) { // Top edges. + enext2fnext(fliptets[i], newface); + symedge(newface, casface); + for(j = 0; j < 3; j++) { + tsspivot(casface, checkseg); + if (checkseg.sh != NULL) { + tssbond1(newface, checkseg); + sstbond(checkseg, newface); + } + enextself(casface); + enextself(newface); + } + } + for (i = 0; i < n; i++) { // Bottom edges. + enextfnext(bfliptets[i], newface); + casface = castets[i]; + for(j = 0; j < 3; j++) { + tsspivot(casface, checkseg); + if (checkseg.sh != NULL) { + tssbond1(newface, checkseg); + sstbond(checkseg, newface); + } + enextself(casface); + enextself(newface); + } + } + } + + if (checksubfaces) { + // Bond subfaces. + for (i = 0; i < n; i++) { // Top faces. + enext2fnext(fliptets[i], newface); + symedge(newface, casface); + tspivot(casface, checksh); + if (checksh.sh != NULL) { + tsbond(newface, checksh); + } + } + for (i = 0; i < n; i++) { // Bottom faces. + enextfnext(bfliptets[i], newface); + casface = castets[i]; + tspivot(casface, checksh); + if (checksh.sh != NULL) { + tsbond(newface, checksh); + } + } + } + + // Update the point-to-tet map. + point2tet(newpt) = encode(fliptets[0]); + // The values in pa, p[0], ..., p[n-1] are not changed. + point2tet(pb) = encode(bfliptets[0]); + + if (flipflag > 0) { + for (i = 0; i < n; i++) { + enext2fnext(fliptets[i], newface); + futureflip = flippush(futureflip, &newface, newpt); + } + for (i = 0; i < n; i++) { + enextfnext(bfliptets[i], newface); + futureflip = flippush(futureflip, &newface, newpt); + } + } + + // If dummyflag !=0, the original tet is shifted by (n - dummyflag + 1). + //*splitedge = (dummyflag == 0 ? fliptets[0] : fliptets[n - dummyflag + 1]); + + delete [] fliptets; + delete [] bfliptets; + delete [] castets; + delete [] pt; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// flip23() Remove a face by tranforming 2-to-3 tetrahedra. // +// // +// 'flipface' (abc) is shared by two tets: abcd and bace. This routine rep- // +// laces them by three new tets: edab, edbc, and edca. As a result, the abc // +// is replaced by the edge de. On return, 'flipface' is edab. // +// // +// If 'hullflag' > 0, one of {a, b, c, d, e} may be 'dummypoint'. There may // +// be hull tets involved in this flip. There are two cases: // +// (1) If d is 'dummypoint', all three new tets are hull tets. If e is // +// 'dummypoint', we reconfigure e to d, i.e., turn it up-side down. // +// (2) If c is 'dummypoint' , two new tets edbc and edca are hull tets. // +// If a or b is 'dummypoint', we reconfigure it to c, i.e., rotate the // +// three old tets counterclockwisely (right-hand rule) until a or b // +// is in c's position (see Fig.). // +// // +// If 'flipflag != 0', the convex hull faces will be checked and flipped if // +// they are locally non-Delaunay. If 'flipflag == 1', we assume that d is a // +// newly inserted vertex, so only the lower part of the convex hull will be // +// checked (this avoids unnecessary insphere() testes). // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::flip23(triface* fliptets, int hullflag, int flipflag) +{ + triface topcastets[3], botcastets[3]; + triface newface, casface; + point pa, pb, pc, pd, pe; + int dummyflag; // range = {-1, 0, 1, 2}. + int i; + + face *pssub, checksh; + face checkseg; + + int *iptr; + + if (hullflag > 0) { + // Check if e is dummypoint. + if (oppo(fliptets[1]) == dummypoint) { + // Swap the two old tets. + newface = fliptets[0]; + fliptets[0] = fliptets[1]; + fliptets[1] = newface; + dummyflag = -1; // e is dummypoint. + } else { + // Check if a or b is dummypoint. + if (org(fliptets[0]) == dummypoint) { + dummyflag = 1; // a is dummypoint. + } else if (dest(fliptets[0]) == dummypoint) { + dummyflag = 2; // b is dummypoint. + } else { + dummyflag = 0; // either c or d is dummypoint. + } + i = dummyflag; + // Rotate i times. + for (; i > 0; i--) { + enextself(fliptets[0]); + enext2self(fliptets[1]); + } + } + } + + pa = org(fliptets[0]); + pb = dest(fliptets[0]); + pc = apex(fliptets[0]); + pd = oppo(fliptets[0]); + pe = oppo(fliptets[1]); + + if (b->verbose > 1) { + printf(" flip 2-to-3: (%d, %d, %d, %d, %d)\n", pointmark(pa), + pointmark(pb), pointmark(pc), pointmark(pd), pointmark(pe)); + } + flip23count++; + + // Get the outer boundary faces. + for (i = 0; i < 3; i++) { + fnext(fliptets[0], topcastets[i]); + enextself(fliptets[0]); + } + for (i = 0; i < 3; i++) { + fnext(fliptets[1], botcastets[i]); + enext2self(fliptets[1]); + } + + if (checksubfaces) { + // Check if the flip face is subfaces. + tspivot(fliptets[0], checksh); + if (checksh.sh != NULL) { + if (b->verbose > 1) { + printf(" Queue a flipped subface (%d, %d, %d).\n", + pointmark(sorg(checksh)), pointmark(sdest(checksh)), + pointmark(sapex(checksh))); + } + stdissolve(checksh); // Disconnect the sub-tet bond. + // Add the missing subface into list. + subfacstack->newindex((void **) &pssub); + *pssub = checksh; + } + } + + // Re-use fliptets[0] and fliptets[1]. + fliptets[0].loc = fliptets[0].ver = 0; + fliptets[1].loc = fliptets[1].ver = 0; + elemmarker(fliptets[0].tet) = 0; + elemmarker(fliptets[1].tet) = 0; + if (checksubsegs) { + // Dealloc the space to subsegments. + if (fliptets[0].tet[8] != NULL) { + tet2segpool->dealloc((shellface *) fliptets[0].tet[8]); + } + fliptets[0].tet[8] = NULL; + if (fliptets[1].tet[8] != NULL) { + tet2segpool->dealloc((shellface *) fliptets[1].tet[8]); + } + fliptets[1].tet[8] = NULL; + } + if (checksubfaces) { + // Dealloc the space to subfaces. + if (fliptets[0].tet[9] != NULL) { + tet2subpool->dealloc((shellface *) fliptets[0].tet[9]); + } + fliptets[0].tet[9] = NULL; + if (fliptets[1].tet[9] != NULL) { + tet2subpool->dealloc((shellface *) fliptets[1].tet[9]); + } + fliptets[1].tet[9] = NULL; + } + + if (hullflag > 0) { + // Check if d is dummytet. + if (pd != dummypoint) { + // Update fliptets[0] to edab. + setvertices(fliptets[0], pe, pd, pa, pb); + // Update fliptets[1] to edbc. + setvertices(fliptets[1], pe, pd, pb, pc); + // Create new tet edca. + maketetrahedron(&(fliptets[2])); + // Check if c is dummypoint. + if (pc != dummypoint) { + setvertices(fliptets[2], pe, pd, pc, pa); + } else { + setvertices(fliptets[2], pd, pe, pa, pc); + // Adjust deac->edca + enext0fnextself(fliptets[2]); + esymself(fliptets[2]); + } + // The hullsize does not change. + } else { + // d is dummypoint. + setvertices(fliptets[0], pa, pb, pe, pd); // Create abed. + setvertices(fliptets[1], pb, pc, pe, pd); // Create bced. + maketetrahedron(&(fliptets[2])); // Create caed. + setvertices(fliptets[2], pc, pa, pe, pd); + // Adjust abed->edab, bced->edbc, caed->edca + for (i = 0; i < 3; i++) { + enext2fnextself(fliptets[i]); + enext2self(fliptets[i]); + esymself(fliptets[i]); + } + // We removed one old hull tet, and added three new hull tets. + hullsize += 2; + } + } else { + // Update fliptets[0] to edab. + setvertices(fliptets[0], pe, pd, pa, pb); + // Update fliptets[1] to edbc. + setvertices(fliptets[1], pe, pd, pb, pc); + // Create new tet edca. + maketetrahedron(&(fliptets[2])); + setvertices(fliptets[2], pe, pd, pc, pa); + } + + // Bond three new tets together. + for (i = 0; i < 3; i++) { + enext0fnext(fliptets[i], newface); + bond(newface, fliptets[(i + 1) % 3]); + } + // Bond to top outer boundary faces (at abcd). + for (i = 0; i < 3; i++) { + enextfnext(fliptets[i], newface); + enextself(newface); + bond(newface, topcastets[i]); + } + // Bond bottom outer boundary faces (at bace). + for (i = 0; i < 3; i++) { + enext2fnext(fliptets[i], newface); + enext2self(newface); + bond(newface, botcastets[i]); + } + + // Bond 15 subsegments if there are. + if (checksubsegs) { + // The middle three: ab, bc, ca. + for (i = 0; i < 3; i++) { + tsspivot(topcastets[i], checkseg); + if (checkseg.sh != NULL) { + enextfnext(fliptets[i], newface); + enextself(newface); + tssbond1(newface, checkseg); + sstbond(checkseg, newface); + } + } + // The top three: da, db, dc. Each edge belongs to two tets. + for (i = 0; i < 3; i++) { + enext2(topcastets[i], casface); + tsspivot(casface, checkseg); + if (checkseg.sh != NULL) { + enext(fliptets[i], newface); + tssbond1(newface, checkseg); + sstbond(checkseg, newface); + enext0fnext(fliptets[(i + 2) % 3], newface); + enextself(newface); + tssbond1(newface, checkseg); + sstbond(checkseg, newface); + } + } + // The bot three: ae, be, ce. Each edge belongs to two tets. + for (i = 0; i < 3; i++) { + enext(botcastets[i], casface); + tsspivot(casface, checkseg); + if (checkseg.sh != NULL) { + enext2(fliptets[i], newface); + tssbond1(newface, checkseg); + sstbond(checkseg, newface); + enext0fnext(fliptets[(i + 2) % 3], newface); + enext2self(newface); + tssbond1(newface, checkseg); + sstbond(checkseg, newface); + } + } + } + + // Bond 6 subfaces if there are. + if (checksubfaces) { + for (i = 0; i < 3; i++) { + tspivot(topcastets[i], checksh); + if (checksh.sh != NULL) { + enextfnext(fliptets[i], newface); + enextself(newface); + tsbond(newface, checksh); + } + } + for (i = 0; i < 3; i++) { + tspivot(botcastets[i], checksh); + if (checksh.sh != NULL) { + enext2fnext(fliptets[i], newface); + enext2self(newface); + tsbond(newface, checksh); + } + } + } + + // if (checksubsegs || checksubfaces) { + // Update the point-to-tet map. + point2tet(pa) = encode(fliptets[0]); + point2tet(pb) = encode(fliptets[0]); + point2tet(pc) = encode(fliptets[1]); + point2tet(pd) = encode(fliptets[0]); + point2tet(pe) = encode(fliptets[0]); + // } + + if (hullflag > 0) { + if (dummyflag != 0) { + // Restore the original position of the points (for flipnm()). + if (dummyflag == -1) { + // Reverse the edge. + for (i = 0; i < 3; i++) { + enext0fnextself(fliptets[i]); + esymself(fliptets[i]); + } + // Swap the last two new tets. + newface = fliptets[1]; + fliptets[1] = fliptets[2]; + fliptets[2] = newface; + } else { + // either a or b were swapped. + i = dummyflag; + // Down-shift new tets i times. + for (; i > 0; i--) { + newface = fliptets[0]; + fliptets[0] = fliptets[2]; + fliptets[2] = fliptets[1]; + fliptets[1] = newface; + } + } + } + } + + if (flipflag > 0) { + // Queue faces which may be locally non-Delaunay. + pd = dest(fliptets[0]); + for (i = 0; i < 3; i++) { + enext2fnext(fliptets[i], newface); + futureflip = flippush(futureflip, &newface, pd); + } + if (flipflag > 1) { + pe = org(fliptets[0]); + for (i = 0; i < 3; i++) { + enextfnext(fliptets[i], newface); + futureflip = flippush(futureflip, &newface, pe); + } + } + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// flip32() Remove an edge by transforming 3-to-2 tetrahedra. // +// // +// 'flipedge' (ab) is shared by three tets: edab, edbc, and edca. This rout- // +// ine replaces them by two new tets: abcd and bace. As a result, the edge // +// ab is replaced by the face abc. On return, 'flipedge' is abcd. // +// // +// If 'hullflag' > 0, one of {a, b, c, d, e} may be 'dummypoint'. There may // +// be hull tets involved in this flip. There are two cases: // +// (1) If d is 'dummypoint', then abcd is hull tet, and bace is normal. // +// If e is 'dummypoint', we reconfigure e to d, i.e., turnover it. // +// (2) If c is 'dummypoint' then both abcd and bace are hull tets. // +// If a or b is 'dummypoint', we reconfigure it to c, i.e., rotate the // +// three old tets counterclockwisely (right-hand rule) until a or b // +// is in c's position (see Fig.). // +// // +// If 'flipflag != 0', the convex hull faces will be checked and flipped if // +// they are locally non-Delaunay. If 'flipflag == 1', we assume that a is a // +// newly inserted vertex, so only the two opposite faces of the convex hull // +// will be checked (this avoids unnecessary insphere() testes). // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::flip32(triface* fliptets, int hullflag, int flipflag) +{ + triface topcastets[3], botcastets[3]; + triface newface, casface; + point pa, pb, pc, pd, pe; + int dummyflag; // Rangle = {-1, 0, 1, 2} + int i; + + face *pssub, checksh; + face checkseg; + + int *iptr; + + if (hullflag > 0) { + // Check if e is 'dummypoint'. + if (org(fliptets[0]) == dummypoint) { + // Reverse the edge. + for (i = 0; i < 3; i++) { + enext0fnextself(fliptets[i]); + esymself(fliptets[i]); + } + // Swap the last two tets. + newface = fliptets[1]; + fliptets[1] = fliptets[2]; + fliptets[2] = newface; + dummyflag = -1; // e is dummypoint. + } else { + // Check if a or b is the 'dummypoint'. + if (apex(fliptets[0]) == dummypoint) { + dummyflag = 1; // a is dummypoint. + } else if (apex(fliptets[1]) == dummypoint) { + dummyflag = 2; // b is dummypoint. + } else { + dummyflag = 0; // either c or d is dummypoint. + } + i = dummyflag; + // Down-shift i times. + for (; i > 0; i--) { + newface = fliptets[2]; + fliptets[2] = fliptets[1]; + fliptets[1] = fliptets[0]; + fliptets[0] = newface; + } + } + } + + pa = apex(fliptets[0]); + pb = apex(fliptets[1]); + pc = apex(fliptets[2]); + pd = dest(fliptets[0]); + pe = org(fliptets[0]); + + if (b->verbose > 1) { + printf(" flip 3-to-2: (%d, %d, %d, %d, %d)\n", pointmark(pa), + pointmark(pb), pointmark(pc), pointmark(pd), pointmark(pe)); + } + flip32count++; + + // Get the outer boundary faces. + for (i = 0; i < 3; i++) { + enextfnext(fliptets[i], casface); + enextself(casface); + symedge(casface, topcastets[i]); + } + for (i = 0; i < 3; i++) { + enext2fnext(fliptets[i], casface); + enext2self(casface); + symedge(casface, botcastets[i]); + } + + if (checksubsegs) { + // Check if the flip edge is subsegment. + tsspivot(fliptets[0], checkseg); + if ((checkseg.sh != NULL)) { + if (!sinfected(checkseg)) { + // This subsegment will be flipped. Queue it. + if (b->verbose > 1) { + printf(" Queue a flipped segment (%d, %d).\n", + pointmark(sorg(checkseg)), pointmark(sdest(checkseg))); + } + sinfect(checkseg); // Only save it once. + subsegstack->newindex((void **) &pssub); + *pssub = checkseg; + } + // Clean the seg-to-tet pointer. + stdissolve(checkseg); + } + } + + if (checksubfaces) { + // Check if the three flip faces are subfaces. + for (i = 0; i < 3; i++) { + tspivot(fliptets[i], checksh); + if (checksh.sh != NULL) { + if (b->verbose > 1) { + printf(" Queue a flipped subface (%d, %d, %d).\n", + pointmark(sorg(checksh)), pointmark(sdest(checksh)), + pointmark(sapex(checksh))); + } + stdissolve(checksh); // Disconnect the sub-tet bond. + // Add the missing subface into list. + subfacstack->newindex((void **) &pssub); + *pssub = checksh; + } + } + } + + // Re-use fliptets[0] and fliptets[1]. + fliptets[0].loc = fliptets[0].ver = 0; + fliptets[1].loc = fliptets[1].ver = 0; + elemmarker(fliptets[0].tet) = 0; + elemmarker(fliptets[1].tet) = 0; + if (checksubsegs) { + // Dealloc the space to subsegments. + if (fliptets[0].tet[8] != NULL) { + tet2segpool->dealloc((shellface *) fliptets[0].tet[8]); + } + fliptets[0].tet[8] = NULL; + if (fliptets[1].tet[8] != NULL) { + tet2segpool->dealloc((shellface *) fliptets[1].tet[8]); + } + fliptets[1].tet[8] = NULL; + } + if (checksubfaces) { + // Dealloc the space to subfaces. + if (fliptets[0].tet[9] != NULL) { + tet2subpool->dealloc((shellface *) fliptets[0].tet[9]); + } + fliptets[0].tet[9] = NULL; + if (fliptets[1].tet[9] != NULL) { + tet2subpool->dealloc((shellface *) fliptets[1].tet[9]); + } + fliptets[1].tet[9] = NULL; + } + + // Delete an old tet. + tetrahedrondealloc(fliptets[2].tet); + + if (hullflag > 0) { + // Check if c is dummypointc. + if (pc != dummypoint) { + // Check if d is dummypoint. + if (pd != dummypoint) { + // No hull tet is involved. + } else { + // We removed three old hull tets, and added on new hull tet. + hullsize -= 2; + } + setvertices(fliptets[0], pa, pb, pc, pd); + setvertices(fliptets[1], pb, pa, pc, pe); + } else { + // c is dummypoint. The two new tets are hull tets. + setvertices(fliptets[0], pb, pa, pd, pc); + setvertices(fliptets[1], pa, pb, pe, pc); + // Adjust badc -> abcd. + enext0fnextself(fliptets[0]); + esymself(fliptets[0]); + // Adjust abec -> bace. + enext0fnextself(fliptets[1]); + esymself(fliptets[1]); + // The hullsize does not changle. + } + } else { + setvertices(fliptets[0], pa, pb, pc, pd); + setvertices(fliptets[1], pb, pa, pc, pe); + } + + // Bond abcd <==> bace. + bond(fliptets[0], fliptets[1]); + // Bond new faces to top outer boundary faces (at abcd). + for (i = 0; i < 3; i++) { + enext0fnext(fliptets[0], newface); + bond(newface, topcastets[i]); + enextself(fliptets[0]); + } + // Bond new faces to bottom outer boundary faces (at bace). + for (i = 0; i < 3; i++) { + enext0fnext(fliptets[1], newface); + bond(newface, botcastets[i]); + enext2self(fliptets[1]); + } + + if (checksubsegs) { + // Bond segments to new (flipped) tets. + for (i = 0; i < 3; i++) { + tsspivot(topcastets[i], checkseg); + if (checkseg.sh != NULL) { + tssbond1(fliptets[0], checkseg); + sstbond(checkseg, fliptets[0]); + } + enextself(fliptets[0]); + } + // The three top edges. + for (i = 0; i < 3; i++) { + enext0fnext(fliptets[0], newface); + enextself(newface); // edge b->d, c->d, a->d. + enext(topcastets[i], casface); + tsspivot(casface, checkseg); + if (checkseg.sh != NULL) { + tssbond1(newface, checkseg); + sstbond(checkseg, newface); + } + enextself(fliptets[0]); + } + // Process the bottom tet bace. + for (i = 0; i < 3; i++) { + tsspivot(botcastets[i], checkseg); + if (checkseg.sh != NULL) { + tssbond1(fliptets[1], checkseg); + sstbond(checkseg, fliptets[1]); + } + enext2self(fliptets[1]); + } + // The three bot edges. + for (i = 0; i < 3; i++) { + enext0fnext(fliptets[1], newface); + enext2self(newface); // edge b<-e, c<-e, a<-e. + enext2(botcastets[i], casface); + tsspivot(casface, checkseg); + if (checkseg.sh != NULL) { + tssbond1(newface, checkseg); + sstbond(checkseg, newface); + } + enext2self(fliptets[1]); + } + } + + if (checksubfaces) { + // Bond the top three casing subfaces. + for (i = 0; i < 3; i++) { + tspivot(topcastets[i], checksh); + if (checksh.sh != NULL) { + enext0fnext(fliptets[0], newface); + tsbond(newface, checksh); + } + enextself(fliptets[0]); + } + // Bond the bottom three casing subfaces. + for (i = 0; i < 3; i++) { + tspivot(botcastets[i], checksh); + if (checksh.sh != NULL) { + enext0fnext(fliptets[1], newface); + tsbond(newface, checksh); + } + enext2self(fliptets[1]); + } + } + + // if (checksubsegs || checksubfaces) { + // Update the point-to-tet map. + point2tet(pa) = encode(fliptets[0]); + point2tet(pb) = encode(fliptets[0]); + point2tet(pc) = encode(fliptets[0]); + point2tet(pd) = encode(fliptets[0]); + point2tet(pe) = encode(fliptets[1]); + // } + + if (hullflag > 0) { + if (dummyflag != 0) { + // Restore the original position of the points (for flipnm()). + if (dummyflag == -1) { + // e were dummypoint. Swap the two new tets. + newface = fliptets[0]; + fliptets[0] = fliptets[1]; + fliptets[1] = newface; + } else { + // a or b was dummypoint. + i = dummyflag; + // Rotate toward left i times. + for (; i > 0; i--) { + enextself(fliptets[0]); + enext2self(fliptets[1]); + } + } + } + } + + if (flipflag > 0) { + // Queue faces which may be locally non-Delaunay. + pa = org(fliptets[0]); + enextfnext(fliptets[0], newface); + futureflip = flippush(futureflip, &newface, pa); + enext2fnext(fliptets[1], newface); + futureflip = flippush(futureflip, &newface, pa); + if (flipflag > 1) { + pb = dest(fliptets[0]); + enext2fnext(fliptets[0], newface); + futureflip = flippush(futureflip, &newface, pb); + enextfnext(fliptets[1], newface); + futureflip = flippush(futureflip, &newface, pb); + pc = apex(fliptets[0]); + enext0fnext(fliptets[0], newface); + futureflip = flippush(futureflip, &newface, pc); + enext0fnext(fliptets[1], newface); + futureflip = flippush(futureflip, &newface, pc); + } + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// flipnm() Remove an edge by transforming n-to-m tetrahedra. // +// // +// This routine attemps to remove an edge, denoted as ab, by transforming a // +// set K of n tetrahedra containing ab into a set K' of m tetrahedra, where // +// K and K' have the same outer boundary, and ab is not in K', where n >= 3, // +// m = 2 * n - 4. It can be viwed as a n-to-m flip. // +// // +// 'oldtets' contains n tets sharing at ab. Imaging that ab perpendicularly // +// crosses your screen, b lies in front of a. Let the projections of the n // +// apexes, denoted as p[0], p[1], ..., p[n-1], onto screen in counterclock- // +// wise order (right-hand rule). The n tets are: abp[0]p[1], abp[1]p[2],..., // +// abp[n-1]p[0], respectively. If one of p[i] is dummypoint, we reconfigure // +// it such that p[0] is dummypoint. a or b should not be dummypoint. // +// // +// The principle of the transformation is to recursively reduce the link of // +// ab by perform 2-to-3 flips until the link size of ab is 3, hence a 3-to-2 // +// flip can be applied to remove the edge. // +// // +// Consider a face abp[i] (i in [0, n-1]), a flip23() can be applied if the // +// edge p[(i-1) % n]p[(i+1) % n] crosses it in its interior. Relabel p[i] to // +// c, p[(i-1) % n] to e, and p[(i+1) % n] to d. This transforms the two tets,// +// abcd and bace, to three tets, edab, edbc, and edca. The tet edab remains // +// in the star of ab, while edbc, and edca do not. As a result, p[i] (c) is // +// not on the link of ab anymore. The link size is reduced by 1. A recursion // +// can be applied if n - 1 > 3. // +// // +// NOTE: In above, the flip23() can be applied even if the edge de crosses // +// ab, i.e., a, b, e, and d are coplanar. Although this will creare a degen- // +// erate tet edab (has zero volume), it will be removed after ab is removed. // +// // +// The number m can be counted as follows: there are n - 3 '2-to-3' flips, 1 // +// '3-to-2' flip. Each '2-to-3' flip produces two new tets, and the '3-to-2' // +// flip produces two new tets, hence m = (n - 3) * 2 + 2 = 2 * n - 4. // +// // +/////////////////////////////////////////////////////////////////////////////// + +/* +bool tetgenmesh::flipnm(int n, triface* oldtets, triface* newtets, + bool delaunay, queue* flipque) +{ + triface *recuroldtets, tmpoldtets[2], baktet; + point pa, pb, pc, pd, pe, pf; + bool success, doflip; + REAL sign, ori; + int *iptr, i, j; + + // Check if any apex of ab is dummypoint. + for (i = n - 1; i > 0; i--) { + if (apex(oldtets[i]) == dummypoint) break; + } + // Now i is the shift distance to the first one. + for (; i > 0; i--) { + baktet = oldtets[0]; + for (j = 0; j < n - 1; j++) { + oldtets[j] = oldtets[j + 1]; + } + oldtets[n - 1] = baktet; + } + + pa = org(oldtets[0]); + pb = dest(oldtets[0]); + + if (b->verbose > 1) { + printf(" flip %d-to-%d: (%d, %d)\n", n, 2 * n - 4, pointmark(pa), + pointmark(pb)); + } + flipnmcount++; + + success = false; + + for (i = 0; i < n; i++) { + pc = apex(oldtets[i]); + pd = apex(oldtets[(i + 1) % n]); + pe = apex(oldtets[(i != 0) ? i - 1 : n - 1]); + // Decide if the face abc is flipable. + if (pc != dummypoint) { + if ((pd != dummypoint) && (pc != dummypoint)) { + ori = orient3d(pa, pb, pd, pe); + if (ori >= 0) { // Allow ori == 0, support 4-to-4 flip. + ori = orient3d(pb, pc, pd, pe); + if (ori > 0) { + ori = orient3d(pc, pa, pd, pe); + } + } + doflip = ori > 0; + if (doflip && delaunay) { + // Only do flip if abc is not a locally Delaunay face. + sign = insphere_sos(pa, pb, pc, pd, pe); + doflip = (sign < 0); + } + } else { + doflip = false; + } + } else { + // Two faces abd and abe are on the hull. + doflip = true; // 2-to-2 flip is possible. + if (delaunay) { + // Only do flip if ab is not a locally Delaunay edge. + pf = apex(oldtets[(i + 2) % n]); + sign = insphere_sos(pa, pb, pd, pf, pe); + doflip = (sign < 0); + } + } + if (doflip) { + // Get the two old tets. + tmpoldtets[0] = oldtets[i]; + tmpoldtets[1] = oldtets[(i != 0) ? i - 1 : n - 1]; + // Adjust tmpoldtets[1] (abec) -> bace. + enext0fnextself(tmpoldtets[1]); + esymself(tmpoldtets[1]); + // Adjust the tets so that newtets[2] will be edca (see Fig.). + enextself(tmpoldtets[0]); + enext2self(tmpoldtets[1]); + // Do flip23() on abp[i] (abc). + flip23(tmpoldtets, newtets, flipque); + // Form the new star of ab which has n-1 tets. + recuroldtets = new triface[n - 1]; + // Set the remaining n-2 tets around ab. + for (j = 0; j < n - 2 ; j++) { + recuroldtets[j] = oldtets[(i + 1 + j) % n]; + } + // Put the last tet having ab. Leave a copy in baktet. + recuroldtets[j] = baktet = newtets[2]; + // Adjust recuroldtets[j] to abp[0]p[1] (see Fig.). + enext2fnextself(recuroldtets[j]); // j == n - 2; + enext2self(recuroldtets[j]); + esymself(recuroldtets[j]); + // Check the size of the link of ab. + if (n > 4) { // Actually, if (n - 1 > 3) + // Recursively do flipnm(). + success = flipnm(n-1, recuroldtets, &(newtets[2]), delaunay, flipque); + // Are we success? + if (!success) { + if (!delaunay) { + // No! Reverse the flip23() operation. + newtets[2] = baktet; // Do we really need this? + flip32(newtets, tmpoldtets, flipque); + // Adjust the tets back to original position (see Fig.). + enext2self(tmpoldtets[0]); + enextself(tmpoldtets[1]); + // Adjust back to abp[(i-1) % n]. + enext0fnextself(tmpoldtets[1]); + esymself(tmpoldtets[1]); + // Delete the two original old tets first. + tetrahedrondealloc(oldtets[i].tet); + tetrahedrondealloc(oldtets[(i != 0) ? i - 1 : n - 1].tet); + // Set the tets back to their original positions. + oldtets[i] = tmpoldtets[0]; + oldtets[(i != 0) ? i - 1 : n - 1] = tmpoldtets[1]; + // Delete the three new tets. + tetrahedrondealloc(newtets[0].tet); + tetrahedrondealloc(newtets[1].tet); + tetrahedrondealloc(newtets[2].tet); // = recuroldtets[j].tet + } else { + // Only delete the first two old tets. Their common face is + // not locally Delaunay and has been flipped. + tetrahedrondealloc(oldtets[i].tet); + tetrahedrondealloc(oldtets[(i != 0) ? i - 1 : n - 1].tet); + // Here the tet recuroldtets[j] does not get deleted. It is + // a part of current mesh. + } + } else { + // Delete the last tet in 'recuroldtets'. This tet is neither in + // 'oldtets' nor in 'newtets'. + tetrahedrondealloc(recuroldtets[j].tet); // j == n - 2 + } + } else { + // Remove ab by a flip32(). + flip32(recuroldtets, &(newtets[2]), flipque); + // Delete the last tet in 'recuroldtets'. This one is just created + // by the flip 2-to-3 operation within this routine. + tetrahedrondealloc(recuroldtets[j].tet); // j == n - 2 + success = true; + } + // The other tets in 'recuroldtets' are still in 'oldtets'. + delete [] recuroldtets; + break; + } // if (doflip) + } // for (i = 0; i < n; i++) + + return success; +} +*/ + +/////////////////////////////////////////////////////////////////////////////// +// // +// lawsonflip() Flip non-locally Delaunay faces by primitive flips. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::lawsonflip3d(int flipflag) +{ + triface fliptets[5], baktets[2]; + triface fliptet, neightet, *parytet; + face checksh, checkseg; + point *pt, pd, pe; + REAL sign, ori; + long flipcount; + bool bflag; // for flipflag = 3. + int n, i; + + int *iptr; + + // for flipflag = 2. + point pa, pb, ppa, ppb; + int ecount; + + if (b->verbose > 1) { + printf(" Lawson flip %ld faces.\n", flippool->items); + flipcount = flip23count + flip32count; + } + + ecount = 0; // Initialize the counter. + + while (futureflip != (badface *) NULL) { + + // Pop a face from the stack. + fliptet = futureflip->tt; // abcd (may be a hull tet) + pd = futureflip->foppo; // The new vertex. + futureflip = futureflip->nextitem; + + // Skip it if it is a dead tet. + if (fliptet.tet[4] == NULL) continue; + // Skip it if it is not the same tet as we saved. + if (oppo(fliptet) != pd) continue; + + // Get its opposite tet. + fliptet.ver = 4; + symedge(fliptet, neightet); + + if ((point) neightet.tet[7] == dummypoint) { + // A hull tet. Check if its base face is visible by d. + pt = (point *) neightet.tet; + ori = orient3d(pt[4], pt[5], pt[6], pd); orient3dcount++; + if (ori < 0) { + // Visible! Found a 2-to-3 flip on abc. + fliptets[0] = fliptet; + fliptets[1] = neightet; + flip23(fliptets, 1, flipflag); // flip a hull tet. + recenttet = fliptets[0]; + } else if (ori == 0) { + // Handle degenerate case ori == 0. + if (oppo(fliptet) == oppo(neightet)) { + // Two hull tets (fliptet and neightet) have the same base face. + for (i = 0; i < 3; i++) { + fnext(fliptet, fliptets[0]); + fnext(neightet, fliptets[1]); + bond(fliptets[0], fliptets[1]); + if (i == 0) { + // apex(fliptets[0]) is the new point. The opposite face may + // be not locally Delaunay. Put it in flip stack. + // assert(apex(fliptets[0]) == pd); // SELF_CHECK + enext0fnextself(fliptets[0]); + futureflip = flippush(futureflip, &(fliptets[0]), pd); + // assert(apex(fliptets[1]) == pd); // SELF_CHECK + enext0fnextself(fliptets[1]); + futureflip = flippush(futureflip, &(fliptets[1]), pd); + } + enextself(fliptet); + enext2self(neightet); + } + // Delete the two tets. + tetrahedrondealloc(fliptet.tet); + tetrahedrondealloc(neightet.tet); + // Update the hull size. + hullsize -= 2; + } + } + continue; + } + + pe = oppo(neightet); + pt = (point *) fliptet.tet; + // assert((point) fliptet.tet[7] != dummypoint); // SELF_CHECK + + sign = insphere_sos(pt[4], pt[5], pt[6], pt[7], pe); + + if (b->verbose > 2) { + printf(" Insphere: (%d, %d, %d) %d, %d\n", pointmark(org(fliptet)), + pointmark(dest(fliptet)), pointmark(apex(fliptet)), + pointmark(oppo(fliptet)), pointmark(pe)); + } + + // Flip it if it is not locally Delaunay. + if (sign < 0) { + // Check the convexity of its three edges. + fliptet.ver = 0; + for (i = 0; i < 3; i++) { + ori = orient3d(org(fliptet), dest(fliptet), pd, pe); orient3dcount++; + if (ori <= 0) break; + enextself(fliptet); + } + if (i == 3) { + // A 2-to-3 flip is found. + if (flipflag == 3) { + // Do not flip a subface. + tspivot(fliptet, checksh); + bflag = (checksh.sh == NULL); + } else { + bflag = true; + } + if (bflag) { + fliptets[0] = fliptet; // tet abcd, d is the new vertex. + symedge(fliptets[0], fliptets[1]); // tet bace. + flip23(fliptets, 0, flipflag); + recenttet = fliptets[0]; // for point location. + } + } else { + // A 3-to-2 or 4-to-4 may possible. + if (flipflag == 3) { + // Do not flip a subsegment. + tsspivot(fliptet, checkseg); + bflag = (checkseg.sh == NULL); + } else { + bflag = true; + } + if (bflag) { + enext0fnext(fliptet, fliptets[0]); + esymself(fliptets[0]); // tet badc, d is the new vertex. + n = 0; + do { + fnext(fliptets[n], fliptets[n + 1]); + n++; + } while ((fliptets[n].tet != fliptet.tet) && (n < 5)); + if (n == 3) { + // Found a 3-to-2 flip. + flip32(fliptets, 0, flipflag); + recenttet = fliptets[0]; // for point location. + if (flipflag == 2) { + ecount = 0; // Clear the counter. + } + } else if ((n == 4) && (ori == 0)) { + // Find a 4-to-4 flip. + flipnmcount++; + // First do a 2-to-3 flip. + fliptets[0] = fliptet; // tet abcd, d is the new vertex. + baktets[0] = fliptets[2]; + baktets[1] = fliptets[3]; + flip23(fliptets, 1, flipflag); // hull tet may involve. + // Then do a 3-to-2 flip. + enextfnextself(fliptets[0]); // fliptets[0] is edab. + enextself(fliptets[0]); + esymself(fliptets[0]); // tet badc, d is the new vertex. + fliptets[1] = baktets[0]; + fliptets[2] = baktets[1]; + flip32(fliptets, 1, flipflag); // hull tet may involve. + recenttet = fliptets[0]; // for point location. + if (flipflag == 2) { + ecount = 0; // Clear the counter. + } + } else { + // An unflipable face. Will be flipped later. + if (flipflag == 2) { // if (flipflag > 1) { + // Queue all other faces at the edge for flipping. + if (b->verbose > 1) { + printf(" Queue an N32 edge (%d, %d)", + pointmark(org(fliptets[0])), pointmark(dest(fliptets[0]))); + } + pe = apex(fliptets[0]); + fliptets[1] = fliptets[0]; + while (1) { + if (b->verbose > 1) { + printf(" %d", pointmark(apex(fliptets[1]))); + } + pd = oppo(fliptets[1]); + futureflip = flippush(futureflip, &fliptets[1], pd); + fnextself(fliptets[1]); + if (apex(fliptets[1]) == pe) break; + } + if (b->verbose > 1) { + printf("\n"); + } + ecount++; // Increase the counter. + if (ecount >= 1000) { + // assert(0); // A flip deadlock. + // Dump the dead lock case. + pa = org(fliptets[0]); + pb = dest(fliptets[0]); + if (ecount == 1000) { + printf("-- Flip failed after inserting point %ld.\n", + pointpool->items); + ppa = pa; // Bakup the startiing loop edge. + ppb = pb; + } else { + if (((ppa == pa) && (ppb == pb)) || + ((ppa == pb) && (ppb == pa))) { + // Dump the current mesh and exit. + outnodes(0); + outelements(0); + exit(1); + } + } + printf("p:draw_subseg(%d, %d)\n",pointmark(pa),pointmark(pb)); + printf("p:draw_subface(%d, %d, %d)\n", pointmark(org(fliptet)), + pointmark(dest(fliptet)), pointmark(apex(fliptet))); + printf("p:draw_tet(%d, %d, %d, %d)\n", pointmark(org(fliptet)), + pointmark(dest(fliptet)), pointmark(apex(fliptet)), + pointmark(oppo(fliptet))); + symedge(fliptet, fliptets[1]); + printf("p:draw_tet(%d, %d, %d, %d)\n", + pointmark(org(fliptets[1])), + pointmark(dest(fliptets[1])), + pointmark(apex(fliptets[1])), + pointmark(oppo(fliptets[1]))); + pe = apex(fliptets[0]); + fliptets[1] = fliptets[0]; + while (1) { + pd = oppo(fliptets[1]); + printf(" -- p:draw_tet(%d, %d, %d, %d)\n", pointmark(pa), + pointmark(pb),pointmark(apex(fliptets[1])),pointmark(pd)); + fnextself(fliptets[1]); + if (apex(fliptets[1]) == pe) break; + } + } // if (ecount >= 1000) + } + } + } // bflag + } // if (i == 3) + } // if (sign < 0) + } + + if (b->verbose > 1) { + printf(" %ld faces stacked, %ld flips.\n", flippool->items, + flip23count + flip32count - flipcount); + } + + flippool->restart(); +} + +#endif // #ifndef flipCXX \ No newline at end of file diff --git a/contrib/TetgenNew/geom.cxx b/contrib/TetgenNew/geom.cxx new file mode 100644 index 0000000000..275df658dd --- /dev/null +++ b/contrib/TetgenNew/geom.cxx @@ -0,0 +1,4517 @@ +#ifndef geomCXX +#define geomCXX + +#include "tetgen.h" + +REAL tetgenmesh::PI = 3.14159265358979323846264338327950288419716939937510582; + +/////////////////////////////////////////////////////////////////////////////// +// // +// tri_edge_2d() Triangle-edge coplanar intersection test. // +// // +// This routine takes a triangle T (with vertices A, B, C) and an edge E (P, // +// Q) in a plane in 3D, and tests if they intersect each other. Return 1 if // +// they are intersected, i.e., T \cap E is not empty, otherwise, return 0. // +// // +// If the point 'R' is not NULL, it lies strictly above T [A, B, C]. // +// // +// If T1 and T2 intersect each other (return 1), they may intersect in diff- // +// erent ways. If 'level' > 0, their intersection type will be reported in // +// combinations of 'types' and 'pos'. // +// // +/////////////////////////////////////////////////////////////////////////////// + +int tetgenmesh::tri_edge_2d(point A, point B, point C, point P, point Q, + point R, int level, int *types, int *pos) +{ + point U[3], V[3]; // The permuted vectors of points. + int pu[3], pv[3]; // The original positions of points. + REAL sA, sB, sC; + REAL s1, s2, s3, s4; + int z1; + + if (R == NULL) { + REAL n[3], len; + // Calculate a lift point, saved in dummypoint. + facenormal(A, B, C, n, 1); + len = sqrt(DOT(n, n)); + n[0] /= len; + n[1] /= len; + n[2] /= len; + len = DIST(A, B); + len += DIST(B, C); + len += DIST(C, A); + len /= 3.0; + R = dummypoint; + R[0] = A[0] + len * n[0]; + R[1] = A[1] + len * n[1]; + R[2] = A[2] + len * n[2]; + } + + // Test A's, B's, and C's orientations wrt plane PQR. + sA = orient3d(P, Q, R, A); + sB = orient3d(P, Q, R, B); + sC = orient3d(P, Q, R, C); + orient3dcount+=3; + + if (b->epsilon) { + // Re-evaluate the sign with respect to the tolerance. + if ((sA != 0) && iscoplanar(P, Q, R, A, sA)) sA = 0; + if ((sB != 0) && iscoplanar(P, Q, R, B, sB)) sB = 0; + if ((sC != 0) && iscoplanar(P, Q, R, C, sC)) sC = 0; + } + + if (b->verbose > 2) { + printf(" Tri-edge-2d (%d %d %d)-(%d %d)-(%d) (%c%c%c)", pointmark(A), + pointmark(B), pointmark(C), pointmark(P), pointmark(Q), pointmark(R), + sA > 0 ? '+' : (sA < 0 ? '-' : '0'), sB>0 ? '+' : (sB<0 ? '-' : '0'), + sC>0 ? '+' : (sC<0 ? '-' : '0')); + } + triedgcopcount++; + + if (sA < 0) { + if (sB < 0) { + if (sC < 0) { // (---). + return 0; + } else { + if (sC > 0) { // (--+). + // All points are in the right positions. + SETVECTOR3(U, A, B, C); // I3 + SETVECTOR3(V, P, Q, R); // I2 + SETVECTOR3(pu, 0, 1, 2); + SETVECTOR3(pv, 0, 1, 2); + z1 = 0; + } else { // (--0). + SETVECTOR3(U, A, B, C); // I3 + SETVECTOR3(V, P, Q, R); // I2 + SETVECTOR3(pu, 0, 1, 2); + SETVECTOR3(pv, 0, 1, 2); + z1 = 1; + } + } + } else { + if (sB > 0) { + if (sC < 0) { // (-+-). + SETVECTOR3(U, C, A, B); // PT = ST + SETVECTOR3(V, P, Q, R); // I2 + SETVECTOR3(pu, 2, 0, 1); + SETVECTOR3(pv, 0, 1, 2); + z1 = 0; + } else { + if (sC > 0) { // (-++). + SETVECTOR3(U, B, C, A); // PT = ST x ST + SETVECTOR3(V, Q, P, R); // PL = SL + SETVECTOR3(pu, 1, 2, 0); + SETVECTOR3(pv, 1, 0, 2); + z1 = 0; + } else { // (-+0). + SETVECTOR3(U, C, A, B); // PT = ST + SETVECTOR3(V, P, Q, R); // I2 + SETVECTOR3(pu, 2, 0, 1); + SETVECTOR3(pv, 0, 1, 2); + z1 = 2; + } + } + } else { + if (sC < 0) { // (-0-). + SETVECTOR3(U, C, A, B); // PT = ST + SETVECTOR3(V, P, Q, R); // I2 + SETVECTOR3(pu, 2, 0, 1); + SETVECTOR3(pv, 0, 1, 2); + z1 = 1; + } else { + if (sC > 0) { // (-0+). + SETVECTOR3(U, B, C, A); // PT = ST x ST + SETVECTOR3(V, Q, P, R); // PL = SL + SETVECTOR3(pu, 1, 2, 0); + SETVECTOR3(pv, 1, 0, 2); + z1 = 2; + } else { // (-00). + SETVECTOR3(U, B, C, A); // PT = ST x ST + SETVECTOR3(V, Q, P, R); // PL = SL + SETVECTOR3(pu, 1, 2, 0); + SETVECTOR3(pv, 1, 0, 2); + z1 = 3; + } + } + } + } + } else { + if (sA > 0) { + if (sB < 0) { + if (sC < 0) { // (+--). + SETVECTOR3(U, B, C, A); // PT = ST x ST + SETVECTOR3(V, P, Q, R); // I2 + SETVECTOR3(pu, 1, 2, 0); + SETVECTOR3(pv, 0, 1, 2); + z1 = 0; + } else { + if (sC > 0) { // (+-+). + SETVECTOR3(U, C, A, B); // PT = ST + SETVECTOR3(V, Q, P, R); // PL = SL + SETVECTOR3(pu, 2, 0, 1); + SETVECTOR3(pv, 1, 0, 2); + z1 = 0; + } else { // (+-0). + SETVECTOR3(U, C, A, B); // PT = ST + SETVECTOR3(V, Q, P, R); // PL = SL + SETVECTOR3(pu, 2, 0, 1); + SETVECTOR3(pv, 1, 0, 2); + z1 = 2; + } + } + } else { + if (sB > 0) { + if (sC < 0) { // (++-). + SETVECTOR3(U, A, B, C); // I3 + SETVECTOR3(V, Q, P, R); // PL = SL + SETVECTOR3(pu, 0, 1, 2); + SETVECTOR3(pv, 1, 0, 2); + z1 = 0; + } else { + if (sC > 0) { // (+++). + return 0; + } else { // (++0). + SETVECTOR3(U, A, B, C); // I3 + SETVECTOR3(V, Q, P, R); // PL = SL + SETVECTOR3(pu, 0, 1, 2); + SETVECTOR3(pv, 1, 0, 2); + z1 = 1; + } + } + } else { // (+0#) + if (sC < 0) { // (+0-). + SETVECTOR3(U, B, C, A); // PT = ST x ST + SETVECTOR3(V, P, Q, R); // I2 + SETVECTOR3(pu, 1, 2, 0); + SETVECTOR3(pv, 0, 1, 2); + z1 = 2; + } else { + if (sC > 0) { // (+0+). + SETVECTOR3(U, C, A, B); // PT = ST + SETVECTOR3(V, Q, P, R); // PL = SL + SETVECTOR3(pu, 2, 0, 1); + SETVECTOR3(pv, 1, 0, 2); + z1 = 1; + } else { // (+00). + SETVECTOR3(U, B, C, A); // PT = ST x ST + SETVECTOR3(V, P, Q, R); // I2 + SETVECTOR3(pu, 1, 2, 0); + SETVECTOR3(pv, 0, 1, 2); + z1 = 3; + } + } + } + } + } else { + if (sB < 0) { + if (sC < 0) { // (0--). + SETVECTOR3(U, B, C, A); // PT = ST x ST + SETVECTOR3(V, P, Q, R); // I2 + SETVECTOR3(pu, 1, 2, 0); + SETVECTOR3(pv, 0, 1, 2); + z1 = 1; + } else { + if (sC > 0) { // (0-+). + SETVECTOR3(U, A, B, C); // I3 + SETVECTOR3(V, P, Q, R); // I2 + SETVECTOR3(pu, 0, 1, 2); + SETVECTOR3(pv, 0, 1, 2); + z1 = 2; + } else { // (0-0). + SETVECTOR3(U, C, A, B); // PT = ST + SETVECTOR3(V, Q, P, R); // PL = SL + SETVECTOR3(pu, 2, 0, 1); + SETVECTOR3(pv, 1, 0, 2); + z1 = 3; + } + } + } else { + if (sB > 0) { + if (sC < 0) { // (0+-). + SETVECTOR3(U, A, B, C); // I3 + SETVECTOR3(V, Q, P, R); // PL = SL + SETVECTOR3(pu, 0, 1, 2); + SETVECTOR3(pv, 1, 0, 2); + z1 = 2; + } else { + if (sC > 0) { // (0++). + SETVECTOR3(U, B, C, A); // PT = ST x ST + SETVECTOR3(V, Q, P, R); // PL = SL + SETVECTOR3(pu, 1, 2, 0); + SETVECTOR3(pv, 1, 0, 2); + z1 = 1; + } else { // (0+0). + SETVECTOR3(U, C, A, B); // PT = ST + SETVECTOR3(V, P, Q, R); // I2 + SETVECTOR3(pu, 2, 0, 1); + SETVECTOR3(pv, 0, 1, 2); + z1 = 3; + } + } + } else { // (00#) + if (sC < 0) { // (00-). + SETVECTOR3(U, A, B, C); // I3 + SETVECTOR3(V, Q, P, R); // PL = SL + SETVECTOR3(pu, 0, 1, 2); + SETVECTOR3(pv, 1, 0, 2); + z1 = 3; + } else { + if (sC > 0) { // (00+). + SETVECTOR3(U, A, B, C); // I3 + SETVECTOR3(V, P, Q, R); // I2 + SETVECTOR3(pu, 0, 1, 2); + SETVECTOR3(pv, 0, 1, 2); + z1 = 3; + } else { // (000) + // Not possible unless ABC is degenerate. + z1 = 4; + } + } + } + } + } + } + + s1 = orient3d(U[0], U[2], R, V[1]); // A, C, R, Q + s2 = orient3d(U[1], U[2], R, V[0]); // B, C, R, P + orient3dcount+=2; + + if (b->epsilon) { + if ((s1 != 0) && iscoplanar(U[0], U[2], R, V[1], s1)) s1 = 0; + if ((s2 != 0) && iscoplanar(U[1], U[2], R, V[0], s2)) s2 = 0; + } + + if (b->verbose > 2) { + printf(" Tri-edge-2d (%d %d %d)-(%d %d %d) (%d) (%c%c)\n", + pointmark(U[0]), pointmark(U[1]), pointmark(U[2]), pointmark(V[0]), + pointmark(V[1]), pointmark(V[2]), z1, s1>0 ? '+' : (s1<0 ? '-' : '0'), + s2>0 ? '+' : (s2<0 ? '-' : '0')); + } + assert(z1 != 4); // SELF_CHECK + + if (s1 > 0) { + return 0; + } + if (s2 < 0) { + return 0; + } + + if (level == 0) { + return 1; // They are intersected. + } + + if (z1 == 1) { + if (s1 == 0) { // (0###) + // C = Q. + types[0] = (int) SHAREVERT; + pos[0] = pu[2]; // C + pos[1] = pv[1]; // Q + types[1] = (int) DISJOINT; + } else { + if (s2 == 0) { // (#0##) + // C = P. + types[0] = (int) SHAREVERT; + pos[0] = pu[2]; // C + pos[1] = pv[0]; // P + types[1] = (int) DISJOINT; + } else { // (-+##) + // C in [P, Q]. + types[0] = (int) ACROSSVERT; + pos[0] = pu[2]; // C + pos[1] = pv[0]; // [P, Q] + types[1] = (int) DISJOINT; + } + } + return 1; + } + + s3 = orient3d(U[0], U[2], R, V[0]); // A, C, R, P + s4 = orient3d(U[1], U[2], R, V[1]); // B, C, R, Q + orient3dcount+=2; + + if (b->epsilon) { + if ((s3 != 0) && iscoplanar(U[0], U[2], R, V[0], s3)) s3 = 0; + if ((s4 != 0) && iscoplanar(U[1], U[2], R, V[1], s4)) s4 = 0; + } + + if (z1 == 0) { // (tritri-03) + if (s1 < 0) { + if (s3 > 0) { + assert(s2 > 0); // SELF_CHECK + if (s4 > 0) { + // [P, Q] overlaps [k, l] (-+++). + types[0] = (int) ACROSSEDGE; + pos[0] = pu[2]; // [C, A] + pos[1] = pv[0]; // [P, Q] + types[1] = (int) TOUCHFACE; + pos[2] = 3; // [A, B, C] + pos[3] = pv[1]; // Q + } else { + if (s4 == 0) { + // Q = l, [P, Q] contains [k, l] (-++0). + types[0] = (int) ACROSSEDGE; + pos[0] = pu[2]; // [C, A] + pos[1] = pv[0]; // [P, Q] + types[1] = (int) TOUCHEDGE; + pos[2] = pu[1]; // [B, C] + pos[3] = pv[1]; // Q + } else { // s4 < 0 + // [P, Q] contains [k, l] (-++-). + types[0] = (int) ACROSSEDGE; + pos[0] = pu[2]; // [C, A] + pos[1] = pv[0]; // [P, Q] + types[1] = (int) ACROSSEDGE; + pos[2] = pu[1]; // [B, C] + pos[3] = pv[0]; // [P, Q] + } + } + } else { + if (s3 == 0) { + assert(s2 > 0); // SELF_CHECK + if (s4 > 0) { + // P = k, [P, Q] in [k, l] (-+0+). + types[0] = (int) TOUCHEDGE; + pos[0] = pu[2]; // [C, A] + pos[1] = pv[0]; // P + types[1] = (int) TOUCHFACE; + pos[2] = 3; // [A, B, C] + pos[3] = pv[1]; // Q + } else { + if (s4 == 0) { + // [P, Q] = [k, l] (-+00). + types[0] = (int) TOUCHEDGE; + pos[0] = pu[2]; // [C, A] + pos[1] = pv[0]; // P + types[1] = (int) TOUCHEDGE; + pos[2] = pu[1]; // [B, C] + pos[3] = pv[1]; // Q + } else { + // P = k, [P, Q] contains [k, l] (-+0-). + types[0] = (int) TOUCHEDGE; + pos[0] = pu[2]; // [C, A] + pos[1] = pv[0]; // P + types[1] = (int) ACROSSEDGE; + pos[2] = pu[1]; // [B, C] + pos[3] = pv[0]; // [P, Q] + } + } + } else { // s3 < 0 + if (s2 > 0) { + if (s4 > 0) { + // [P, Q] in [k, l] (-+-+). + types[0] = (int) TOUCHFACE; + pos[0] = 3; // [A, B, C] + pos[1] = pv[0]; // P + types[1] = (int) TOUCHFACE; + pos[2] = 3; // [A, B, C] + pos[3] = pv[1]; // Q + } else { + if (s4 == 0) { + // Q = l, [P, Q] in [k, l] (-+-0). + types[0] = (int) TOUCHFACE; + pos[0] = 3; // [A, B, C] + pos[1] = pv[0]; // P + types[1] = (int) TOUCHEDGE; + pos[2] = pu[1]; // [B, C] + pos[3] = pv[1]; // Q + } else { // s4 < 0 + // [P, Q] overlaps [k, l] (-+--). + types[0] = (int) TOUCHFACE; + pos[0] = 3; // [A, B, C] + pos[1] = pv[0]; // P + types[1] = (int) ACROSSEDGE; + pos[2] = pu[1]; // [B, C] + pos[3] = pv[0]; // [P, Q] + } + } + } else { // s2 == 0 + // P = l (#0##). + types[0] = (int) TOUCHEDGE; + pos[0] = pu[1]; // [B, C] + pos[1] = pv[0]; // P + types[1] = (int) DISJOINT; + } + } + } + } else { // s1 == 0 + // Q = k (0####) + types[0] = (int) TOUCHEDGE; + pos[0] = pu[2]; // [C, A] + pos[1] = pv[1]; // Q + types[1] = (int) DISJOINT; + } + } else if (z1 == 2) { // (tritri-23) + if (s1 < 0) { + if (s3 > 0) { + assert(s2 > 0); // SELF_CHECK + if (s4 > 0) { + // [P, Q] overlaps [A, l] (-+++). + types[0] = (int) ACROSSVERT; + pos[0] = pu[0]; // A + pos[1] = pv[0]; // [P, Q] + types[1] = (int) TOUCHFACE; + pos[2] = 3; // [A, B, C] + pos[3] = pv[1]; // Q + } else { + if (s4 == 0) { + // Q = l, [P, Q] contains [A, l] (-++0). + types[0] = (int) ACROSSVERT; + pos[0] = pu[0]; // A + pos[1] = pv[0]; // [P, Q] + types[1] = (int) TOUCHEDGE; + pos[2] = pu[1]; // [B, C] + pos[3] = pv[1]; // Q + } else { // s4 < 0 + // [P, Q] contains [A, l] (-++-). + types[0] = (int) ACROSSVERT; + pos[0] = pu[0]; // A + pos[1] = pv[0]; // [P, Q] + types[1] = (int) ACROSSEDGE; + pos[2] = pu[1]; // [B, C] + pos[3] = pv[0]; // [P, Q] + } + } + } else { + if (s3 == 0) { + assert(s2 > 0); // SELF_CHECK + if (s4 > 0) { + // P = A, [P, Q] in [A, l] (-+0+). + types[0] = (int) SHAREVERT; + pos[0] = pu[0]; // A + pos[1] = pv[0]; // P + types[1] = (int) TOUCHFACE; + pos[2] = 3; // [A, B, C] + pos[3] = pv[1]; // Q + } else { + if (s4 == 0) { + // [P, Q] = [A, l] (-+00). + types[0] = (int) SHAREVERT; + pos[0] = pu[0]; // A + pos[1] = pv[0]; // P + types[1] = (int) TOUCHEDGE; + pos[2] = pu[1]; // [B, C] + pos[3] = pv[1]; // Q + } else { // s4 < 0 + // Q = l, [P, Q] in [A, l] (-+0-). + types[0] = (int) SHAREVERT; + pos[0] = pu[0]; // A + pos[1] = pv[0]; // P + types[1] = (int) ACROSSEDGE; + pos[2] = pu[1]; // [B, C] + pos[3] = pv[0]; // [P, Q] + } + } + } else { // s3 < 0 + if (s2 > 0) { + if (s4 > 0) { + // [P, Q] in [A, l] (-+-+). + types[0] = (int) TOUCHFACE; + pos[0] = 3; // [A, B, C] + pos[1] = pv[0]; // P + types[0] = (int) TOUCHFACE; + pos[0] = 3; // [A, B, C] + pos[1] = pv[1]; // Q + } else { + if (s4 == 0) { + // Q = l, [P, Q] in [A, l] (-+-0). + types[0] = (int) TOUCHFACE; + pos[0] = 3; // [A, B, C] + pos[1] = pv[0]; // P + types[0] = (int) TOUCHEDGE; + pos[0] = pu[1]; // [B, C] + pos[1] = pv[1]; // Q + } else { // s4 < 0 + // [P, Q] overlaps [A, l] (-+--). + types[0] = (int) TOUCHFACE; + pos[0] = 3; // [A, B, C] + pos[1] = pv[0]; // P + types[0] = (int) ACROSSEDGE; + pos[0] = pu[1]; // [B, C] + pos[1] = pv[0]; // [P, Q] + } + } + } else { // s2 == 0 + // P = l (#0##). + types[0] = (int) TOUCHEDGE; + pos[0] = pu[1]; // [B, C] + pos[1] = pv[0]; // P + types[1] = (int) DISJOINT; + } + } + } + } else { // s1 == 0 + // Q = A (0###). + types[0] = (int) SHAREVERT; + pos[0] = pu[0]; // A + pos[1] = pv[1]; // Q + types[1] = (int) DISJOINT; + } + } else if (z1 == 3) { // (tritri-33) + if (s1 < 0) { + if (s3 > 0) { + assert(s2 > 0); // SELF_CHECK + if (s4 > 0) { + // [P, Q] overlaps [A, B] (-+++). + types[0] = (int) ACROSSVERT; + pos[0] = pu[0]; // A + pos[1] = pv[0]; // [P, Q] + types[1] = (int) TOUCHEDGE; + pos[2] = pu[0]; // [A, B] + pos[3] = pv[1]; // Q + } else { + if (s4 == 0) { + // Q = B, [P, Q] contains [A, B] (-++0). + types[0] = (int) ACROSSVERT; + pos[0] = pu[0]; // A + pos[1] = pv[0]; // [P, Q] + types[1] = (int) SHAREVERT; + pos[2] = pu[1]; // B + pos[3] = pv[1]; // Q + } else { // s4 < 0 + // [P, Q] contains [A, B] (-++-). + types[0] = (int) ACROSSVERT; + pos[0] = pu[0]; // A + pos[1] = pv[0]; // [P, Q] + types[1] = (int) ACROSSVERT; + pos[2] = pu[1]; // B + pos[3] = pv[0]; // [P, Q] + } + } + } else { + if (s3 == 0) { + assert(s2 > 0); // SELF_CHECK + if (s4 > 0) { + // P = A, [P, Q] in [A, B] (-+0+). + types[0] = (int) SHAREVERT; + pos[0] = pu[0]; // A + pos[1] = pv[0]; // P + types[1] = (int) TOUCHEDGE; + pos[2] = pu[0]; // [A, B] + pos[3] = pv[1]; // Q + } else { + if (s4 == 0) { + // [P, Q] = [A, B] (-+00). + types[0] = (int) SHAREEDGE; + pos[0] = pu[0]; // [A, B] + pos[1] = pv[0]; // [P, Q] + types[1] = (int) DISJOINT; + } else { // s4 < 0 + // P= A, [P, Q] in [A, B] (-+0-). + types[0] = (int) SHAREVERT; + pos[0] = pu[0]; // A + pos[1] = pv[0]; // P + types[1] = (int) ACROSSVERT; + pos[2] = pu[1]; // B + pos[3] = pv[0]; // [P, Q] + } + } + } else { // s3 < 0 + if (s2 > 0) { + if (s4 > 0) { + // [P, Q] in [A, B] (-+-+). + types[0] = (int) TOUCHEDGE; + pos[0] = pu[0]; // [A, B] + pos[1] = pv[0]; // P + types[1] = (int) TOUCHEDGE; + pos[2] = pu[0]; // [A, B] + pos[3] = pv[1]; // Q + } else { + if (s4 == 0) { + // Q = B, [P, Q] in [A, B] (-+-0). + types[0] = (int) TOUCHEDGE; + pos[0] = pu[0]; // [A, B] + pos[1] = pv[0]; // P + types[1] = (int) SHAREVERT; + pos[2] = pu[1]; // B + pos[3] = pv[1]; // Q + } else { // s4 < 0 + // [P, Q] overlaps [A, B] (-+--). + types[0] = (int) TOUCHEDGE; + pos[0] = pu[0]; // [A, B] + pos[1] = pv[0]; // P + types[1] = (int) ACROSSVERT; + pos[2] = pu[1]; // B + pos[3] = pv[0]; // [P, Q] + } + } + } else { // s2 == 0 + // P = B (#0##). + types[0] = (int) SHAREVERT; + pos[0] = pu[1]; // B + pos[1] = pv[0]; // P + types[1] = (int) DISJOINT; + } + } + } + } else { // s1 == 0 + // Q = A (0###). + types[0] = (int) SHAREVERT; + pos[0] = pu[0]; // A + pos[1] = pv[1]; // Q + types[1] = (int) DISJOINT; + } + } + + return 1; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// tri_edge_test() Triangle-edge intersection test. // +// // +// This routine takes a triangle T (with vertices A, B, C) and an edge E (P, // +// Q) in 3D, and tests if they intersect each other. Return 1 if they are // +// intersected, i.e., T \cap E is not empty, otherwise, return 0. // +// // +// If the point 'R' is not NULL, it lies strictly above the plane defined by // +// A, B, C. It is used in test when T and E are coplanar. // +// // +// If T1 and T2 intersect each other (return 1), they may intersect in diff- // +// erent ways. If 'level' > 0, their intersection type will be reported in // +// combinations of 'types' and 'pos'. // +// // +/////////////////////////////////////////////////////////////////////////////// + +int tetgenmesh::tri_edge_test(point A, point B, point C, point P, point Q, + point R, int level, int *types, int *pos) +{ + point U[3], V[3], Ptmp; + int pu[3], pv[3], itmp; + REAL sP, sQ, s1, s2, s3; + int z1; + + // Test the locations of P and Q with respect to ABC. + sP = orient3d(A, B, C, P); + sQ = orient3d(A, B, C, Q); + orient3dcount+=2; + + if (b->epsilon > 0) { + if ((sP != 0) && iscoplanar(A, B, C, P, sP)) sP = 0; + if ((sQ != 0) && iscoplanar(A, B, C, Q, sQ)) sQ = 0; + } + + if (b->verbose > 2) { + printf(" Tri-edge (%d %d %d)-(%d %d) (%c%c).\n", pointmark(A), + pointmark(B), pointmark(C), pointmark(P), pointmark(Q), + sP>0 ? '+' : (sP<0 ? '-' : '0'), sQ>0 ? '+' : (sQ<0 ? '-' : '0')); + } + triedgcount++; + + if (sP < 0) { + if (sQ < 0) { // (--) disjoint + return 0; + } else { + if (sQ > 0) { // (-+) + SETVECTOR3(U, A, B, C); + SETVECTOR3(V, P, Q, R); + SETVECTOR3(pu, 0, 1, 2); + SETVECTOR3(pv, 0, 1, 2); + z1 = 0; + } else { // (-0) + SETVECTOR3(U, A, B, C); + SETVECTOR3(V, P, Q, R); + SETVECTOR3(pu, 0, 1, 2); + SETVECTOR3(pv, 0, 1, 2); + z1 = 1; + } + } + } else { + if (sP > 0) { // (+-) + if (sQ < 0) { + SETVECTOR3(U, A, B, C); + SETVECTOR3(V, Q, P, R); // P and Q are flipped. + SETVECTOR3(pu, 0, 1, 2); + SETVECTOR3(pv, 1, 0, 2); + z1 = 0; + } else { + if (sQ > 0) { // (++) disjoint + return 0; + } else { // (+0) + SETVECTOR3(U, B, A, C); // A and B are flipped. + SETVECTOR3(V, P, Q, R); + SETVECTOR3(pu, 1, 0, 2); + SETVECTOR3(pv, 0, 1, 2); + z1 = 1; + } + } + } else { // sP == 0 + if (sQ < 0) { // (0-) + SETVECTOR3(U, A, B, C); + SETVECTOR3(V, Q, P, R); // P and Q are flipped. + SETVECTOR3(pu, 0, 1, 2); + SETVECTOR3(pv, 1, 0, 2); + z1 = 1; + } else { + if (sQ > 0) { // (0+) + SETVECTOR3(U, B, A, C); // A and B are flipped. + SETVECTOR3(V, Q, P, R); // P and Q are flipped. + SETVECTOR3(pu, 1, 0, 2); + SETVECTOR3(pv, 1, 0, 2); + z1 = 1; + } else { // (00) + // A, B, C, P, and Q are coplanar. + z1 = 2; + } + } + } + } + + if (z1 == 2) { + // The triangle and the edge are coplanar. + return tri_edge_2d(A, B, C, P, Q, R, level, types, pos); + } + + s1 = orient3d(U[0], U[1], V[0], V[1]); orient3dcount++; + if (b->epsilon) { + if ((s1 != 0) && iscoplanar(U[0], U[1], V[0], V[1], s1)) s1 = 0; + } + if (s1 < 0) { + return 0; + } + + s2 = orient3d(U[1], U[2], V[0], V[1]); orient3dcount++; + if (b->epsilon) { + if ((s2 != 0) && iscoplanar(U[1], U[2], V[0], V[1], s2)) s2 = 0; + } + if (s2 < 0) { + return 0; + } + + s3 = orient3d(U[2], U[0], V[0], V[1]); orient3dcount++; + if (b->epsilon) { + if ((s3 != 0) && iscoplanar(U[2], U[0], V[0], V[1], s3)) s3 = 0; + } + if (s3 < 0) { + return 0; + } + + if (b->verbose > 2) { + printf(" Tri-edge (%d %d %d)-(%d %d) (%c%c%c).\n", pointmark(U[0]), + pointmark(U[1]), pointmark(U[2]), pointmark(V[0]), pointmark(V[1]), + s1>0 ? '+' : (s1<0 ? '-' : '0'), s2>0 ? '+' : (s2<0 ? '-' : '0'), + s3>0 ? '+' : (s3<0 ? '-' : '0')); + } + + if (level == 0) { + return 1; // The are intersected. + } + + types[1] = (int) DISJOINT; // No second intersection point. + + if (z1 == 0) { + if (s1 > 0) { + if (s2 > 0) { + if (s3 > 0) { // (+++) + // [P, Q] passes interior of [A, B, C]. + types[0] = (int) ACROSSFACE; + pos[0] = 3; // interior of [A, B, C] + pos[1] = 0; // [P, Q] + } else { // s3 == 0 (++0) + // [P, Q] intersects [C, A]. + types[0] = (int) ACROSSEDGE; + pos[0] = pu[2]; // [C, A] + pos[1] = 0; // [P, Q] + } + } else { // s2 == 0 + if (s3 > 0) { // (+0+) + // [P, Q] intersects [B, C]. + types[0] = (int) ACROSSEDGE; + pos[0] = pu[1]; // [B, C] + pos[1] = 0; // [P, Q] + } else { // s3 == 0 (+00) + // [P, Q] passes C. + types[0] = (int) ACROSSVERT; + pos[0] = pu[2]; // C + pos[1] = 0; // [P, Q] + } + } + } else { // s1 == 0 + if (s2 > 0) { + if (s3 > 0) { // (0++) + // [P, Q] intersects [A, B]. + types[0] = (int) ACROSSEDGE; + pos[0] = pu[0]; // [A, B] + pos[1] = 0; // [P, Q] + } else { // s3 == 0 (0+0) + // [P, Q] passes A. + types[0] = (int) ACROSSVERT; + pos[0] = pu[0]; // A + pos[1] = 0; // [P, Q] + } + } else { // s2 == 0 + if (s3 > 0) { // (00+) + // [P, Q] passes B. + types[0] = (int) ACROSSVERT; + pos[0] = pu[1]; // B + pos[1] = 0; // [P, Q] + } else { // s3 == 0 (000) + // Impossible. + assert(0); + } + } + } + } else { // z1 == 1 + if (s1 > 0) { + if (s2 > 0) { + if (s3 > 0) { // (+++) + // Q lies in [A, B, C]. + types[0] = (int) TOUCHFACE; + pos[0] = 0; // [A, B, C] + pos[1] = pv[1]; // Q + } else { // s3 == 0 (++0) + // Q lies on [C, A]. + types[0] = (int) TOUCHEDGE; + pos[0] = pu[2]; // [C, A] + pos[1] = pv[1]; // Q + } + } else { // s2 == 0 + if (s3 > 0) { // (+0+) + // Q lies on [B, C]. + types[0] = (int) TOUCHEDGE; + pos[0] = pu[1]; // [B, C] + pos[1] = pv[1]; // Q + } else { // s3 == 0 (+00) + // Q = C. + types[0] = (int) SHAREVERT; + pos[0] = pu[2]; // C + pos[1] = pv[1]; // Q + } + } + } else { // s1 == 0 + if (s2 > 0) { + if (s3 > 0) { // (0++) + // Q lies on [A, B]. + types[0] = (int) TOUCHEDGE; + pos[0] = pu[0]; // [A, B] + pos[1] = pv[1]; // Q + } else { // s3 == 0 (0+0) + // Q = A. + types[0] = (int) SHAREVERT; + pos[0] = pu[0]; // A + pos[1] = pv[1]; // Q + } + } else { // s2 == 0 + if (s3 > 0) { // (00+) + // Q = B. + types[0] = (int) SHAREVERT; + pos[0] = pu[1]; // B + pos[1] = pv[1]; // Q + } else { // s3 == 0 (000) + // Impossible. + assert(0); + } + } + } + } + + return 1; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// tri_tri_2d() Triangle-triangle coplanar intersection test. // +// // +// This routine takes two triangles T1 (with vertices A, B, C) and T2 (with // +// vertices P, Q, R) in 3D, and T1 and T2 are coplanar, and tests if they // +// intersect each other. Return 1 if they intersect, ie., T1 \cap T2 is not // +// empty, otherwise, return 0. // +// // +// If 'O' is not NULL, it lies strictly above A, B, C. // +// // +// If T1 and T2 intersect (return 1) and if 'level' > 0, // +// // +/////////////////////////////////////////////////////////////////////////////// + +int tetgenmesh::tri_tri_2d(point A, point B, point C, point P, point Q, + point R, point O, int level, int *types, int *pos) +{ + point U[3], V[3]; + int pu[3], pv[3], pe[3]; + REAL s1, s2, s3, s4; + REAL s5, s6 ,s7, s8, s9; + int z1; + + if (O == NULL) { + REAL n[3], len; + // Calculate a lift point, saved in dummypoint. + facenormal(A, B, C, n, 1); + len = sqrt(DOT(n, n)); + n[0] /= len; + n[1] /= len; + n[2] /= len; + len = DIST(A, B); + len += DIST(B, C); + len += DIST(C, A); + len /= 3.0; + O = dummypoint; + O[0] = A[0] + len * n[0]; + O[1] = A[1] + len * n[1]; + O[2] = A[2] + len * n[2]; + } + + s1 = orient3d(A, B, O, P); + s2 = orient3d(B, C, O, P); + s3 = orient3d(C, A, O, P); + orient3dcount+=3; + + if (b->epsilon) { + if ((s1 != 0) && iscoplanar(A, B, O, P, s1)) s1 = 0; + if ((s2 != 0) && iscoplanar(B, C, O, P, s2)) s2 = 0; + if ((s3 != 0) && iscoplanar(C, A, O, P, s3)) s3 = 0; + } + + if (b->verbose > 2) { + printf(" Tri-tri-2d (%d %d %d)-(%d %d %d) (%c%c%c)\n", pointmark(A), + pointmark(B), pointmark(C), pointmark(P), pointmark(Q), pointmark(R), + s1>0 ? '+' : (s1 < 0 ? '-' : '0'), s2>0 ? '+' : (s2<0 ? '-' : '0'), + s3>0 ? '+' : (s3<0 ? '-' : '0')); + } + + if (s1 < 0) { + if (s2 < 0) { + if (s3 < 0) { // (---) + assert(0); // Not possible. + } else { + if (s3 > 0) { // (--+) + SETVECTOR3(U, B, C, A); // PT = ST x ST + SETVECTOR3(pu, 1, 2, 0); + z1 = 2; + } else { // (--0) + assert(0); // Not possible. + } + } + } else { + if (s2 > 0) { + if (s3 < 0) { // (-+-) + SETVECTOR3(U, A, B, C); // I3 + SETVECTOR3(pu, 0, 1, 2); + z1 = 2; + } else { + if (s3 > 0) { // (-++) + SETVECTOR3(U, A, B, C); // I3 + SETVECTOR3(pu, 0, 1, 2); + z1 = 1; + } else { // (-+0) + SETVECTOR3(U, A, B, C); // I3 + SETVECTOR3(pu, 0, 1, 2); + z1 = 4; + } + } + } else { // s2 == 0 + if (s3 < 0) { //(-0-) + assert(0); // Not possible + } else { + if (s3 > 0) { // (-0+) + SETVECTOR3(U, B, C, A); // PT = ST x ST + SETVECTOR3(pu, 1, 2, 0); + z1 = 3; + } else { // (-00) + assert(0); // Not possible + } + } + } + } + } else { + if (s1 > 0) { + if (s2 < 0) { + if (s3 < 0) { // (+--) + SETVECTOR3(U, C, A, B); // PT = ST + SETVECTOR3(pu, 2, 0, 1); + z1 = 2; + } else { + if (s3 > 0) { // (+-+) + SETVECTOR3(U, B, C, A); // PT = ST x ST + SETVECTOR3(pu, 1, 2, 0); + z1 = 1; + } else { // (+-0) + SETVECTOR3(U, C, A, B); // PT = ST + SETVECTOR3(pu, 2, 0, 1); + z1 = 3; + } + } + } else { + if (s2 > 0) { + if (s3 < 0) { // (++-) + SETVECTOR3(U, C, A, B); // PT = ST + SETVECTOR3(pu, 2, 0, 1); + z1 = 1; + } else { + if (s3 > 0) { // (+++) + SETVECTOR3(U, A, B, C); // I3 + SETVECTOR3(pu, 0, 1, 2); + z1 = 7; + } else { // (++0) + SETVECTOR3(U, C, A, B); // PT = ST + SETVECTOR3(pu, 2, 0, 1); + z1 = 5; + } + } + } else { // s2 == 0 + if (s3 < 0) { // (+0-) + SETVECTOR3(U, B, C, A); // PT = ST x ST + SETVECTOR3(pu, 1, 2, 0); + z1 = 4; + } else { + if (s3 > 0) { // (+0+) + SETVECTOR3(U, B, C, A); // PT = ST x ST + SETVECTOR3(pu, 1, 2, 0); + z1 = 5; + } else { // (+00) + SETVECTOR3(U, C, A, B); // PT = ST + SETVECTOR3(pu, 2, 0, 1); + z1 = 6; + } + } + } + } + } else { // s1 == 0 + if (s2 < 0) { + if (s3 < 0) { // (0--) + assert(0); // Not possible + } else { + if (s3 > 0) { // (0-+) + SETVECTOR3(U, B, C, A); // PT = ST x ST + SETVECTOR3(pu, 1, 2, 0); + z1 = 4; + } else { // (0-0) + assert(0); // Not possible + } + } + } else { + if (s2 > 0) { + if (s3 < 0) { // (0+-) + SETVECTOR3(U, A, B, C); // I3 + SETVECTOR3(pu, 0, 1, 2); + z1 = 3; + } else { + if (s3 > 0) { // (0++) + SETVECTOR3(U, A, B, C); // I3 + SETVECTOR3(pu, 0, 1, 2); + z1 = 5; + } else { // (0+0) + SETVECTOR3(U, A, B, C); // I3 + SETVECTOR3(pu, 0, 1, 2); + z1 = 6; + } + } + } else { // s2 == 0 + if (s3 < 0) { // (00-) + assert(0); // Not possible + } else { + if (s3 > 0) { // (00+) + SETVECTOR3(U, B, C, A); // PT = ST x ST + SETVECTOR3(pu, 1, 2, 0); + z1 = 6; + } else { // (000) + assert(0); // Not possible + } + } + } + } + } + } + + if (b->verbose > 2) { + printf(" Tri-tri-2d (%d %d %d)-(%d) z1(%d)\n", pointmark(U[0]), + pointmark(U[1]), pointmark(U[2]), pointmark(P), z1); + } + + if (z1 == 5) { // P in [A, B] + if (level > 0) { + s4 = orient3d(U[0], U[1], O, V[1]); // A, B, Q + if (s4 < 0) { + s5 = orient3d(U[0], U[1], O, V[2]); // A, B, R + if (s5 < 0) { + // P touches [A, B] + types[0] = (int) TOUCHEDGE; + pos[0] = pu[0]; // [A, B] + pos[1] = 0; // P + types[1] = (int) DISJOINT; + } else { // s5 >= 0 + // [R, P] intersects [A, B, C] + types[0] = (int) EDGETRIINT; + pos[0] = 3; // [A, B, C] + pos[1] = 2; // [R, P] + types[1] = (int) DISJOINT; + } + } else { // s4 >= 0 + // [P, Q] intersects [A, B, C] + types[0] = (int) EDGETRIINT; + pos[0] = 3; // [A, B, C] + pos[1] = 0; // [P, Q] + types[1] = (int) DISJOINT; + } + } + return 1; + } + + if (z1 == 7) { // P in [A, B, C] + if (level > 0) { + // [P, Q] intersects [A, B, C] + types[0] = (int) EDGETRIINT; + pos[0] = 3; // [A, B, C] + pos[1] = 0; // [P, Q] + types[1] = (int) DISJOINT; + } + return 1; + } + + // Orient P, Q, R to be CCW wrt O, keep P as origin + s4 = orient3d(P, Q, R, O); + orient3dcount++; + if (b->epsilon) { + if ((s4 != 0) && iscoplanar(P, Q, R, O, s4)) s4 = 0; + } + + assert(s4 != 0); + if (s4 < 0) { + SETVECTOR3(V, P, Q, R); + SETVECTOR3(pv, 0, 1, 2); + SETVECTOR3(pe, 0, 1, 2); + } else { + SETVECTOR3(V, P, R, Q); + SETVECTOR3(pv, 0, 2, 1); + SETVECTOR3(pe, 2, 1, 0); + } + + //////////////////////////// z1 == 1 /////////////////////////////////////// + + if (z1 == 1) { + + s5 = orient3d(U[0], U[1], O, V[1]); // A, B, Q + if (s5 < 0) { + s6 = orient3d(U[0], U[1], O, V[2]); // A, B, R + if (s6 < 0) { + return 0; + } else { // s6 >= 0 + s7 = orient3d(V[1], V[2], O, U[0]); // Q, R, A + if (s7 < 0) { + return 0; + } else { // s7 >= 0 + s8 = orient3d(V[0], U[1], O, V[2]); // P, B, R + if (s8 >= 0) { + // z2 = 1; + } else { + return 0; + } + // NOTE: in the reference, the above two cases are reversed. + // It should be an error by the authors. + } + } + } else { // s5 >= 0 + s6 = orient3d(U[0], V[0], O, V[1]); // A, P, Q + if (s6 < 0) { + return 0; + } else { // s6 >= 0 + s7 = orient3d(V[0], U[1], O, V[1]); // P, B, Q + if (s7 < 0) { + s8 = orient3d(V[0], U[1], O, V[2]); // P, B, R + if (s8 < 0) { + return 0; + } else { // s8 >= 0 + s9 = orient3d(V[1], V[2], O, U[1]); // Q, R, B + if (s9 < 0) { + return 0; + } else { // s9 >= 0 + // z2 = 2; + } + } + } else { // s7 >= 0 + // z2 = 3; + } + } + } + + if (level == 0) { + return 1; + } + + if (s5 < 0) { + if (s6 > 0) { // (tritri2d-R1-1) + if (s7 > 0) { + if (s8 > 0) { + // [A, B] intersects [P, Q, R] + types[0] = (int) EDGETRIINT; + pos[0] = pu[0]; // [A, B] + pos[1] = 3; // [P, Q, R] + types[1] = (int) DISJOINT; + } else { // s8 == 0 + // [R, P] passes B + types[0] = (int) ACROSSVERT; + pos[0] = pu[1]; // B + pos[1] = pe[2]; // [R, P] + types[1] = (int) DISJOINT; + } + } else { // s7 == 0 + assert(s8 > 0); + // [Q, R] passes A. + types[0] = (int) ACROSSVERT; + pos[0] = pu[0]; // A + pos[1] = pe[1]; // [Q, R] + types[1] = (int) DISJOINT; + } + } else { // s6 == 0 // (tritri2d-R1-1a) + if (s7 > 0) { + if (s8 > 0) { + // R touches [A, B] + types[0] = (int) TOUCHEDGE; + pos[0] = pu[0]; // [A, B] + pos[1] = pv[2]; // R + types[1] = (int) DISJOINT; + } else { // s8 == 0 + // R = B + types[0] = (int) SHAREVERT; + pos[0] = pu[1]; // B + pos[1] = pv[2]; // R + types[1] = (int) DISJOINT; + } + } else { // s7 == 0 + assert(s8 > 0); + // R = A + types[0] = (int) SHAREVERT; + pos[0] = pu[0]; // A + pos[1] = pv[2]; // R + types[1] = (int) DISJOINT; + } + } + } else { // s5 >= 0 + if (s5 > 0) { + if (s6 > 0) { + if (s7 < 0) { // (tritri2d-R1-2) + if (s8 > 0) { + if (s9 > 0) { + // [A, B] intersects [P, Q, R] + types[0] = (int) EDGETRIINT; + pos[0] = pu[0]; // [A, B] + pos[1] = 3; // [P, Q, R] + types[1] = (int) DISJOINT; + } else { // s9 == 0 + // [Q, R] passes B + types[0] = (int) ACROSSVERT; + pos[0] = pu[1]; // B + pos[1] = pe[1]; // [Q, R] + types[1] = (int) DISJOINT; + } + } else { // s8 == 0 + if (s9 > 0) { + // [R, P] passes B + types[0] = (int) ACROSSVERT; + pos[0] = pu[1]; // B + pos[1] = pe[2]; // [R, P] + types[1] = (int) DISJOINT; + } else { // s9 == 0 + // R = B + types[0] = (int) SHAREVERT; + pos[0] = pu[1]; // B + pos[1] = pv[2]; // R + types[1] = (int) DISJOINT; + } + } + } else { // s7 >= 0 (tritri2d-R1-3) + if (s7 > 0) { + // [P, Q] intersects [A, B, C] + // [A, B] intersects [P, Q, R] + types[0] = (int) EDGETRIINT; + pos[0] = pu[0]; // [A, B] + pos[1] = 3; // [P, Q, R] + types[1] = (int) DISJOINT; + } else { // s7 == 0 + // [P, Q] passes B + // [A, B] intersects [P, Q, R] + types[0] = (int) EDGETRIINT; + pos[0] = pu[0]; // [A, B] + pos[1] = 3; // [P, Q, R] + types[1] = (int) DISJOINT; + } + } + } else { // s6 == 0 (tritri2d-R1-3 bottom) + // [P, Q] passes A + types[0] = (int) ACROSSVERT; + pos[0] = pu[0]; // A + pos[1] = pe[0]; // [P, Q] + types[1] = (int) DISJOINT; + } + } else { // s5 == 0 + if (s6 > 0) { + if (s7 < 0) { // (tritri2d-R1-2a) + if (s8 > 0) { + assert(s9 > 0); + // [A, B] intersects [P, Q, R] + types[0] = (int) EDGETRIINT; + pos[0] = pu[0]; // [A, B] + pos[1] = 3; // [P, Q, R] + types[1] = (int) DISJOINT; + } else { // s8 == 0 + assert(s9 > 0); + // [R, P] passes B + types[0] = (int) ACROSSVERT; + pos[0] = pu[1]; // B + pos[1] = pe[2]; // [R, P] + types[1] = (int) DISJOINT; + } + } else { // s7 >= 0 (tritri2d-R1-3a) + if (s7 > 0) { + // [A, B] intersects [P, Q, R] + types[0] = (int) EDGETRIINT; + pos[0] = pu[0]; // [A, B] + pos[1] = 3; // [P, Q, R] + types[1] = (int) DISJOINT; + } else { // s7 == 0 + s8 = orient3d(U[0], U[1], O, V[2]); // A, B, R + if (s8 < 0) { + // Q = B + types[0] = (int) SHAREVERT; + pos[0] = pu[1]; // B + pos[1] = pv[1]; // Q + types[1] = (int) DISJOINT; + } else { + if (s8 > 0) { + // [A, B] intersects [P, Q, R] + types[0] = (int) EDGETRIINT; + pos[0] = pu[0]; // [A, B] + pos[1] = 3; // [P, Q, R] + types[1] = (int) DISJOINT; + } else { // s8 == 0 + s9 = orient3d(U[0], V[0], O, V[2]); // A, P, R + if (s9 != 0) { + // [Q, R] intersects [A, B, C] + // [A, B] intersects [P, Q, R] + types[0] = (int) EDGETRIINT; + pos[0] = pu[0]; // [A, B] + pos[1] = 3; // [P, Q, R] + types[1] = (int) DISJOINT; + } else { // s9 == 0 + // [Q, R] = [A, B] + types[0] = (int) SHAREEDGE; + pos[0] = pu[0]; // [A, B] + pos[1] = pe[1]; // [Q, R] + types[1] = (int) DISJOINT; + } + } + } + } + } + } else { // s6 == 0 (tritri2d-R1-3b) + // Q = A + types[0] = (int) SHAREVERT; + pos[0] = pu[0]; // A + pos[1] = pv[1]; // Q + types[1] = (int) DISJOINT; + } + } + } + + return 1; + } // z1 == 1 + + //////////////////////////// z1 == 2, 3, 4 ///////////////////////////////// + + if ((z1 == 2) || (z1 == 3) || (z1 == 4)) { + + s5 = orient3d(U[0], U[1], O, V[1]); // A, B, Q + if (s5 < 0) { + s6 = orient3d(U[0], U[1], O, V[2]); // A, B, R + if (s6 < 0) { + return 0; + } else { // s6 >= 0 + s7 = orient3d(V[1], V[2], O, U[0]); // Q, R, A + if (s7 < 0) { + s8 = orient3d(V[1], V[2], O, U[2]); // Q, R, C + if (s8 < 0) { + return 0; + } else { + s9 = orient3d(U[2], U[0], O, V[2]); // C, A, R + if (s9 < 0) { + return 0; + } else { + // z2 = 1; + } + } + } else { // s7 >= 0 + s8 = orient3d(V[2], V[0], O, U[1]); // R, P, B + if (s8 < 0) { + return 0; + } else { + // z2 = 2; + } + } + } + } else { // s5 >= 0 + s6 = orient3d(U[2], U[0], O, V[1]); // C, A, Q + if (s6 < 0) { + s7 = orient3d(V[0], U[2], O, V[1]); // P, C, Q + if (s7 > 0) { + // Comments! To my analysis, it should be s7 >= 0. + // See fig. tritri2d-R2-3a + return 0; + } else { // s7 <= 0 + s8 = orient3d(U[2], U[0], O, V[2]); // C, A, R + if (s8 < 0) { + return 0; + } else { // s8 >= 0 + s9 = orient3d(V[1], V[2], O, U[2]); // Q, R, C + if (s9 < 0) { + return 0; + } else { + // z2 = 3; + } + } + } + } else { // s6 >= 0 + s7 = orient3d(V[0], U[1], O, V[1]); // P, B, Q + if (s7 < 0) { + s8 = orient3d(V[0], U[1], O, V[2]); // P, B, R + if (s8 < 0) { + return 0; + } else { // s8 >= 0 + s9 = orient3d(U[0], U[1], O, V[2]); // A, B, R + if (s9 < 0) { + return 0; + } else { + // z2 = 4; + } + } + } else { // s7 >= 0 + s8 = orient3d(V[0], U[2], O, V[1]); // P, C, Q + if (s8 > 0) { + return 0; + } else { // s8 <= 0 + // z2 = 5; + } + } + } + } + + if (level == 0) { + return 1; + } + + if (s5 < 0) { + if (s6 > 0) { + if (s7 < 0) { // (tritri2d-R2-1) + if (s8 > 0) { + if (s9 > 0) { // top-left + // [Q, R] intersects [A, B, C] + types[0] = (int) TRIEDGEINT; + pos[0] = 3; // [A, B, C] + pos[1] = pe[1]; // [Q, R] + types[1] = (int) DISJOINT; + } else { // s9 == 0 top-right + // R touches [C, A] + types[0] = (int) TOUCHEDGE; + pos[0] = pu[2]; // [C, A] + pos[1] = pv[2]; // R + types[1] = (int) DISJOINT; + } + } else { // s8 == 0 + if (s9 > 0) { // bot-left + // [Q, R] passes C. + types[0] = (int) ACROSSVERT; + pos[0] = pu[2]; // C + pos[1] = pe[1]; // [Q, R] + types[1] = (int) DISJOINT; + } else { // s9 == 0 bot-right + // R = C + types[0] = (int) SHAREVERT; + pos[0] = pu[2]; // C + pos[1] = pv[2]; // R + types[1] = (int) DISJOINT; + } + } + } else { // s7 >= 0 (tritri2d-R2-2) + if (s7 > 0) { + if (s8 > 0) { + // [A, B] intersects [P, Q, R] + types[0] = (int) EDGETRIINT; + pos[0] = pu[0]; // [A, B] + pos[1] = 3; // [P, Q, R] + types[1] = (int) DISJOINT; + } else { // s8 == 0 (s6 > 0) R != B (top-right) + if ((z1 == 2) || (z1 == 4)) { + // [R, P] passes B + types[0] = (int) ACROSSVERT; + pos[0] = pu[1]; // B + pos[1] = pe[2]; // [R, P] + types[1] = (int) DISJOINT; + } else { // z1 == 3 (tritri2d-R3-2) + // [R, P] intersects [A, B, C] + // [A, B] intersects [P, Q, R] + types[0] = (int) EDGETRIINT; + pos[0] = pu[0]; // [A, B] + pos[1] = 3; // [P, Q, R] + types[1] = (int) DISJOINT; + } + } + } else { // s7 == 0 (s6 > 0) R != A (bottom) + assert(s8 > 0); + s9 = orient3d(V[1], V[2], O, U[2]); // Q, R, C + // Note: if z1 == 4, it must be s9 < 0. + if (s9 < 0) { + // [Q, R] passes A + types[0] = (int) ACROSSVERT; + pos[0] = pu[0]; // A + pos[1] = pe[1]; // [Q, R] + types[1] = (int) DISJOINT; + } else { // s9 >= 0 + // [A, C] intersects [P, Q, R] + types[0] = (int) EDGETRIINT; + pos[0] = pu[2]; // [C, A] + pos[1] = 3; // [P, Q, R] + types[1] = (int) DISJOINT; + } + } + } + } else { // s6 == 0 (tritri2d-R2-2a) + assert(s7 >= 0); // s7 < 0 is not possible + if (s7 > 0) { + if ((z1 == 2) || (z1 == 4)) { + if (s8 > 0) { + // R touches [A, B] + types[0] = (int) TOUCHEDGE; + pos[0] = pu[0]; // [A, B] + pos[1] = pv[2]; // R + types[1] = (int) DISJOINT; + } else { // s8 == 0 + // R = B + types[0] = (int) SHAREVERT; + pos[0] = pu[1]; // B + pos[1] = pv[2]; // R + types[1] = (int) DISJOINT; + } + } else { // z1 == 3 (tritri2d-R3-2a) + // [R, P] intersects [A, B, C] + // [A, B] intersects [P, Q, R] + types[0] = (int) EDGETRIINT; + pos[0] = pu[0]; // [A, B] + pos[1] = 3; // [P, Q, R] + types[1] = (int) DISJOINT; + } + } else { // s7 == 0 + // R = A + types[0] = (int) SHAREVERT; + pos[0] = pu[0]; // A + pos[1] = pv[2]; // R + types[1] = (int) DISJOINT; + } + } + } else { // s5 >= 0 + if (s5 > 0) { + if (s6 < 0) { + if (s7 < 0) { // (tritri2d-R2-3) + if (s8 > 0) { + if (s9 > 0) { + // [C, A] intersects [P, Q, R] + types[0] = (int) EDGETRIINT; + pos[0] = pu[2]; // [C, A] + pos[1] = 3; // [P, Q, R] + types[1] = (int) DISJOINT; + } else { // s9 == 0 + // [Q, R] passes C + types[0] = (int) ACROSSVERT; + pos[0] = pu[2]; // C + pos[1] = pe[1]; // [Q, R] + types[1] = (int) DISJOINT; + } + } else { // s8 == 0 + if (s9 > 0) { + // R touches [C, A] + types[0] = (int) TOUCHEDGE; + pos[0] = pu[2]; // [C, A] + pos[1] = pv[2]; // R + types[1] = (int) DISJOINT; + } else { // s9 == 0 + // R = C + types[0] = (int) SHAREVERT; + pos[0] = pu[2]; // C + pos[1] = pv[2]; // R + types[1] = (int) DISJOINT; + } + } + } else { // s7 == 0 (see tritri2d-R2-3a) + // Not possible be intersecting! + assert(0); + } + } else { // s6 >= 0 + if (s6 > 0) { + if (s7 < 0) { // (tritri2d-R2-4) + if (s8 > 0) { // (top-left) + // [A, B] intersects [P, Q, R] + types[0] = (int) EDGETRIINT; + pos[0] = pu[0]; // [A, B] + pos[1] = 3; // [P, Q, R] + types[1] = (int) DISJOINT; + } else { // s8 == 0 + if (s9 > 0) { // (top-right) + // [R, P] passes B + types[0] = (int) ACROSSVERT; + pos[0] = pu[1]; // B + pos[1] = pe[2]; // [R, P] + types[1] = (int) DISJOINT; + } else { // s9 == 0 (bot-left) + // R = B + types[0] = (int) SHAREVERT; + pos[0] = pu[1]; // B + pos[1] = pv[2]; // R + types[1] = (int) DISJOINT; + } + } + } else { // s7 >= 0 + if (s7 > 0) { // (tritri2d-R2-5) + if (s8 < 0) { // (top-left) + // [P, Q] intersects [A, B, C] + types[0] = (int) TRIEDGEINT; + pos[0] = 3; // [A, B, C] + pos[1] = pe[0]; // [P, Q] + types[1] = (int) DISJOINT; + } else { // s8 == 0 (top-right) + if ((z1 == 2) || (z1 == 3)) { + // [P, Q] passes C + types[0] = (int) ACROSSVERT; + pos[0] = pu[2]; // C + pos[1] = pe[0]; // [P, Q] + types[1] = (int) DISJOINT; + } else { // z1 == 4 (tritri2d-R4-5) + // [P, Q] intersects [A, B, C] + // [C, A] intersects [P, Q, R] + types[0] = (int) EDGETRIINT; + pos[0] = pu[2]; // [C, A] + pos[1] = 3; // [P, Q, R] + types[1] = (int) DISJOINT; + } + } + } else { // s7 == 0 (bot-left) + // [A, B] intersects [P, Q, R] + types[0] = (int) EDGETRIINT; + pos[0] = pu[0]; // [A, B] + pos[1] = 3; // [P, Q, R] + types[1] = (int) DISJOINT; + } + } + } else { // s6 == 0 (tritri2d-R2-5a) + if ((z1 == 2) || (z1 == 3)) { + if (s7 > 0) { + if (s8 < 0) { + // Q touches [C, A] + types[0] = (int) TOUCHEDGE; + pos[0] = pu[2]; // [C, A] + pos[1] = pv[1]; // Q + types[1] = (int) DISJOINT; + } else { // s8 == 0 + // Q = C + types[0] = (int) SHAREVERT; + pos[0] = pu[2]; // C + pos[1] = pv[1]; // Q + types[1] = (int) DISJOINT; + } + } else { // s7 == 0 + assert(0); // Not possible + } + } else { // z1 == 4 (tritri2d-R4-5a) + // [P, Q] intersects [A, B, C] + // [C, A] intersects [P, Q, R] + types[0] = (int) EDGETRIINT; + pos[0] = pu[2]; // [C, A] + pos[1] = 3; // [P, Q, R] + types[1] = (int) DISJOINT; + } + } + } + } else { // s5 == 0 + if (s6 < 0) { // (tritri2d-R2-3b) + if (s7 < 0) { + if (s8 > 0) { + if (s9 > 0) { + // [C, A] intersects [P, Q, R] + types[0] = (int) EDGETRIINT; + pos[0] = pu[2]; // [C, A] + pos[1] = 3; // [P, Q, R] + types[1] = (int) DISJOINT; + } else { // s9 == 0 + // [Q, R] passes C + types[0] = (int) ACROSSVERT; + pos[0] = pu[2]; // C + pos[1] = pe[1]; // [Q, R] + types[1] = (int) DISJOINT; + } + } else { // s8 == 0 + if (s9 > 0) { + // R touches [C, A] + types[0] = (int) TOUCHEDGE; + pos[0] = pu[2]; // [C, A] + pos[1] = pv[2]; // R + types[1] = (int) DISJOINT; + } else { // s9 == 0 + // R = C + types[0] = (int) SHAREVERT; + pos[0] = pu[2]; // C + pos[1] = pv[2]; // R + types[1] = (int) DISJOINT; + } + } + } else { // s7 == 0 (see tritri2d-R2-3c) + assert(0); // To my analysis should be disjoint. + } + } else { + if (s6 > 0) { + if (s7 < 0) { // (tritri2d-R2-4a) + if (s8 > 0) { + if (s9 > 0) { + // [A, B] intersects [P, Q, R] + types[0] = (int) EDGETRIINT; + pos[0] = pu[0]; // [A, B] + pos[1] = 3; // [P, Q, R] + types[1] = (int) DISJOINT; + } else { // s9 == 0 + // [A, B] intersects [P, Q, R] + types[0] = (int) EDGETRIINT; + pos[0] = pu[0]; // [A, B] + pos[1] = 3; // [P, Q, R] + types[1] = (int) DISJOINT; + } + } else { // s8 == 0 + if (s9 > 0) { + // [R, P] passes B + types[0] = (int) ACROSSVERT; + pos[0] = pu[1]; // B + pos[1] = pe[2]; // [R, P] + types[1] = (int) DISJOINT; + } else { // s9 == 0 + // R = B + types[0] = (int) SHAREVERT; + pos[0] = pu[1]; // B + pos[1] = pv[2]; // R + types[1] = (int) DISJOINT; + } + } + } else { // s7 >= 0 + if (s7 > 0) { + assert(0); // Not possible (see tritri2d-R2-5b) + } else { // s7 == 0 + if (z1 == 3) {// (tritri2d-R3-5b) + // [P, Q] intersects [A, B, C] + // [A, B] intersects [P, Q, R] + types[0] = (int) EDGETRIINT; + pos[0] = pu[0]; // [A, B] + pos[1] = 3; // [P, Q, R] + types[1] = (int) DISJOINT; + } else { // (tritri2d-R2-5b) + s8 = orient3d(U[0], U[1], O, V[2]); // A, B, R + if (s8 < 0) { + // Q = B + types[0] = (int) SHAREVERT; + pos[0] = pu[1]; // B + pos[1] = pv[1]; // Q + types[1] = (int) DISJOINT; + } else { + if (s8 > 0) { + // [A, B] intersect [P, Q, R] + types[0] = (int) EDGETRIINT; + pos[0] = pu[0]; // [A, B] + pos[1] = 3; // [P, Q, R] + types[1] = (int) DISJOINT; + } else { // s8 == 0 + s9 = orient3d(U[0], U[2], O, V[2]); // A, C, R + if (s9 == 0) { + // [A, B] = [Q, R] + types[0] = (int) SHAREEDGE; + pos[0] = pu[0]; // [A, B] + pos[1] = pe[2]; // [R, P] + types[1] = (int) DISJOINT; + } else { + // [P, Q] intersects [A, B, C] + // [A, B] intersect [P, Q, R] + types[0] = (int) EDGETRIINT; + pos[0] = pu[0]; // [A, B] + pos[1] = 3; // [P, Q, R] + types[1] = (int) DISJOINT; + } + } + } + } + } + } + } else { // s6 == 0 (tritri2d-R2-6) + // Q = A. Note that R must above P, Q. + s7 = orient3d(V[1], V[2], O, U[2]); // Q, R, C + // Note: if z1 == 4, it must be s7 > 0 + if (s7 < 0) { + // [Q, R] intersects [A, B, C] + types[0] = (int) EDGETRIINT; + pos[0] = 3; // [A, B, C] + pos[1] = pe[1]; // [Q, R] + types[1] = (int) DISJOINT; + } else { + if (s7 > 0) { + // Q = A + types[0] = (int) SHAREVERT; + pos[0] = pu[0]; // A + pos[1] = pv[1]; // Q + types[1] = (int) DISJOINT; + } else { // s7 == 0 + s8 = orient3d(V[0], V[2], O, U[2]); // P, R, C + if (s8 == 0) { + // [Q, R] = [A, C] + types[0] = (int) SHAREEDGE; + pos[0] = pu[2]; // [C, A] + pos[1] = pe[1]; // [Q, R] + types[1] = (int) DISJOINT; + } else { // s8 > 0 || s8 < 0 + // [A, C] intersects [P, Q, R] + types[0] = (int) EDGETRIINT; + pos[0] = pu[2]; // [C, A] + pos[1] = 3; // [P, Q, R] + types[1] = (int) DISJOINT; + } + } + } + } + } + } + } + + return 1; + } // z1 == 2 || z1 == 3 || z1 == 4 + + if (z1 == 6) { // P = A + + if (level > 0) { + s5 = orient3d(U[0], U[1], O, V[1]); // A, B, Q + if (s5 < 0) { + s6 = orient3d(U[0], U[1], O, V[2]); // A, B, R + if (s6 < 0) { // (tritri2d-R6--) + // P = A + types[0] = (int) SHAREVERT; + pos[0] = pu[0]; // A + pos[1] = pv[0]; // P + types[1] = (int) DISJOINT; + } else { + if (s6 > 0) { // (tritri2d-R6-+) + // Note: it must be orient3d(Q, R, A) > 0. + // [P, Q] intersects [A, B, C] + types[0] = (int) TRIEDGEINT; + pos[0] = 3; // [A, B, C] + pos[1] = pe[0]; // [P, Q] + types[1] = (int) DISJOINT; + } else { // s6 == 0 (tritri2d-R6-0) + // Note: it must be orient3d(Q, R, A) > 0. + s7 = orient3d(V[1], V[2], O, U[1]); // Q, R, B + if (s7 == 0) { + // [R, P] = [A, B] + types[0] = (int) SHAREEDGE; + pos[0] = pu[0]; // [A, B] + pos[1] = pe[2]; // [R, P] + types[1] = (int) DISJOINT; + } else { + // [R, P] intersects [A, B, C] + // [A, B] intersects [P, Q, R] + types[0] = (int) EDGETRIINT; + pos[0] = pu[0]; // [A, B] + pos[1] = 3; // [P, Q, R] + types[1] = (int) DISJOINT; + } + } + } + } else { // s5 >= 0 + if (s5 > 0) { + s6 = orient3d(U[0], U[2], O, V[1]); // A, C, Q + if (s6 < 0) { // (tritri2d-R6+-) + // [P, Q] intersects [A, B, C] + types[0] = (int) TRIEDGEINT; + pos[0] = 3; // [A, B, C] + pos[1] = pe[0]; // [P, Q] + types[1] = (int) DISJOINT; + } else { // s6 >= 0 + if (s6 > 0) { // (tritri2d-R6++) (not draw) + // P = A + types[0] = (int) SHAREVERT; + pos[0] = pu[0]; // A + pos[1] = pv[0]; // P + types[1] = (int) DISJOINT; + } else { // s6 == 0 // (tritri2d-R6+0) + s7 = orient3d(V[1], V[2], O, U[2]); // Q, R, O, C + if (s7 == 0) { + // [P, Q] = [C, A] + types[0] = (int) SHAREEDGE; + pos[0] = pu[2]; // [C, A] + pos[1] = pe[0]; // [P, Q] + types[1] = (int) DISJOINT; + } else { + // [P, Q] intersects [A, B, C] + // [A, C] intersects [P, Q, R] + types[0] = (int) TRIEDGEINT; + pos[0] = 3; // [A, B, C] + pos[1] = pe[0]; // [P, Q] + types[1] = (int) DISJOINT; + } + } + } + } else { // s5 == 0 + s6 = orient3d(V[1], V[2], O, U[1]); // Q, R, B + if (s6 == 0) { + // [P, Q] = [A, B] + types[0] = (int) SHAREEDGE; + pos[0] = pu[0]; // [A, B] + pos[1] = pe[0]; // [P, Q] + types[1] = (int) DISJOINT; + } else { + // [P, Q] intersects [A, B, C] + // [A, B] intersects [P, Q, R] + types[0] = (int) TRIEDGEINT; + pos[0] = 3; // [A, B, C] + pos[1] = pe[0]; // [P, Q] + types[1] = (int) DISJOINT; + } + } + } + } // if (level > 0) + + return 1; + } // z1 == 6 +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// tri_tri_test() Triangle-triangle intersection test. // +// // +// This routine takes two triangles T1 (with vertices A, B, C) and T2 (P, Q // +// R) in 3D, and tests if they intersect each other. Return 1 if they are // +// intersected, i.e., T1 \cap T2 is not empty, otherwise, return 0. // +// // +// If the point 'O' is not NULL, it lies strictly above the plane defined by // +// A, B, C. It is used in test when T1 and T2 are coplanar. // +// // +// If T1 and T2 intersect each other (return 1), they may intersect in diff- // +// erent ways. If 'level' > 0, their intersection type will be reported in // +// combinations of 'types' and 'pos'. // +// // +/////////////////////////////////////////////////////////////////////////////// + +int tetgenmesh::tri_tri_test(point A, point B, point C, point P, point Q, + point R, point O, int level, int *types, int *pos) +{ + point U[3], V[3], W[3], Ptmp; // The permuted vectors of points. + int pu[3], pv[3], pw[3], iu, iv, itmp; + REAL sA, sB, sC, sP, sQ, sR; + REAL s1, s2, s3, s4; + int z1, z2; + + // Test A's, B's, and C's orientations wrt plane PQR. + sA = orient3d(P, Q, R, A); + sB = orient3d(P, Q, R, B); + sC = orient3d(P, Q, R, C); + orient3dcount+=3; + + if (b->epsilon) { + if ((sA != 0) && iscoplanar(P, Q, R, A, sA)) sA = 0; + if ((sB != 0) && iscoplanar(P, Q, R, B, sB)) sB = 0; + if ((sC != 0) && iscoplanar(P, Q, R, C, sC)) sC = 0; + } + + if (b->verbose > 2) { + printf(" Tri-tri (%d %d %d)-(%d %d %d) (%c%c%c)\n", pointmark(A), + pointmark(B), pointmark(C), pointmark(P), pointmark(Q), pointmark(R), + sA > 0 ? '+' : (sA < 0 ? '-' : '0'), sB>0 ? '+' : (sB<0 ? '-' : '0'), + sC>0 ? '+' : (sC<0 ? '-' : '0')); + } + // tritricount++; + iu = iv = 0; + + if (sA < 0) { + if (sB < 0) { + if (sC < 0) { // (---). + return DISJOINT; + } else { + if (sC > 0) { // (--+). + // All points are in the right positions. + SETVECTOR3(U, A, B, C); // I3 + SETVECTOR3(V, P, Q, R); // I2 + SETVECTOR3(pu, 0, 1, 2); + SETVECTOR3(pv, 0, 1, 2); + z1 = 0; + } else { // (--0). + SETVECTOR3(U, A, B, C); // I3 + SETVECTOR3(V, P, Q, R); // I2 + SETVECTOR3(pu, 0, 1, 2); + SETVECTOR3(pv, 0, 1, 2); + z1 = 1; + } + } + } else { + if (sB > 0) { + if (sC < 0) { // (-+-). + SETVECTOR3(U, C, A, B); // PT = ST + SETVECTOR3(V, P, Q, R); // I2 + SETVECTOR3(pu, 2, 0, 1); + SETVECTOR3(pv, 0, 1, 2); + z1 = 0; + } else { + if (sC > 0) { // (-++). + SETVECTOR3(U, B, C, A); // PT = ST x ST + SETVECTOR3(V, Q, P, R); // PL = SL + SETVECTOR3(pu, 1, 2, 0); + SETVECTOR3(pv, 1, 0, 2); iv = 1; + z1 = 0; + } else { // (-+0). + SETVECTOR3(U, C, A, B); // PT = ST + SETVECTOR3(V, P, Q, R); // I2 + SETVECTOR3(pu, 2, 0, 1); + SETVECTOR3(pv, 0, 1, 2); + z1 = 2; + } + } + } else { + if (sC < 0) { // (-0-). + SETVECTOR3(U, C, A, B); // PT = ST + SETVECTOR3(V, P, Q, R); // I2 + SETVECTOR3(pu, 2, 0, 1); + SETVECTOR3(pv, 0, 1, 2); + z1 = 1; + } else { + if (sC > 0) { // (-0+). + SETVECTOR3(U, B, C, A); // PT = ST x ST + SETVECTOR3(V, Q, P, R); // PL = SL + SETVECTOR3(pu, 1, 2, 0); + SETVECTOR3(pv, 1, 0, 2); iv = 1; + z1 = 2; + } else { // (-00). + SETVECTOR3(U, B, C, A); // PT = ST x ST + SETVECTOR3(V, Q, P, R); // PL = SL + SETVECTOR3(pu, 1, 2, 0); + SETVECTOR3(pv, 1, 0, 2); iv = 1; + z1 = 3; + } + } + } + } + } else { + if (sA > 0) { + if (sB < 0) { + if (sC < 0) { // (+--). + SETVECTOR3(U, B, C, A); // PT = ST x ST + SETVECTOR3(V, P, Q, R); // I2 + SETVECTOR3(pu, 1, 2, 0); + SETVECTOR3(pv, 0, 1, 2); + z1 = 0; + } else { + if (sC > 0) { // (+-+). + SETVECTOR3(U, C, A, B); // PT = ST + SETVECTOR3(V, Q, P, R); // PL = SL + SETVECTOR3(pu, 2, 0, 1); + SETVECTOR3(pv, 1, 0, 2); iv = 1; + z1 = 0; + } else { // (+-0). + SETVECTOR3(U, C, A, B); // PT = ST + SETVECTOR3(V, Q, P, R); // PL = SL + SETVECTOR3(pu, 2, 0, 1); + SETVECTOR3(pv, 1, 0, 2); iv = 1; + z1 = 2; + } + } + } else { + if (sB > 0) { + if (sC < 0) { // (++-). + SETVECTOR3(U, A, B, C); // I3 + SETVECTOR3(V, Q, P, R); // PL = SL + SETVECTOR3(pu, 0, 1, 2); + SETVECTOR3(pv, 1, 0, 2); iv = 1; + z1 = 0; + } else { + if (sC > 0) { // (+++). + return DISJOINT; + } else { // (++0). + SETVECTOR3(U, A, B, C); // I3 + SETVECTOR3(V, Q, P, R); // PL = SL + SETVECTOR3(pu, 0, 1, 2); + SETVECTOR3(pv, 1, 0, 2); iv = 1; + z1 = 1; + } + } + } else { + if (sC < 0) { // (+0-). + SETVECTOR3(U, B, C, A); // PT = ST x ST + SETVECTOR3(V, P, Q, R); // I2 + SETVECTOR3(pu, 1, 2, 0); + SETVECTOR3(pv, 0, 1, 2); + z1 = 2; + } else { + if (sC > 0) { // (+0+). + SETVECTOR3(U, C, A, B); // PT = ST + SETVECTOR3(V, Q, P, R); // PL = SL + SETVECTOR3(pu, 2, 0, 1); + SETVECTOR3(pv, 1, 0, 2); iv = 1; + z1 = 1; + } else { // (+00). + SETVECTOR3(U, B, C, A); // PT = ST x ST + SETVECTOR3(V, P, Q, R); // I2 + SETVECTOR3(pu, 1, 2, 0); + SETVECTOR3(pv, 0, 1, 2); + z1 = 3; + } + } + } + } + } else { + if (sB < 0) { + if (sC < 0) { // (0--). + SETVECTOR3(U, B, C, A); // PT = ST x ST + SETVECTOR3(V, P, Q, R); // PL = I2 + SETVECTOR3(pu, 1, 2, 0); + SETVECTOR3(pv, 0, 1, 2); + z1 = 1; + } else { + if (sC > 0) { // (0-+). + SETVECTOR3(U, A, B, C); // I3 + SETVECTOR3(V, P, Q, R); // I2 + SETVECTOR3(pu, 0, 1, 2); + SETVECTOR3(pv, 0, 1, 2); + z1 = 2; + } else { // (0-0). + SETVECTOR3(U, C, A, B); // PT = ST + SETVECTOR3(V, Q, P, R); // PL = SL + SETVECTOR3(pu, 2, 0, 1); + SETVECTOR3(pv, 1, 0, 2); iv = 1; + z1 = 3; + } + } + } else { + if (sB > 0) { + if (sC < 0) { // (0+-). + SETVECTOR3(U, A, B, C); // I3 + SETVECTOR3(V, Q, P, R); // PL = SL + SETVECTOR3(pu, 0, 1, 2); + SETVECTOR3(pv, 1, 0, 2); iv = 1; + z1 = 2; + } else { + if (sC > 0) { // (0++). + SETVECTOR3(U, B, C, A); // PT = ST x ST + SETVECTOR3(V, Q, P, R); // PL = SL + SETVECTOR3(pu, 1, 2, 0); + SETVECTOR3(pv, 1, 0, 2); iv = 1; + z1 = 1; + } else { // (0+0). + SETVECTOR3(U, C, A, B); // PT = ST + SETVECTOR3(V, P, Q, R); // I2 + SETVECTOR3(pu, 2, 0, 1); + SETVECTOR3(pv, 0, 1, 2); + z1 = 3; + } + } + } else { + if (sC < 0) { // (00-). + SETVECTOR3(U, A, B, C); // I3 + SETVECTOR3(V, Q, P, R); // PL = SL + SETVECTOR3(pu, 0, 1, 2); + SETVECTOR3(pv, 1, 0, 2); iv = 1; + z1 = 3; + } else { + if (sC > 0) { // (00+). + SETVECTOR3(U, A, B, C); // I3 + SETVECTOR3(V, P, Q, R); // I2 + SETVECTOR3(pu, 0, 1, 2); + SETVECTOR3(pv, 0, 1, 2); + z1 = 3; + } else { // (000) + // (A, B, C) is coplanar with (P, Q, R). + SETVECTOR3(U, A, B, C); // I3 + SETVECTOR3(V, P, Q, R); // I2 + SETVECTOR3(pu, 0, 1, 2); + SETVECTOR3(pv, 0, 1, 2); + z1 = -1; + } + } + } + } + } + } + + sP = orient3d(U[0], U[1], U[2], V[0]); + sQ = orient3d(U[0], U[1], U[2], V[1]); + sR = orient3d(U[0], U[1], U[2], V[2]); + orient3dcount+=3; + + if (b->epsilon) { + if ((sP != 0) && iscoplanar(U[0], U[1], U[2], V[0], sP)) sP = 0; + if ((sQ != 0) && iscoplanar(U[0], U[1], U[2], V[1], sQ)) sQ = 0; + if ((sR != 0) && iscoplanar(U[0], U[1], U[2], V[2], sR)) sR = 0; + } + + if (b->verbose > 2) { + printf(" Tri-tri (%d %d %d)-(%d %d %d) (%c%c%c)\n", pointmark(U[0]), + pointmark(U[1]), pointmark(U[2]), pointmark(V[0]), pointmark(V[1]), + pointmark(V[2]), sP>0 ? '+' : (sP<0 ? '-' : '0'), + sQ>0 ? '+' : (sQ<0 ? '-' : '0'), sR>0 ? '+' : (sR<0 ? '-' : '0')); + } + + if (sP < 0) { + if (sQ < 0) { + if (sR < 0) { // (---) + return DISJOINT; + } else { + if (sR > 0) { // (--+) + // P1->Q1 is opposite to A1->B1. Swicth A1 and B1. + SETVECTOR3(W, V[0], V[1], V[2]); // I3 + SWAP2(U[0], U[1], Ptmp); // PL = SL + SETVECTOR3(pw, pv[0], pv[1], pv[2]); + SWAP2(pu[0], pu[1], itmp); iu = 1; + z2 = 0; + } else { // (--0) + SETVECTOR3(W, V[0], V[1], V[2]); // I3 + SWAP2(U[0], U[1], Ptmp); // PL = SL + SETVECTOR3(pw, pv[0], pv[1], pv[2]); + SWAP2(pu[0], pu[1], itmp); iu = 1; + z2 = 1; + } + } + } else { + if (sQ > 0) { + if (sR < 0) { // (-+-) + SETVECTOR3(W, V[2], V[0], V[1]); // PT = ST + SWAP2(U[0], U[1], Ptmp); // PL = SL + SETVECTOR3(pw, pv[2], pv[0], pv[1]); + SWAP2(pu[0], pu[1], itmp); iu = 1; + z2 = 0; + } else { + if (sR > 0) { // (-++) + SETVECTOR3(W, V[1], V[2], V[0]); // PT = ST x ST + SETVECTOR3(pw, pv[1], pv[2], pv[0]); + z2 = 0; + } else { // (-+0) + SETVECTOR3(W, V[2], V[0], V[1]); // PT = ST + SWAP2(U[0], U[1], Ptmp); // PL = SL + SETVECTOR3(pw, pv[2], pv[0], pv[1]); + SWAP2(pu[0], pu[1], itmp); iu = 1; + z2 = 2; + } + } + } else { + if (sR < 0) { // (-0-) + SETVECTOR3(W, V[2], V[0], V[1]); // PT = ST + SWAP2(U[0], U[1], Ptmp); // PL = SL + SETVECTOR3(pw, pv[2], pv[0], pv[1]); + SWAP2(pu[0], pu[1], itmp); iu = 1; + z2 = 1; + } else { + if (sR > 0) { // (-0+) + SETVECTOR3(W, V[1], V[2], V[0]); // PT = ST x ST + SETVECTOR3(pw, pv[1], pv[2], pv[0]); + z2 = 2; + } else { // (-00) + SETVECTOR3(W, V[1], V[2], V[0]); // PT = ST x ST + SETVECTOR3(pw, pv[1], pv[2], pv[0]); + z2 = 3; + } + } + } + } + } else { + if (sP > 0) { + if (sQ < 0) { + if (sR < 0) { // (+--) + SETVECTOR3(W, V[1], V[2], V[0]); // PT = ST x ST + SWAP2(U[0], U[1], Ptmp); // PL = SL + SETVECTOR3(pw, pv[1], pv[2], pv[0]); + SWAP2(pu[0], pu[1], itmp); iu = 1; + z2 = 0; + } else { + if (sR > 0) { // (+-+) + SETVECTOR3(W, V[2], V[0], V[1]); // PT = ST + SETVECTOR3(pw, pv[2], pv[0], pv[1]); + z2 = 0; + } else { // (+-0) + SETVECTOR3(W, V[2], V[0], V[1]); // PT = ST + SETVECTOR3(pw, pv[2], pv[0], pv[1]); + z2 = 2; + } + } + } else { + if (sQ > 0) { + if (sR < 0) { // (++-) + SETVECTOR3(W, V[0], V[1], V[2]); // I3 + SETVECTOR3(pw, pv[0], pv[1], pv[2]); + z2 = 0; + } else { + if (sR > 0) { // (+++) + return DISJOINT; + } else { // (++0) + SETVECTOR3(W, V[0], V[1], V[2]); // I3 + SETVECTOR3(pw, pv[0], pv[1], pv[2]); + z2 = 1; + } + } + } else { // sQ == 0 + if (sR < 0) { // (+0-) + SETVECTOR3(W, V[1], V[2], V[0]); // PT = ST x ST + SWAP2(U[0], U[1], Ptmp); // PL = SL + SETVECTOR3(pw, pv[1], pv[2], pv[0]); + SWAP2(pu[0], pu[1], itmp); iu = 1; + z2 = 2; + } else { + if (sR > 0) { // (+0+) + SETVECTOR3(W, V[2], V[0], V[1]); // PT = ST + SETVECTOR3(pw, pv[2], pv[0], pv[1]); + z2 = 1; + } else { // (+00) + SETVECTOR3(W, V[1], V[2], V[0]); // PT = ST x ST + SWAP2(U[0], U[1], Ptmp); // PL = SL + SETVECTOR3(pw, pv[1], pv[2], pv[0]); + SWAP2(pu[0], pu[1], itmp); iu = 1; + z2 = 3; + } + } + } + } + } else { // sP == 0 + if (sQ < 0) { + if (sR < 0) { // (0--) + SETVECTOR3(W, V[1], V[2], V[0]); // PT = ST x ST + SWAP2(U[0], U[1], Ptmp); // PL = SL + SETVECTOR3(pw, pv[1], pv[2], pv[0]); + SWAP2(pu[0], pu[1], itmp); iu = 1; + z2 = 1; + } else { + if (sR > 0) { // (0-+) + SETVECTOR3(W, V[0], V[1], V[2]); // I3 + SWAP2(U[0], U[1], Ptmp); // PL = SL + SETVECTOR3(pw, pv[0], pv[1], pv[2]); + SWAP2(pu[0], pu[1], itmp); iu = 1; + z2 = 2; + } else { // (0-0) + SETVECTOR3(W, V[2], V[0], V[1]); // PT = ST + SETVECTOR3(pw, pv[2], pv[0], pv[1]); + z2 = 3; + } + } + } else { + if (sQ > 0) { + if (sR < 0) { // (0+-) + SETVECTOR3(W, V[0], V[1], V[2]); // I3 + SETVECTOR3(pw, pv[0], pv[1], pv[2]); + z2 = 2; + } else { + if (sR > 0) { // (0++) + SETVECTOR3(W, V[1], V[2], V[0]); // PT = ST x ST + SETVECTOR3(pw, pv[1], pv[2], pv[0]); + z2 = 1; + } else { // (0+0) + SETVECTOR3(W, V[2], V[0], V[1]); // PT = ST + SWAP2(U[0], U[1], Ptmp); // PL = SL + SETVECTOR3(pw, pv[2], pv[0], pv[1]); + SWAP2(pu[0], pu[1], itmp); iu = 1; + z2 = 3; + } + } + } else { // sQ == 0 + if (sR < 0) { // (00-) + SETVECTOR3(W, V[0], V[1], V[2]); // I3 + SETVECTOR3(pw, pv[0], pv[1], pv[2]); + z2 = 3; + } else { + if (sR > 0) { // (00+) + SETVECTOR3(W, V[0], V[1], V[2]); // I3 + SWAP2(U[0], U[1], Ptmp); // PL = SL + SETVECTOR3(pw, pv[0], pv[1], pv[2]); + SWAP2(pu[0], pu[1], itmp); iu = 1; + z2 = 3; + } else { // (000) + z2 = -1; + } + } + } + } + } + } + + if (z1 == -1) { + assert(z2 == -1); // SELF_CHECK + return tri_tri_2d(A, B, C, P, Q, R, O, level, types, pos); + } + + if ((iu == 1) && (z1 == 2)) { + z1 = 4; // A and B are inverted. + } + + if (z2 == 1) { + s1 = orient3d(U[0], U[2], W[0], W[2]); // A, C, P, R + s2 = orient3d(U[1], U[2], W[1], W[2]); // B, C, Q, R + orient3dcount+=2; + if (b->epsilon > 0) { + if ((s1 != 0) && iscoplanar(U[0], U[2], W[0], W[2], s1)) s1 = 0; + if ((s2 != 0) && iscoplanar(U[1], U[2], W[1], W[2], s2)) s2 = 0; + } + } else { + s1 = orient3d(U[0], U[2], W[2], W[1]); // A, C, R, Q + s2 = orient3d(U[1], U[2], W[2], W[0]); // B, C, R, P + orient3dcount+=2; + if (b->epsilon > 0) { + if ((s1 != 0) && iscoplanar(U[0], U[2], W[2], W[1], s1)) s1 = 0; + if ((s2 != 0) && iscoplanar(U[1], U[2], W[2], W[0], s2)) s2 = 0; + } + } + + if (b->verbose > 2) { + printf(" Tri-tri (%d %d %d)-(%d %d %d) (%d-%d) (%c%c)\n", + pointmark(U[0]), pointmark(U[1]), pointmark(U[2]), pointmark(W[0]), + pointmark(W[1]), pointmark(W[2]), z1, z2, + s1>0 ? '+' : (s1<0 ? '-' : '0'), s2>0 ? '+' : (s2<0 ? '-' : '0')); + } + + if (z2 == 1) { + if (s1 < 0) { + return 0; + } + if (s2 > 0) { + return 0; + } + } else { + if (s1 > 0) { + return 0; + } + if (s2 < 0) { + return 0; + } + } + + if (level == 0) { + return 1; + } + + if (z2 == 1) { + + if (z1 == 0) { // (01) + if (s1 == 0) { + // R = k in [A, C] (tritri-010###). + types[0] = (int) TOUCHEDGE; + pos[0] = (iu == 0 ? pu[2] : mi1mo3[pu[2]]); // [C, A] + pos[1] = pw[2]; // R + types[1] = (int) DISJOINT; + } else { + if (s2 == 0) { + // R = l in [B, C] (tritri-01#0##). + types[0] = (int) TOUCHEDGE; + pos[0] = (iu == 0 ? pu[1] : mi1mo3[pu[1]]); // [B, C] + pos[1] = pw[2]; // R + types[1] = (int) DISJOINT; + } else { + // R in [k, l] in [A, B, C] (tritri-01+-##). + types[0] = (int) TOUCHFACE; + pos[0] = 3; // [A, B, C] + pos[1] = pw[2]; // R + types[1] = (int) DISJOINT; + } + } + } else if (z1 == 1) { // (11) + assert(s1 == 0); // SELF_CHECK + // C = R (tritri-110###). + types[0] = (int) SHAREVERT; + pos[0] = pu[2]; // C + pos[1] = pw[2]; // R + types[1] = (int) DISJOINT; + } else if (z1 == 2) { // (21) + if (s1 == 0) { + // R = A (tritri-210###). + types[0] = (int) SHAREVERT; + pos[0] = pu[0]; // A + pos[1] = pw[2]; // R + types[1] = (int) DISJOINT; + } else { + if (s2 == 0) { + // R = l, R in [B, C] (tritri-21#0##). + types[0] = (int) TOUCHEDGE; + pos[0] = (iu == 0 ? pu[1] : mi1mo3[pu[1]]); // [B, C] + pos[1] = pw[2]; // R + types[1] = (int) DISJOINT; + } else { + // R in [k, l] in [A, B, C] (tritri-21+-##). + types[0] = (int) TOUCHFACE; + pos[0] = 3; // Interior of [A, B, C] + pos[1] = pw[2]; // R + types[1] = (int) DISJOINT; + } + } + } else if (z1 == 3) { // (31) + if (s1 == 0) { + // R = A (tritri-310###). + types[0] = (int) SHAREVERT; + pos[0] = pu[0]; // A + pos[1] = pw[2]; // R + types[1] = (int) DISJOINT; + } else { + if (s2 == 0) { + // R = B (tritri-31#0##). + types[0] = (int) SHAREVERT; + pos[0] = pu[1]; // B + pos[1] = pw[2]; // R + types[1] = (int) DISJOINT; + } else { + // R in [A, B] (tritri-31+-##). + types[0] = (int) TOUCHEDGE; + pos[0] = (iu == 0 ? pu[0] : mi1mo3[pu[0]]); // [A, B] + pos[1] = pw[2]; // R + types[1] = (int) DISJOINT; + } + } + } else if (z1 == 4) { // (41) + if (s1 == 0) { // (tritri-410###). + // R touches [C, A] + types[0] = (int) TOUCHEDGE; + pos[0] = (iu == 0 ? pu[2] : mi1mo3[pu[2]]); // [C, A] + pos[1] = pw[2]; // R + types[1] = (int) DISJOINT; + } else { + if (s2 == 0) { // (tritri-41#0##). + // R = B + types[0] = (int) SHAREVERT; + pos[0] = pu[1]; // B + pos[1] = pw[2]; // R + types[1] = (int) DISJOINT; + } else { // (tritri-41+-##) + // R in [k, l] + types[0] = (int) TOUCHFACE; + pos[0] = 3; // [A, B, C] + pos[1] = pw[2]; // R + types[1] = (int) DISJOINT; + } + } + } + + return 1; + } // z2 == 1 + + if (z1 == 1) { + + if (z2 == 0) { // (10) + if (s1 == 0) { + // C = j, C in [Q, R] (tritri-100###). + types[0] = (int) ACROSSVERT; + pos[0] = pu[2]; // C + pos[1] = (iv == 0 ? pw[1] : mi1mo3[pw[1]]); // [Q, R] + types[1] = (int) DISJOINT; + } else { + if (s2 == 0) { + // C = i, C in [P, R] (tritri-10#0##). + types[0] = (int) ACROSSVERT; + pos[0] = pu[2]; // C + pos[1] = (iv == 0 ? pw[2] : mi1mo3[pw[2]]); // [R, P] + types[1] = (int) DISJOINT; + } else { + // C in [i, j] in [P, Q, R] (tritri-10-+##). + types[0] = (int) ACROSSVERT; + pos[0] = pu[2]; // C + pos[1] = 3; // Interior of [P, Q, R] + types[1] = (int) DISJOINT; + } + } + } else if (z2 == 2) { // (12) + if (s1 == 0) { // + // C = j, C in [Q, R] (tritri-120###). + types[0] = (int) ACROSSVERT; + pos[0] = pu[2]; // C + pos[1] = (iv == 0 ? pw[1] : mi1mo3[pw[1]]); // [Q, R] + types[1] = (int) DISJOINT; + } else { + if (s2 == 0) { + // C = P (tritri-12#0##). + types[0] = (int) SHAREVERT; + pos[0] = pu[2]; // C + pos[1] = pw[0]; // P + types[1] = (int) DISJOINT; + } else { + // C in [i, j] in [P, Q, R] (tritri-12-+##). + types[0] = (int) ACROSSVERT; + pos[0] = pu[2]; // C + pos[1] = 3; // Interior of [P, Q, R] + types[1] = (int) DISJOINT; + } + } + } else if (z2 == 3) { // (13) + if (s1 == 0) { + // C = Q (tritri-130###). + types[0] = (int) SHAREVERT; + pos[0] = pu[2]; // C + pos[1] = pw[1]; // Q + types[1] = (int) DISJOINT; + } else { + if (s2 == 0) { + // C = P (tritri-13#0##). + types[0] = (int) SHAREVERT; + pos[0] = pu[2]; // C + pos[1] = pw[0]; // P + types[1] = (int) DISJOINT; + } else { + // C in [P, Q] (tritri-13-+##). + types[0] = (int) ACROSSVERT; + pos[0] = pu[2]; // C + pos[1] = (iv == 0 ? pw[0] : mi1mo3[pw[0]]); // [P, Q] + types[1] = (int) DISJOINT; + } + } + } + + return 1; + } // if (z1 == 1) + + // Two additional orientation tests are needed. + s3 = orient3d(U[0], U[2], W[2], W[0]); // A, C, R, P + s4 = orient3d(U[1], U[2], W[2], W[1]); // B, C, R, Q + orient3dcount+=2; + if (b->epsilon > 0) { + if ((s3 != 0) && iscoplanar(U[0], U[2], W[2], W[0], s3)) s3 = 0; + if ((s4 != 0) && iscoplanar(U[1], U[2], W[2], W[1], s4)) s4 = 0; + } + + if (b->verbose > 2) { + printf(" (tritri-%d%d%c%c%c%c)\n", z1, z2, + s1>0 ? '+' : (s1<0 ? '-' : '0'), s2>0 ? '+' : (s2<0 ? '-' : '0'), + s3>0 ? '+' : (s3<0 ? '-' : '0'), s4>0 ? '+' : (s4<0 ? '-' : '0')); + } + + if (z1 == 0) { + + if (z2 == 0) { // (00) + if (s1 < 0) { + if (s3 > 0) { + assert(s2 > 0); // SELF_CHECK. + if (s4 > 0) { + // [i, j] overlaps [k, l] (tritri-00-+++). + types[0] = (int) ACROSSEDGE; + pos[0] = (iu == 0 ? pu[2] : mi1mo3[pu[2]]); // [C, A] + pos[1] = 3; // [P, Q, R] + types[1] = (int) ACROSSFACE; + pos[2] = 3; // [A, B, C] + pos[3] = (iv == 0 ? pw[1] : mi1mo3[pw[1]]); // [Q, R] + } else { + if (s4 == 0) { + // j = l, [i, j] contains [k, l] (tritri-00-++0). + types[0] = (int) ACROSSEDGE; + pos[0] = (iu == 0 ? pu[2] : mi1mo3[pu[2]]); // [C, A] + pos[1] = 3; // [P, Q, R] + types[1] = (int) ACROSSEDGE; + pos[2] = (iu == 0 ? pu[1] : mi1mo3[pu[1]]); // [B, C] + pos[3] = (iv == 0 ? pw[1] : mi1mo3[pw[1]]); // [Q, R] + } else { + // [i, j] contains [k, l] (tritri-00-++-). + types[0] = (int) ACROSSEDGE; + pos[0] = (iu == 0 ? pu[2] : mi1mo3[pu[2]]); // [C, A] + pos[1] = 3; // [P, Q, R] + types[1] = (int) ACROSSEDGE; + pos[2] = (iu == 0 ? pu[1] : mi1mo3[pu[1]]); // [B, C] + pos[3] = 3; // [P, Q, R] + } + } + } else { + if (s3 == 0) { // (00-+0#) + assert(s2 > 0); // SELF_CHECK. + if (s4 > 0) { + // i = k, [i, j] overlaps [k, l] (tritri-00-+0+). + types[0] = (int) ACROSSEDGE; + pos[0] = (iu == 0 ? pu[2] : mi1mo3[pu[2]]); // [C, A] + pos[1] = (iv == 0 ? pw[2] : mi1mo3[pw[2]]); // [R, P] + types[1] = (int) ACROSSFACE; + pos[2] = 3; // [A, B, C] + pos[3] = (iv == 0 ? pw[1] : mi1mo3[pw[1]]); // [Q, R] + } else { + if (s4 == 0) { + // [i, j] = [k, l] (tritri-00-+00). + types[0] = (int) ACROSSEDGE; + pos[0] = (iu == 0 ? pu[2] : mi1mo3[pu[2]]); // [C, A] + pos[1] = (iv == 0 ? pw[2] : mi1mo3[pw[2]]); // [R, P] + types[1] = (int) ACROSSEDGE; + pos[2] = (iu == 0 ? pu[1] : mi1mo3[pu[1]]); // [B, C] + pos[3] = (iv == 0 ? pw[1] : mi1mo3[pw[1]]); // [Q, R] + } else { + // i = k, [i, j] contains [k, l] (tritri-00-+0-). + types[0] = (int) ACROSSEDGE; + pos[0] = (iu == 0 ? pu[2] : mi1mo3[pu[2]]); // [C, A] + pos[1] = (iv == 0 ? pw[2] : mi1mo3[pw[2]]); // [R, P] + types[1] = (int) ACROSSEDGE; + pos[2] = (iu == 0 ? pu[1] : mi1mo3[pu[1]]); // [B, C] + pos[3] = 3; // [P, Q, R] + } + } + } else { // s3 < 0 + if (s2 > 0) { + if (s4 > 0) { + // [i, j] in [k, l] in [A, B, C] (tritri-00-+-+). + types[0] = (int) ACROSSFACE; + pos[0] = 3; // [A, B, C] + pos[1] = (iv == 0 ? pw[2] : mi1mo3[pw[2]]); // [R, P] + types[1] = (int) ACROSSFACE; + pos[2] = 3; // [A, B, C] + pos[3] = (iv == 0 ? pw[1] : mi1mo3[pw[1]]); // [Q, R] + } else { + if (s4 == 0) { + // j = l, [i, j] in [k, l] (tritri-00-+-0) + types[0] = (int) ACROSSFACE; + pos[0] = 3; // [A, B, C] + pos[1] = (iv == 0 ? pw[2] : mi1mo3[pw[2]]); // [R, P] + types[1] = (int) ACROSSEDGE; + pos[2] = (iu == 0 ? pu[1] : mi1mo3[pu[1]]); // [B, C] + pos[3] = (iv == 0 ? pw[1] : mi1mo3[pw[1]]); // [Q, R] + } else { // s4 < 0 + // [i, j] overlaps [k, l] (tritri-00-+--) + types[0] = (int) ACROSSFACE; + pos[0] = 3; // [A, B, C] + pos[1] = (iv == 0 ? pw[2] : mi1mo3[pw[2]]); // [R, P] + types[1] = (int) ACROSSEDGE; + pos[2] = (iu == 0 ? pu[1] : mi1mo3[pu[1]]); // [B, C] + pos[3] = 3; // [P, Q, R] + } + } + } else { // s2 == 0 + assert(s4 < 0); // SELF_CHECK + // i = l, [P, R] intersects [B, C] (tritri-00#0##). + types[0] = (int) ACROSSEDGE; + pos[0] = (iu == 0 ? pu[1] : mi1mo3[pu[1]]); // [B, C] + pos[1] = (iv == 0 ? pw[2] : mi1mo3[pw[2]]); // [R, P] + types[1] = (int) DISJOINT; + } + } + } + } else { // s1 == 0 + // j = k, [Q, R] intersects [A, B] (tritri-000###). + types[0] = (int) ACROSSEDGE; + pos[0] = (iu == 0 ? pu[2] : mi1mo3[pu[2]]); // [C, A] + pos[1] = (iv == 0 ? pw[1] : mi1mo3[pw[1]]); // [Q, R] + types[1] = (int) DISJOINT; + } + } else if (z2 == 2) { // (02) + if (s1 < 0) { + if (s3 > 0) { // (02-#+#) + assert(s2 > 0); // SELF_CHECK; + if (s4 > 0) { + // [P, j] overlaps [k, l] (tritri-02-+++). + types[0] = (int) ACROSSEDGE; + pos[0] = (iu == 0 ? pu[2] : mi1mo3[pu[2]]); // [C, A] + pos[1] = 3; // [P, Q, R] + types[1] = (int) ACROSSFACE; + pos[2] = 3; // [A, B, C] + pos[3] = (iv == 0 ? pw[1] : mi1mo3[pw[1]]); // [Q, R] + } else { + if (s4 == 0) { + // j = k, [i, j] contains [k, l] (tritri-02-++0). + types[0] = (int) ACROSSEDGE; + pos[0] = (iu == 0 ? pu[2] : mi1mo3[pu[2]]); // [C, A] + pos[1] = 3; // [P, Q, R] + types[1] = (int) ACROSSEDGE; + pos[2] = (iu == 0 ? pu[1] : mi1mo3[pu[1]]); // [B, C] + pos[3] = (iv == 0 ? pw[1] : mi1mo3[pw[1]]); // [Q, R] + } else { // s4 < 0 + // [i, j] contains [k, l] (tritri-02-++-). + types[0] = (int) ACROSSEDGE; + pos[0] = (iu == 0 ? pu[2] : mi1mo3[pu[2]]); // [C, A] + pos[1] = 3; // [P, Q, R] + types[1] = (int) ACROSSEDGE; + pos[2] = (iu == 0 ? pu[1] : mi1mo3[pu[1]]); // [B, C] + pos[3] = 3; // [P, Q, R] + } + } + } else { + if (s3 == 0) { // (02-#0#) + assert(s2 > 0); // SELF_CHECK + if (s4 > 0) { + // P = k, [P, j] in [k, l] (tritri-02-+0+). + types[0] = (int) TOUCHEDGE; + pos[0] = (iu == 0 ? pu[2] : mi1mo3[pu[2]]); // [C, A] + pos[1] = pw[0]; // P + types[1] = (int) ACROSSFACE; + pos[2] = 3; // [A, B, C] + pos[3] = (iv == 0 ? pw[1] : mi1mo3[pw[1]]); // [Q, R] + } else { + if (s4 == 0) { + // [P, j] = [k, l] (tritri-02-+00). + types[0] = (int) TOUCHEDGE; + pos[0] = (iu == 0 ? pu[2] : mi1mo3[pu[2]]); // [C, A] + pos[1] = pw[0]; // P + types[1] = (int) ACROSSEDGE; + pos[2] = (iu == 0 ? pu[1] : mi1mo3[pu[1]]); // [B, C] + pos[3] = (iv == 0 ? pw[1] : mi1mo3[pw[1]]); // [Q, R] + } else { // s4 < 0 + // P = k, [P, j] contains [k, l] (tritri-02-+0-). + types[0] = (int) TOUCHEDGE; + pos[0] = (iu == 0 ? pu[2] : mi1mo3[pu[2]]); // [C, A] + pos[1] = pw[0]; // P + types[1] = (int) ACROSSEDGE; + pos[2] = (iu == 0 ? pu[1] : mi1mo3[pu[1]]); // [B, C] + pos[3] = 3; // [P, Q, R] + } + } + } else { // s3 < 0. + if (s2 > 0) { + if (s4 > 0) { + // [P, j] in [k, l] in [A, B, C] (tritri-02-+-+). + types[0] = (int) TOUCHFACE; + pos[0] = 3; // [A, B, C] + pos[1] = pw[0]; // P + types[1] = (int) ACROSSFACE; + pos[2] = 3; // [A, B, C] + pos[3] = (iv == 0 ? pw[1] : mi1mo3[pw[1]]); // [Q, R] + } else { + if (s4 == 0) { + // j = l, [P, j] overlaps [k, l] (tritri-02-+-0). + types[0] = (int) TOUCHFACE; + pos[0] = 3; // [A, B, C] + pos[1] = pw[0]; // P + types[1] = (int) ACROSSEDGE; + pos[2] = (iu == 0 ? pu[1] : mi1mo3[pu[1]]); // [B, C] + pos[3] = (iv == 0 ? pw[1] : mi1mo3[pw[1]]); // [Q, R] + } else { // s4 < 0 + // [P, j] overlaps [k, l] (tritri-02-+--). + types[0] = (int) TOUCHFACE; + pos[0] = 3; // [A, B, C] + pos[1] = pw[0]; // P + types[1] = (int) ACROSSEDGE; + pos[2] = (iu == 0 ? pu[1] : mi1mo3[pu[1]]); // [B, C] + pos[3] = 3; // [P, Q, R] + } + } + } else { // s2 == 0 + assert(s4 < 0); // SELF_CHECK + // P = k, P in [B, C] (tritri-02#0##). + types[0] = (int) TOUCHEDGE; + pos[0] = (iu == 0 ? pu[1] : mi1mo3[pu[1]]); // [B, C] + pos[1] = pw[0]; // P + types[1] = (int) DISJOINT; + } + } + } + } else { // s1 == 0 + // j = k, [Q, R] intersects [A, C] (tritri-020###). + types[0] = (int) ACROSSEDGE; + pos[0] = (iu == 0 ? pu[2] : mi1mo3[pu[2]]); // [C, A] + pos[1] = (iv == 0 ? pw[1] : mi1mo3[pw[1]]); // [Q, R] + types[1] = (int) DISJOINT; + } + } else if (z2 == 3) { // (03) + if (s1 < 0) { + if (s3 > 0) { + assert(s2 > 0); // SELF_CHECK + if (s4 > 0) { + // [P, Q] overlaps [k, l] (tritri-03-+++). + types[0] = (int) ACROSSEDGE; + pos[0] = (iu == 0 ? pu[2] : mi1mo3[pu[2]]); // [C, A] + pos[1] = (iv == 0 ? pw[0] : mi1mo3[pw[0]]); // Int([P, Q]) + types[1] = (int) TOUCHFACE; + pos[2] = 3; // [A, B, C] + pos[3] = pw[1]; // Q + } else { + if (s4 == 0) { + // Q = l, [P, Q] contains [k, l] (tritri-03-++0). + types[0] = (int) ACROSSEDGE; + pos[0] = (iu == 0 ? pu[2] : mi1mo3[pu[2]]); // [C, A] + pos[1] = (iv == 0 ? pw[0] : mi1mo3[pw[0]]); // Int([P, Q]) + types[1] = (int) TOUCHEDGE; + pos[2] = (iu == 0 ? pu[1] : mi1mo3[pu[1]]); // [B, C] + pos[3] = pw[1]; // Q + } else { // s4 < 0 + // [P, Q] contains [k, l] (tritri-03-++-). + types[0] = (int) ACROSSEDGE; + pos[0] = (iu == 0 ? pu[2] : mi1mo3[pu[2]]); // [C, A] + pos[1] = (iv == 0 ? pw[0] : mi1mo3[pw[0]]); // Int([P, Q]) + types[1] = (int) ACROSSEDGE; + pos[2] = (iu == 0 ? pu[1] : mi1mo3[pu[1]]); // [B, C] + pos[3] = (iv == 0 ? pw[0] : mi1mo3[pw[0]]); // Int([P, Q]) + } + } + } else { + if (s3 == 0) { + assert(s2 > 0); // SELF_CHECK + if (s4 > 0) { + // P = k, [P, Q] in [k, l] (tritri-03-+0+). + types[0] = (int) TOUCHEDGE; + pos[0] = (iu == 0 ? pu[2] : mi1mo3[pu[2]]); // [C, A] + pos[1] = pw[0]; // P + types[1] = (int) TOUCHFACE; + pos[2] = 3; // [A, B, C] + pos[3] = pw[1]; // Q + } else { + if (s4 == 0) { + // [P, Q] = [k, l] (tritri-03-+00). + types[0] = (int) TOUCHEDGE; + pos[0] = (iu == 0 ? pu[2] : mi1mo3[pu[2]]); // [C, A] + pos[1] = pw[0]; // P + types[1] = (int) TOUCHEDGE; + pos[2] = (iu == 0 ? pu[1] : mi1mo3[pu[1]]); // [B, C] + pos[3] = pw[1]; // Q + } else { // s4 < 0 + // P = k, [P, Q] contains [k, l] (tritri-03-+0-). + types[0] = (int) TOUCHEDGE; + pos[0] = (iu == 0 ? pu[2] : mi1mo3[pu[2]]); // [C, A] + pos[1] = pw[0]; // P + types[1] = (int) ACROSSEDGE; + pos[2] = (iu == 0 ? pu[1] : mi1mo3[pu[1]]); // [B, C] + pos[3] = (iv == 0 ? pw[0] : mi1mo3[pw[0]]); // Int([P, Q]) + } + } + } else { // s3 < 0 + if (s2 > 0) { + if (s4 > 0) { + // [P, Q] in [k, l] (tritri-03-+-+). + types[0] = (int) TOUCHFACE; + pos[0] = 3; // [A, B, C] + pos[1] = pw[0]; // P + types[1] = (int) TOUCHFACE; + pos[2] = 3; // [A, B, C] + pos[3] = pw[1]; // Q + } else { + if (s4 == 0) { + // Q = l, [P, Q] in [k, l] (tritri-03-+-0). + types[0] = (int) TOUCHFACE; + pos[0] = 3; // [A, B, C] + pos[1] = pw[0]; // P + types[1] = (int) TOUCHEDGE; + pos[2] = (iu == 0 ? pu[1] : mi1mo3[pu[1]]); // [B, C] + pos[3] = pw[1]; // Q + } else { // s4 < 0 + // [P, Q] overlaps [k, l] (tritri-03-+--). + types[0] = (int) TOUCHFACE; + pos[0] = 3; // [A, B, C] + pos[1] = pw[0]; // P + types[1] = (int) ACROSSEDGE; + pos[2] = (iu == 0 ? pu[1] : mi1mo3[pu[1]]); // [B, C] + pos[3] = (iv == 0 ? pw[0] : mi1mo3[pw[0]]); // Int([P, Q]) + } + } + } else { // s2 == 0 + // P = l in [B, C] (tritri-03#0##). + types[0] = (int) TOUCHEDGE; + pos[0] = (iu == 0 ? pu[1] : mi1mo3[pu[1]]); // [B, C] + pos[1] = pw[0]; // P + types[1] = (int) DISJOINT; + } + } + } + } else { + // Q = k in [A, C] (tritri-030###). + types[0] = (int) TOUCHEDGE; + pos[0] = (iu == 0 ? pu[2] : mi1mo3[pu[2]]); // [C, A] + pos[1] = pw[1]; // Q + types[1] = (int) DISJOINT; + } + } + + return 1; + } // if (z1 == 0) + + if (z1 == 2) { + + if (z2 == 0) { // (20) + if (s1 < 0) { + if (s3 > 0) { + assert(s2 > 0); // SELF_CHECK + if (s4 > 0) { + // [i, j] overlaps [A, l] (tritri-20-+++). + types[0] = (int) ACROSSVERT; + pos[0] = pu[0]; // A + pos[1] = 3; // [P, Q, R] + types[1] = (int) ACROSSFACE; + pos[2] = 3; // [A, B, C] + pos[3] = (iv == 0 ? pw[1] : mi1mo3[pw[1]]); // [Q, R] + } else { + if (s4 == 0) { + // j = l, [i, j] contains [A, l] (tritri-20-++0). + types[0] = (int) ACROSSVERT; + pos[0] = pu[0]; // A + pos[1] = 3; // [P, Q, R] + types[1] = (int) ACROSSEDGE; + pos[2] = (iu == 0 ? pu[1] : mi1mo3[pu[1]]); // [B, C] + pos[3] = (iv == 0 ? pw[1] : mi1mo3[pw[1]]); // [Q, R] + } else { // s4 < 0 + // [i, j] contains [A, l] (tritri-20-++-). + types[0] = (int) ACROSSVERT; + pos[0] = pu[0]; // A + pos[1] = 3; // [P, Q, R] + types[1] = (int) ACROSSEDGE; + pos[2] = (iu == 0 ? pu[1] : mi1mo3[pu[1]]); // [B, C] + pos[3] = 3; // [P, Q, R] + } + } + } else { + if (s3 == 0) { + assert(s2 > 0); // SELF_CHECK + if (s4 > 0) { + // i = A, [i, j] overlaps [A, l] (tritri-20-+0+). + types[0] = (int) ACROSSVERT; + pos[0] = pu[0]; // A + pos[1] = (iv == 0 ? pw[2] : mi1mo3[pw[2]]); // [R, P] + types[1] = (int) ACROSSFACE; + pos[2] = 3; // [A, B, C] + pos[3] = (iv == 0 ? pw[1] : mi1mo3[pw[1]]); // [Q, R] + } else { + if (s4 == 0) { + // [i, j] = [A, l] (tritri-20-+00). + types[0] = (int) ACROSSVERT; + pos[0] = pu[0]; // A + pos[1] = (iv == 0 ? pw[2] : mi1mo3[pw[2]]); // [R, P] + types[1] = (int) ACROSSEDGE; + pos[2] = (iu == 0 ? pu[1] : mi1mo3[pu[1]]); // [B, C] + pos[3] = (iv == 0 ? pw[1] : mi1mo3[pw[1]]); // [Q, R] + } else { // s4 < 0 + // i = A, [i, j] contains [A, l] (tritri-20-+0-). + types[0] = (int) ACROSSVERT; + pos[0] = pu[0]; // A + pos[1] = (iv == 0 ? pw[2] : mi1mo3[pw[2]]); // [R, P] + types[1] = (int) ACROSSEDGE; + pos[2] = (iu == 0 ? pu[1] : mi1mo3[pu[1]]); // [B, C] + pos[3] = 3; // [P, Q, R] + } + } + } else { // s3 < 0 + if (s2 > 0) { + if (s4 > 0) { + // [i, j] in [A, l] in [A, B, C] (tritri-20-+-+). + types[0] = (int) ACROSSFACE; + pos[0] = 3; // [A, B, C] + pos[1] = (iv == 0 ? pw[2] : mi1mo3[pw[2]]); // [R, P] + types[1] = (int) ACROSSFACE; + pos[2] = 3; // [A, B, C] + pos[3] = (iv == 0 ? pw[1] : mi1mo3[pw[1]]); // [Q, R] + } else { + if (s4 == 0) { + // j = l, [i, j] in [A, l] (tritri-20-+-0). + types[0] = (int) ACROSSFACE; + pos[0] = 3; // [A, B, C] + pos[1] = (iv == 0 ? pw[2] : mi1mo3[pw[2]]); // [R, P] + types[1] = (int) ACROSSEDGE; + pos[2] = (iu == 0 ? pu[1] : mi1mo3[pu[1]]); // [B, C] + pos[3] = (iv == 0 ? pw[1] : mi1mo3[pw[1]]); // [Q, R] + } else { // s4 < 0 + // [i, j] overlaps [A, l] (tritri-20-+--). + types[0] = (int) ACROSSFACE; + pos[0] = 3; // [A, B, C] + pos[1] = (iv == 0 ? pw[2] : mi1mo3[pw[2]]); // [R, P] + types[1] = (int) ACROSSEDGE; + pos[2] = (iu == 0 ? pu[1] : mi1mo3[pu[1]]); // [B, C] + pos[3] = 3; // [P, Q, R] + } + } + } else { // s2 == 0 + // i = l, [P, R] intersects [B, C] (tritri-20#0##). + types[0] = (int) ACROSSEDGE; + pos[0] = (iu == 0 ? pu[1] : mi1mo3[pu[1]]); // [B, C] + pos[1] = (iv == 0 ? pw[2] : mi1mo3[pw[2]]); // [R, P] + types[1] = (int) DISJOINT; + } + } + } + } else { // s1 == 0 + // j = A in [Q, R] (tritri-200###). + types[0] = (int) ACROSSVERT; + pos[0] = pu[0]; // A + pos[1] = (iv == 0 ? pw[1] : mi1mo3[pw[1]]); // [Q, R] + types[1] = (int) DISJOINT; + } + } else if (z2 == 2) { // (22) + if (s1 < 0) { + if (s3 > 0) { + assert(s2 > 0); // SELF_CHECK + if (s4 > 0) { + // [P, j] overlaps [A, l] (tritri-22-+++). + types[0] = (int) ACROSSVERT; + pos[0] = pu[0]; // A + pos[1] = 3; // [P, Q, R] + types[1] = (int) ACROSSFACE; + pos[2] = 3; // [A, B, C] + pos[3] = (iv == 0 ? pw[1] : mi1mo3[pw[1]]); // [Q, R] + } else { + if (s4 == 0) { + // j = l, [P, j] contains [A, l] (tritri-22-++0). + types[0] = (int) ACROSSVERT; + pos[0] = pu[0]; // A + pos[1] = 3; // [P, Q, R] + types[1] = (int) ACROSSEDGE; + pos[2] = (iu == 0 ? pu[1] : mi1mo3[pu[1]]); // [B, C] + pos[3] = (iv == 0 ? pw[1] : mi1mo3[pw[1]]); // [Q, R] + } else { // s4 < 0 + // [P, j] contains [A, l] (tritri-22-++-). + types[0] = (int) ACROSSVERT; + pos[0] = pu[0]; // A + pos[1] = 3; // [P, Q, R] + types[1] = (int) ACROSSEDGE; + pos[2] = (iu == 0 ? pu[1] : mi1mo3[pu[1]]); // [B, C] + pos[3] = 3; // [P, Q, R] + } + } + } else { + if (s3 == 0) { + assert(s2 > 0); // SELF_CHECK + if (s4 > 0) { + // P = A, [P, j] in [A, l] (tritri-22-+0+). + types[0] = (int) SHAREVERT; + pos[0] = pu[0]; // A + pos[1] = pw[0]; // P + types[1] = (int) ACROSSFACE; + pos[2] = 3; // [A, B, C] + pos[3] = (iv == 0 ? pw[1] : mi1mo3[pw[1]]); // [Q, R] + } else { + if (s4 == 0) { + // [P, j] = [A, l] (tritri-22-+00). + types[0] = (int) SHAREVERT; + pos[0] = pu[0]; // A + pos[1] = pw[0]; // P + types[1] = (int) ACROSSEDGE; + pos[2] = (iu == 0 ? pu[1] : mi1mo3[pu[1]]); // [B, C] + pos[3] = (iv == 0 ? pw[1] : mi1mo3[pw[1]]); // [Q, R] + } else { // s4 < 0 + // P = A, [P, j] contains [A, l] (tritri-22-+0-). + types[0] = (int) SHAREVERT; + pos[0] = pu[0]; // A + pos[1] = pw[0]; // P + types[1] = (int) ACROSSEDGE; + pos[2] = (iu == 0 ? pu[1] : mi1mo3[pu[1]]); // [B, C] + pos[3] = 3; // [P, Q, R] + } + } + } else { // s3 < 0 + if (s2 > 0) { + if (s4 > 0) { + // [P, j] in [A, l] in [A, B, C] (tritri-22-+-+). + types[0] = (int) TOUCHFACE; + pos[0] = 3; // [A, B, C] + pos[1] = pw[0]; // P + types[1] = (int) ACROSSFACE; + pos[2] = 3; // [A, B, C] + pos[3] = (iv == 0 ? pw[1] : mi1mo3[pw[1]]); // [Q, R] + } else { + if (s4 == 0) { + // j = l, [P, j] in [A, l] (tritri-22-+-0). + types[0] = (int) TOUCHFACE; + pos[0] = 3; // [A, B, C] + pos[1] = pw[0]; // P + types[1] = (int) ACROSSEDGE; + pos[2] = (iu == 0 ? pu[1] : mi1mo3[pu[1]]); // [B, C] + pos[3] = (iv == 0 ? pw[1] : mi1mo3[pw[1]]); // [Q, R] + } else { // s4 < 0 + // P, j] overlaps [A, l] (tritri-22-+--). + types[0] = (int) TOUCHFACE; + pos[0] = 3; // [A, B, C] + pos[1] = pw[0]; // P + types[1] = (int) ACROSSEDGE; + pos[2] = (iu == 0 ? pu[1] : mi1mo3[pu[1]]); // [B, C] + pos[3] = 3; // [P, Q, R] + } + } + } else { // s2 == 0 + // P = l, P in [B, C] (tritri-22#0##). + types[0] = (int) TOUCHEDGE; + pos[0] = (iu == 0 ? pu[1] : mi1mo3[pu[1]]); // Int({[B, C]) + pos[1] = pw[0]; // P + types[1] = (int) DISJOINT; + } + } + } + } else { + // j = A in [Q, R] (tritri-220###). + types[0] = (int) ACROSSVERT; + pos[0] = pu[0]; // A + pos[1] = (iv == 0 ? pw[1] : mi1mo3[pw[1]]); // [Q, R] + types[1] = (int) DISJOINT; + } + } else if (z2 == 3) { // (23) + if (s1 < 0) { + if (s3 > 0) { + assert(s2 > 0); // SELF_CHECK + if (s4 > 0) { + // [P, Q] overlaps [A, l] (tritri-23-+++). + types[0] = (int) ACROSSVERT; + pos[0] = pu[0]; // A + pos[1] = (iv == 0 ? pw[0] : mi1mo3[pw[0]]); // Int([P, Q]) + types[1] = (int) TOUCHFACE; + pos[2] = 3; // [A, B, C] + pos[3] = pw[1]; // Q + } else { + if (s4 == 0) { + // Q = l, [P, Q] contains [A, l] (tritri-23-++0). + types[0] = (int) ACROSSVERT; + pos[0] = pu[0]; // A + pos[1] = (iv == 0 ? pw[0] : mi1mo3[pw[0]]); // Int([P, Q]) + types[1] = (int) TOUCHEDGE; + pos[2] = (iu == 0 ? pu[1] : mi1mo3[pu[1]]); // [B, C] + pos[3] = pw[1]; // Q + } else { // s4 < 0 + // [P, Q] contains [A, l] (tritri-23-++-). + types[0] = (int) ACROSSVERT; + pos[0] = pu[0]; // A + pos[1] = (iv == 0 ? pw[0] : mi1mo3[pw[0]]); // Int([P, Q]) + types[1] = (int) ACROSSEDGE; + pos[2] = (iu == 0 ? pu[1] : mi1mo3[pu[1]]); // [B, C] + pos[3] = (iv == 0 ? pw[0] : mi1mo3[pw[0]]); // Int([P, Q]) + } + } + } else { + if (s3 == 0) { + assert(s2 > 0); // SELF_CHECK + if (s4 > 0) { + // P = A, [P, Q] in [A, l] (tritri-23-+0+). + types[0] = (int) SHAREVERT; + pos[0] = pu[0]; // A + pos[1] = pw[0]; // P + types[1] = (int) TOUCHFACE; + pos[2] = 3; // [A, B, C] + pos[3] = pw[1]; // Q + } else { + if (s4 == 0) { + // [P, Q] = [A, l] (tritri-23-+00). + types[0] = (int) SHAREVERT; + pos[0] = pu[0]; // A + pos[1] = pw[0]; // P + types[1] = (int) TOUCHEDGE; + pos[2] = (iu == 0 ? pu[1] : mi1mo3[pu[1]]); // [B, C] + pos[3] = pw[1]; // Q + } else { // s4 < 0 + // [P, Q] contains [A, l] (tritri-23-+0-). + types[0] = (int) SHAREVERT; + pos[0] = pu[0]; // A + pos[1] = pw[0]; // P + types[1] = (int) ACROSSEDGE; + pos[2] = (iu == 0 ? pu[1] : mi1mo3[pu[1]]); // [B, C] + pos[3] = (iv == 0 ? pw[0] : mi1mo3[pw[0]]); // Int([P, Q]) + } + } + } else { // s3 < 0 + if (s2 > 0) { + if (s4 > 0) { + // [P, Q] in [A, l] (tritri-23-+-+). + types[0] = (int) TOUCHFACE; + pos[0] = 3; // [A, B, C] + pos[1] = pw[0]; // P + types[1] = (int) TOUCHFACE; + pos[2] = 3; // [A, B, C] + pos[3] = pw[1]; // Q + } else { + if (s4 == 0) { + // Q = l, [P, Q] in [A, l] (tritri-23-+-0). + types[0] = (int) TOUCHFACE; + pos[0] = 3; // [A, B, C] + pos[1] = pw[0]; // P + types[1] = (int) TOUCHEDGE; + pos[2] = (iu == 0 ? pu[1] : mi1mo3[pu[1]]); // [B, C] + pos[3] = pw[1]; // Q + } else { // s4 < 0 + // [P, Q] overlaps [A, l] (tritri-23-+--). + types[0] = (int) TOUCHFACE; + pos[0] = 3; // [A, B, C] + pos[1] = pw[0]; // P + types[1] = (int) ACROSSEDGE; + pos[2] = (iu == 0 ? pu[1] : mi1mo3[pu[1]]); // [B, C] + pos[3] = (iv == 0 ? pw[0] : mi1mo3[pw[0]]); // Int([P, Q]) + } + } + } else { // s2 == 0 + // P = l in [B, C] (tritri-23#0##). + types[0] = (int) TOUCHEDGE; + pos[0] = (iu == 0 ? pu[1] : mi1mo3[pu[1]]); // [B, C] + pos[1] = pw[0]; // P + types[1] = (int) DISJOINT; + } + } + } + } else { + // Q = A (tritri-230###). + types[0] = (int) SHAREVERT; + pos[0] = pu[0]; // A + pos[1] = pw[1]; // Q + types[1] = (int) DISJOINT; + } + } + + return 1; + } // if (z1 == 2) + + if (z1 == 3) { + + if (z2 == 0) { // (30) + if (s1 < 0) { + if (s3 > 0) { + assert(s2 > 0); // SELF_CHECK + if (s4 > 0) { + // [i, j] overlaps [A, B] (tritri-30-+++). + types[0] = (int) ACROSSVERT; + pos[0] = pu[0]; // A + pos[1] = 3; // [P, Q, R] + types[1] = (int) ACROSSEDGE; + pos[2] = (iu == 0 ? pu[0] : mi1mo3[pu[0]]); // Int([A, B]) + pos[3] = (iv == 0 ? pw[1] : mi1mo3[pw[1]]); // [Q, R] + } else { + if (s4 == 0) { + // j = B, [i, j] contains [A, B] (tritri-30-++0). + types[0] = (int) ACROSSVERT; + pos[0] = pu[0]; // A + pos[1] = 3; // [P, Q, R] + types[1] = (int) ACROSSVERT; + pos[2] = pu[1]; // B + pos[3] = (iv == 0 ? pw[1] : mi1mo3[pw[1]]); // [Q, R] + } else { // s4 < 0 + // [i, j] contains [A, B] (tritri-30-++-). + types[0] = (int) ACROSSVERT; + pos[0] = pu[0]; // A + pos[1] = 3; // [P, Q, R] + types[1] = (int) ACROSSVERT; + pos[2] = pu[1]; // B + pos[3] = 3; // [P, Q, R] + } + } + } else { + if (s3 == 0) { + assert(s2 > 0); // SELF_CHECK + if (s4 > 0) { + // i = A, [i, j] in [A, B] (tritri-30-+0+). + types[0] = (int) ACROSSVERT; + pos[0] = pu[0]; // A + pos[1] = (iv == 0 ? pw[2] : mi1mo3[pw[2]]); // [R, P] + types[1] = (int) ACROSSEDGE; + pos[2] = (iu == 0 ? pu[0] : mi1mo3[pu[0]]); // Int([A, B]) + pos[3] = (iv == 0 ? pw[1] : mi1mo3[pw[1]]); // [Q, R] + } else { + if (s4 == 0) { + // [i, j] = [A, B] (tritri-30-+00). + types[0] = (int) ACROSSVERT; + pos[0] = pu[0]; // A + pos[1] = (iv == 0 ? pw[2] : mi1mo3[pw[2]]); // [R, P] + types[1] = (int) ACROSSVERT; + pos[2] = pu[1]; // B + pos[3] = (iv == 0 ? pw[1] : mi1mo3[pw[1]]); // [Q, R] + } else { // s4 < 0 + // i = A, [i, j] contains [A, B] (tritri-30-+0-). + types[0] = (int) ACROSSVERT; + pos[0] = pu[0]; // A + pos[1] = (iv == 0 ? pw[2] : mi1mo3[pw[2]]); // [R, P] + types[1] = (int) ACROSSVERT; + pos[2] = pu[1]; // B + pos[3] = 3; // [P, Q, R] + } + } + } else { // s3 < 0 + if (s2 > 0) { + if (s4 > 0) { + // [i, j] in [A, B] (tritri-30-+-+). + types[0] = (int) ACROSSEDGE; + pos[0] = (iu == 0 ? pu[0] : mi1mo3[pu[0]]); // Int([A, B]) + pos[1] = (iv == 0 ? pw[2] : mi1mo3[pw[2]]); // [R, P] + types[1] = (int) ACROSSEDGE; + pos[2] = (iu == 0 ? pu[0] : mi1mo3[pu[0]]); // Int([A, B]) + pos[3] = (iv == 0 ? pw[1] : mi1mo3[pw[1]]); // [Q, R] + } else { + if (s4 == 0) { + // j = B, [i, j] in [A, B] (tritri-30-+-0). + types[0] = (int) ACROSSEDGE; + pos[0] = (iu == 0 ? pu[0] : mi1mo3[pu[0]]); // Int([A, B]) + pos[1] = (iv == 0 ? pw[2] : mi1mo3[pw[2]]); // [R, P] + types[1] = (int) ACROSSVERT; + pos[2] = pu[1]; // B + pos[3] = (iv == 0 ? pw[1] : mi1mo3[pw[1]]); // [Q, R] + } else { // s4 < 0 + // [i, j] overlaps [A, B] (tritri-30-+--). + types[0] = (int) ACROSSEDGE; + pos[0] = (iu == 0 ? pu[0] : mi1mo3[pu[0]]); // Int([A, B]) + pos[1] = (iv == 0 ? pw[2] : mi1mo3[pw[2]]); // [R, P] + types[1] = (int) ACROSSVERT; + pos[2] = pu[1]; // B + pos[3] = 3; // [P, Q, R] + } + } + } else { // s2 == 0 + // i = B in [P, R] (tritri-30#0##). + types[0] = (int) ACROSSVERT; + pos[0] = pu[1]; // B + pos[1] = (iv == 0 ? pw[2] : mi1mo3[pw[2]]); // [R, P] + types[1] = (int) DISJOINT; + } + } + } + } else { + // j = A in [Q, R] (tritri-300###). + types[0] = (int) ACROSSVERT; + pos[0] = pu[0]; // A + pos[1] = (iv == 0 ? pw[1] : mi1mo3[pw[1]]); // [Q, R] + types[1] = (int) DISJOINT; + } + } else if (z2 == 2) { // (32) + if (s1 < 0) { + if (s3 > 0) { + assert(s2 > 0); // SELF_CHECK + if (s4 > 0) { + // [P, j] overlaps [A, B] (tritri-32-+++). + types[0] = (int) ACROSSVERT; + pos[0] = pu[0]; // A + pos[1] = 3; // [P, Q, R] + types[1] = (int) ACROSSEDGE; + pos[2] = (iu == 0 ? pu[0] : mi1mo3[pu[0]]); // Int([A, B]) + pos[3] = (iv == 0 ? pw[1] : mi1mo3[pw[1]]); // [Q, R] + } else { + if (s4 == 0) { + // j = B, [P, j] contains [A, B] (tritri-32-++0). + types[0] = (int) ACROSSVERT; + pos[0] = pu[0]; // A + pos[1] = 3; // [P, Q, R] + types[1] = (int) ACROSSVERT; + pos[2] = pu[1]; // B + pos[3] = (iv == 0 ? pw[1] : mi1mo3[pw[1]]); // [Q, R] + } else { // s4 < 0 + // [P, j] contains [A, B] (tritri-32-++-). + types[0] = (int) ACROSSVERT; + pos[0] = pu[0]; // A + pos[1] = 3; // [P, Q, R] + types[1] = (int) ACROSSVERT; + pos[2] = pu[1]; // B + pos[3] = 3; // [P, Q, R] + } + } + } else { + if (s3 == 0) { + assert(s2 > 0); // SELF_CHECK + if (s4 > 0) { + // P = A, [P, j] in [A, B] (tritri-32-+0+). + types[0] = (int) SHAREVERT; + pos[0] = pu[0]; // A + pos[1] = pw[0]; // P + types[1] = (int) ACROSSEDGE; + pos[2] = (iu == 0 ? pu[0] : mi1mo3[pu[0]]); // Int([A, B]) + pos[3] = (iv == 0 ? pw[1] : mi1mo3[pw[1]]); // [Q, R] + } else { + if (s4 == 0) { + // [P, j] = [A, B] (tritri-32-+00). + types[0] = (int) SHAREVERT; + pos[0] = pu[0]; // A + pos[1] = pw[0]; // P + types[1] = (int) ACROSSVERT; + pos[2] = pu[1]; // B + pos[3] = (iv == 0 ? pw[1] : mi1mo3[pw[1]]); // [Q, R] + } else { // s4 < 0 + // P = A, [P, j] contains [A, B] (tritri-32-+0-). + types[0] = (int) SHAREVERT; + pos[0] = pu[0]; // A + pos[1] = pw[0]; // P + types[1] = (int) ACROSSVERT; + pos[2] = pu[1]; // B + pos[3] = 3; // [P, Q, R] + } + } + } else { // s3 < 0 + if (s2 > 0) { + if (s4 > 0) { + // [P, j] in [A, B] (tritri-32-+-+). + types[0] = (int) TOUCHEDGE; + pos[0] = (iu == 0 ? pu[0] : mi1mo3[pu[0]]); // Int([A, B]) + pos[1] = pw[0]; // P + types[1] = (int) ACROSSEDGE; + pos[2] = (iu == 0 ? pu[0] : mi1mo3[pu[0]]); // Int([A, B]) + pos[3] = (iv == 0 ? pw[1] : mi1mo3[pw[1]]); // [Q, R] + } else { + if (s4 == 0) { + // j = B, [P, j] in [A, B] (tritri-32-+-0). + types[0] = (int) TOUCHEDGE; + pos[0] = (iu == 0 ? pu[0] : mi1mo3[pu[0]]); // Int([A, B]) + pos[1] = pw[0]; // P + types[1] = (int) ACROSSVERT; + pos[2] = pu[1]; // B + pos[3] = (iv == 0 ? pw[1] : mi1mo3[pw[1]]); // [Q, R] + } else { // s4 < 0 + // [P, j] overlaps [A, B] (tritri-32-+--). + types[0] = (int) TOUCHEDGE; + pos[0] = (iu == 0 ? pu[0] : mi1mo3[pu[0]]); // Int([A, B]) + pos[1] = pw[0]; // P + types[1] = (int) ACROSSVERT; + pos[2] = pu[1]; // B + pos[3] = 3; // [P, Q, R] + } + } + } else { // s2 == 0 + // P = B (tritri-32#0##). + types[0] = (int) SHAREVERT; + pos[0] = pu[1]; // B + pos[1] = pw[0]; // P + types[1] = (int) DISJOINT; + } + } + } + } else { + // j = A in [Q, R] (tritri-320###). + types[0] = (int) ACROSSVERT; + pos[0] = pu[0]; // A + pos[1] = (iv == 0 ? pw[1] : mi1mo3[pw[1]]); // [Q, R] + types[1] = (int) DISJOINT; + } + } else if (z2 == 3) { // (33) + if (s1 < 0) { + if (s3 > 0) { + assert(s2 > 0); // SELF_CHECK + if (s4 > 0) { + // [P, Q] overlaps [A, B] (tritri-33-+++). + types[0] = (int) ACROSSVERT; + pos[0] = pu[0]; // A + pos[1] = (iv == 0 ? pw[0] : mi1mo3[pw[0]]); // Int([P, Q]) + types[1] = (int) TOUCHEDGE; + pos[2] = (iu == 0 ? pu[0] : mi1mo3[pu[0]]); // Int([A, B]) + pos[3] = pw[1]; // Q + } else { + if (s4 == 0) { + // Q = B, [P, Q] contains [A, B] (tritri-33-++0). + types[0] = (int) ACROSSVERT; + pos[0] = pu[0]; // A + pos[1] = (iv == 0 ? pw[0] : mi1mo3[pw[0]]); // Int([P, Q]) + types[1] = (int) SHAREVERT; + pos[2] = pu[1]; // B + pos[3] = pw[1]; // Q + } else { // s4 < 0 + // [P, Q] contains [A, B] (tritri-33-++-). + types[0] = (int) ACROSSVERT; + pos[0] = pu[0]; // A + pos[1] = (iv == 0 ? pw[0] : mi1mo3[pw[0]]); // Int([P, Q]) + types[1] = (int) ACROSSVERT; + pos[2] = pu[1]; // B + pos[3] = (iv == 0 ? pw[0] : mi1mo3[pw[0]]); // Int([P, Q]) + } + } + } else { + if (s3 == 0) { + assert(s2 > 0); // SELF_CHECK + if (s4 > 0) { + // P = A, [P, Q] in [A, B] (tritri-33-+0+). + types[0] = (int) SHAREVERT; + pos[0] = pu[0]; // A + pos[1] = pw[0]; // P + types[1] = (int) TOUCHEDGE; + pos[2] = (iu == 0 ? pu[0] : mi1mo3[pu[0]]); // Int([A, B]) + pos[3] = pw[1]; // Q + } else { + if (s4 == 0) { + // [P, Q] = [A, B] (tritri-33-+00). + types[0] = (int) SHAREEDGE; + pos[0] = (iu == 0 ? pu[0] : mi1mo3[pu[0]]); // Int([A, B]) + pos[1] = (iv == 0 ? pw[0] : mi1mo3[pw[0]]); // Int([P, Q]) + types[1] = DISJOINT; + } else { // s4 < 0 + // P = A, [P, Q] contains [A, B] (tritri-33-+0-). + types[0] = (int) SHAREVERT; + pos[0] = pu[0]; // A + pos[1] = pw[0]; // P + types[1] = (int) ACROSSVERT; + pos[2] = pu[0]; // A + pos[3] = (iv == 0 ? pw[0] : mi1mo3[pw[0]]); // Int([P, Q]) + } + } + } else { // s3 < 0 + if (s2 > 0) { + if (s4 > 0) { + // [P, Q] in [A, B] (tritri-33-+-+). + types[0] = (int) TOUCHEDGE; + pos[0] = (iu == 0 ? pu[0] : mi1mo3[pu[0]]); // Int([A, B]) + pos[1] = pw[0]; // P + types[1] = (int) TOUCHEDGE; + pos[2] = (iu == 0 ? pu[0] : mi1mo3[pu[0]]); // Int([A, B]) + pos[3] = pw[1]; // Q + } else { + if (s4 == 0) { + // Q = B, [P, Q] overlaps [A, B] (tritri-33-+-0). + types[0] = (int) TOUCHEDGE; + pos[0] = (iu == 0 ? pu[0] : mi1mo3[pu[0]]); // Int([A, B]) + pos[1] = pw[0]; // P + types[1] = (int) SHAREVERT; + pos[2] = pu[1]; // B + pos[3] = pw[1]; // Q + } else { // s4 < 0 + // [P, Q] overlaps [A, B] (tritri-33-+--). + types[0] = (int) TOUCHEDGE; + pos[0] = (iu == 0 ? pu[0] : mi1mo3[pu[0]]); // Int([A, B]) + pos[1] = pw[0]; // P + types[1] = (int) ACROSSVERT; + pos[2] = pu[1]; // B + pos[3] = (iv == 0 ? pw[0] : mi1mo3[pw[0]]); // Int([P, Q]) + } + } + } else { // s2 == 0 + // P = B (tritri-33#0##). + types[0] = (int) SHAREVERT; + pos[0] = pu[1]; // B + pos[1] = pw[0]; // P + types[1] = (int) DISJOINT; + } + } + } + } else { + // Q = A (tritri-330###). + types[0] = (int) SHAREVERT; + pos[0] = pu[0]; // A + pos[1] = pw[1]; // Q + types[1] = (int) DISJOINT; + } + } + + return 1; + } // if (z1 == 3) + + if (z1 == 4) { + + if (z2 == 0) { // (40) + if (s1 < 0) { + if (s3 > 0) { + assert(s2 > 0); // SELF_CHECK + if (s4 > 0) { + // [i, j] overlaps [k, B] (tritri-40-+++) + types[0] = (int) ACROSSEDGE; + pos[0] = (iu == 0 ? pu[2] : mi1mo3[pu[2]]); // [C, A] + pos[1] = 3; // [P, Q, R] + types[1] = (int) ACROSSFACE; + pos[2] = 3; // [A, B, C] + pos[3] = (iv == 0 ? pw[1] : mi1mo3[pw[1]]); // [Q, R] + } else { + if (s4 == 0) { + // j = B, [i, j] overlaps [k, B] (tritri-40-++0) + types[0] = (int) ACROSSEDGE; + pos[0] = (iu == 0 ? pu[2] : mi1mo3[pu[2]]); // [C, A] + pos[1] = 3; // [P, Q, R] + types[1] = (int) ACROSSVERT; + pos[2] = pu[1]; // B + pos[3] = (iv == 0 ? pw[1] : mi1mo3[pw[1]]); // [Q, R] + } else { // s4 < 0 + // [i, j] contains [k, B] (tritri-40-++-) + types[0] = (int) ACROSSEDGE; + pos[0] = (iu == 0 ? pu[2] : mi1mo3[pu[2]]); // [C, A] + pos[1] = 3; // [P, Q, R] + types[1] = (int) ACROSSVERT; + pos[2] = pu[1]; // B + pos[3] = 3; // [P, Q, R] + } + } + } else { + if (s3 == 0) { + assert(s2 > 0); // SELF_CHECK + if (s4 > 0) { + // i = k, [i, j] in [k, B] (tritri-40-+0+) + types[0] = (int) ACROSSEDGE; + pos[0] = (iu == 0 ? pu[2] : mi1mo3[pu[2]]); // [C, A] + pos[1] = (iv == 0 ? pw[2] : mi1mo3[pw[2]]); // [R, P] + types[1] = (int) ACROSSFACE; + pos[2] = 3; // [A, B, C] + pos[3] = (iv == 0 ? pw[1] : mi1mo3[pw[1]]); // [Q, R] + } else { + if (s4 == 0) { + // [i, j] = [k, B] (tritri-40-+00) + types[0] = (int) ACROSSEDGE; + pos[0] = (iu == 0 ? pu[2] : mi1mo3[pu[2]]); // [C, A] + pos[1] = (iv == 0 ? pw[2] : mi1mo3[pw[2]]); // [R, P] + types[1] = (int) ACROSSVERT; + pos[2] = pu[1]; // B + pos[3] = (iv == 0 ? pw[1] : mi1mo3[pw[1]]); // [Q, R] + } else { // s4 < 0 + // i = k, [i, j] contains [k, B] (tritri-40-+0-) + types[0] = (int) ACROSSEDGE; + pos[0] = (iu == 0 ? pu[2] : mi1mo3[pu[2]]); // [C, A] + pos[1] = (iv == 0 ? pw[2] : mi1mo3[pw[2]]); // [R, P] + types[1] = (int) ACROSSVERT; + pos[2] = pu[1]; // B + pos[3] = 3; // [P, Q, R] + } + } + } else { // s3 < 0 + if (s2 > 0) { + if (s4 > 0) { + // [i, j] in [k, B] (tritri-40-+-+) + types[0] = (int) ACROSSFACE; + pos[0] = 3; // [A, B, C] + pos[1] = (iv == 0 ? pw[2] : mi1mo3[pw[2]]); // [R, P] + types[1] = (int) ACROSSFACE; + pos[2] = 3; // [A, B, C] + pos[3] = (iv == 0 ? pw[1] : mi1mo3[pw[1]]); // [Q, R] + } else { + if (s4 == 0) { + // j = B, [i, j] in [k, B] (tritri-40-+-0) + types[0] = (int) ACROSSFACE; + pos[0] = 3; // [A, B, C] + pos[1] = (iv == 0 ? pw[2] : mi1mo3[pw[2]]); // [R, P] + types[1] = (int) ACROSSVERT; + pos[2] = pu[1]; // B + pos[3] = (iv == 0 ? pw[1] : mi1mo3[pw[1]]); // [Q, R] + } else { // s4 < 0 + // [i, j] overlaps [k, B] (tritri-40-+--) + types[0] = (int) ACROSSFACE; + pos[0] = 3; // [A, B, C] + pos[1] = (iv == 0 ? pw[2] : mi1mo3[pw[2]]); // [R, P] + types[1] = (int) ACROSSVERT; + pos[2] = pu[1]; // B + pos[3] = 3; // [P, Q, R] + } + } + } else { // s2 == 0 + // assert(s4 < 0); // SELF_CHECK + // i = B (tritri-40#0##) + types[0] = (int) ACROSSVERT; + pos[0] = pu[1]; // B + pos[1] = (iv == 0 ? pw[2] : mi1mo3[pw[2]]); // [R, P] + types[1] = (int) DISJOINT; + } + } + } + } else { // s1 == 0 + // j = k (tritri-400###) + types[0] = (int) ACROSSEDGE; + pos[0] = (iu == 0 ? pu[2] : mi1mo3[pu[2]]); // [C, A] + pos[1] = (iv == 0 ? pw[1] : mi1mo3[pw[1]]); // [Q, R] + types[1] = (int) DISJOINT; + } + } else if (z2 == 2) { // (42) + if (s1 < 0) { + if (s3 > 0) { + assert(s2 > 0); // SELF_CHECK + if (s4 > 0) { + // [P, j] overlaps [k, B] (tritri-42-+++) + types[0] = (int) ACROSSEDGE; + pos[0] = (iu == 0 ? pu[2] : mi1mo3[pu[2]]); // [C, A] + pos[1] = 3; // [P, Q, R] + types[1] = (int) ACROSSFACE; + pos[2] = 3; // [A, B, C] + pos[3] = (iv == 0 ? pw[1] : mi1mo3[pw[1]]); // [Q, R] + } else { + if (s4 == 0) { + // j = B, [P, j] contains [k, B] (tritri-42-++0) + types[0] = (int) ACROSSEDGE; + pos[0] = (iu == 0 ? pu[2] : mi1mo3[pu[2]]); // [C, A] + pos[1] = 3; // [P, Q, R] + types[1] = (int) ACROSSVERT; + pos[2] = pu[1]; // B + pos[3] = (iv == 0 ? pw[1] : mi1mo3[pw[1]]); // [Q, R] + } else { // s4 < 0 + // [P, j] contains [k, B] (tritri-42-++-) + types[0] = (int) ACROSSEDGE; + pos[0] = (iu == 0 ? pu[2] : mi1mo3[pu[2]]); // [C, A] + pos[1] = 3; // [P, Q, R] + types[1] = (int) ACROSSVERT; + pos[2] = pu[1]; // B + pos[3] = 3; // [P, Q, R] + } + } + } else { + if (s3 == 0) { + assert(s2 > 0); // SELF_CHECK + if (s4 > 0) { + // P = k, [P, j] in [k, B] (tritri-42-+0+) + types[0] = (int) TOUCHEDGE; + pos[0] = (iu == 0 ? pu[2] : mi1mo3[pu[2]]); // [C, A] + pos[1] = pw[0]; // P + types[1] = (int) ACROSSFACE; + pos[2] = 3; // [A, B, C] + pos[3] = (iv == 0 ? pw[1] : mi1mo3[pw[1]]); // [Q, R] + } else { + if (s4 == 0) { + // [P, j] = [k, B] (tritri-42-+00) + types[0] = (int) TOUCHEDGE; + pos[0] = (iu == 0 ? pu[2] : mi1mo3[pu[2]]); // [C, A] + pos[1] = pw[0]; // P + types[1] = (int) ACROSSVERT; + pos[2] = pu[1]; // B + pos[3] = (iv == 0 ? pw[1] : mi1mo3[pw[1]]); // [Q, R] + } else { // s4 < 0 + // P = k, [P, j] in [k, B] (tritri-42-+0-) + types[0] = (int) TOUCHEDGE; + pos[0] = (iu == 0 ? pu[2] : mi1mo3[pu[2]]); // [C, A] + pos[1] = pw[0]; // P + types[1] = (int) ACROSSVERT; + pos[2] = pu[1]; // B + pos[3] = 3; // [P, Q, R] + } + } + } else { // s3 < 0 + if (s2 > 0) { + if (s4 > 0) { + // [P, j] in [k, B] (tritri-42-+-+) + types[0] = (int) TOUCHFACE; + pos[0] = 3; // [A, B, C] + pos[1] = pw[0]; // P + types[1] = (int) ACROSSFACE; + pos[2] = 3; // [A, B, C] + pos[3] = (iv == 0 ? pw[1] : mi1mo3[pw[1]]); // [Q, R] + } else { + if (s4 == 0) { + // j = B, [P, j] in [k, B] (tritri-42-+-0) + types[0] = (int) TOUCHFACE; + pos[0] = 3; // [A, B, C] + pos[1] = pw[0]; // P + types[1] = (int) ACROSSVERT; + pos[2] = pu[1]; // B + pos[3] = (iv == 0 ? pw[1] : mi1mo3[pw[1]]); // [Q, R] + } else { // s4 < 0 + // [P, j] overlaps [k, B] (tritri-42-+--) + types[0] = (int) TOUCHFACE; + pos[0] = 3; // [A, B, C] + pos[1] = pw[0]; // P + types[1] = (int) ACROSSVERT; + pos[2] = pu[1]; // B + pos[3] = 3; // [P, Q, R] + } + } + } else { // s2 == 0 + // assert(s4 < 0); // SELF_CHECK + // P = B (tritri-42#0##) + types[0] = (int) SHAREVERT; + pos[0] = pu[1]; // B + pos[1] = pw[0]; // P + types[1] = (int) DISJOINT; + } + } + } + } else { // s1 == 0 + // j = k (tritri-420###) + types[0] = (int) ACROSSEDGE; + pos[0] = (iu == 0 ? pu[2] : mi1mo3[pu[2]]); // [C, A] + pos[1] = (iv == 0 ? pw[1] : mi1mo3[pw[1]]); // [Q, R] + types[1] = (int) DISJOINT; + } + } else if (z2 == 3) { // (43) + if (s1 < 0) { + if (s3 > 0) { + assert(s2 > 0); // SELF_CHECK + if (s4 > 0) { + // [P, Q] overlaps [k, B] (tritri-43-+++) + types[0] = (int) ACROSSEDGE; + pos[0] = (iu == 0 ? pu[2] : mi1mo3[pu[2]]); // [C, A] + pos[1] = (iv == 0 ? pw[0] : mi1mo3[pw[0]]); // Int([P, Q]) + types[1] = (int) TOUCHFACE; + pos[2] = 3; // [A, B, C] + pos[3] = pw[1]; // Q + } else { + if (s4 == 0) { + // Q = B, [P, Q] contains [k, B] (tritri-43-++0) + types[0] = (int) ACROSSEDGE; + pos[0] = (iu == 0 ? pu[2] : mi1mo3[pu[2]]); // [C, A] + pos[1] = (iv == 0 ? pw[0] : mi1mo3[pw[0]]); // Int([P, Q]) + types[1] = (int) SHAREVERT; + pos[2] = pu[1]; // B + pos[3] = pw[1]; // Q + } else { // s4 < 0 + // [P, Q] contains [k, B] (tritri-43-++-) + types[0] = (int) ACROSSEDGE; + pos[0] = (iu == 0 ? pu[2] : mi1mo3[pu[2]]); // [C, A] + pos[1] = (iv == 0 ? pw[0] : mi1mo3[pw[0]]); // Int([P, Q]) + types[1] = (int) ACROSSVERT; + pos[2] = pu[1]; // B + pos[3] = (iv == 0 ? pw[0] : mi1mo3[pw[0]]); // Int([P, Q]) + } + } + } else { + if (s3 == 0) { + assert(s2 > 0); // SELF_CHECK + if (s4 > 0) { + // P = k, [P, Q] in [k, B] (tritri-43-+0+) + types[0] = (int) TOUCHEDGE; + pos[0] = (iu == 0 ? pu[2] : mi1mo3[pu[2]]); // [C, A] + pos[1] = pw[0]; // P + types[1] = (int) TOUCHFACE; + pos[2] = 3; // [A, B, C] + pos[3] = pw[1]; // Q + } else { + if (s4 == 0) { + // [P, Q] = [k, B] (tritri-43-+00) + types[0] = (int) TOUCHEDGE; + pos[0] = (iu == 0 ? pu[2] : mi1mo3[pu[2]]); // [C, A] + pos[1] = pw[0]; // P + types[1] = (int) SHAREVERT; + pos[2] = pu[1]; // B + pos[3] = pw[1]; // Q + } else { // s4 < 0 + // P = k, [P, Q] contains [k, B] (tritri-43-+0-) + types[0] = (int) TOUCHEDGE; + pos[0] = (iu == 0 ? pu[2] : mi1mo3[pu[2]]); // [C, A] + pos[1] = pw[0]; // P + types[1] = (int) ACROSSVERT; + pos[2] = pu[1]; // B + pos[3] = (iv == 0 ? pw[0] : mi1mo3[pw[0]]); // Int([P, Q]) + } + } + } else { // s3 < 0 + if (s2 > 0) { + if (s4 > 0) { + // [P, Q] in [k, B] (tritri-43-+-+) + types[0] = (int) TOUCHFACE; + pos[0] = 3; // [A, B, C] + pos[1] = pw[0]; // P + types[1] = (int) TOUCHFACE; + pos[2] = 3; // [A, B, C] + pos[3] = pw[1]; // Q + } else { + if (s4 == 0) { + // Q = B, [P, Q] in [k, B] (tritri-43-+-0) + types[0] = (int) TOUCHFACE; + pos[0] = 3; // [A, B, C] + pos[1] = pw[0]; // P + types[1] = (int) SHAREVERT; + pos[2] = pu[1]; // B + pos[3] = pw[1]; // Q + } else { // s4 < 0 + // [P, Q] overlaps [k, B] (tritri-43-+--) + types[0] = (int) TOUCHFACE; + pos[0] = 3; // [A, B, C] + pos[1] = pw[0]; // P + types[1] = (int) ACROSSVERT; + pos[2] = pu[1]; // B + pos[3] = (iv == 0 ? pw[0] : mi1mo3[pw[0]]); // Int([P, Q]) + } + } + } else { // s2 == 0 + // assert(s4 < 0); // SELF_CHECK + // P = B (tritri-43#0##) + types[0] = (int) SHAREVERT; + pos[0] = pu[1]; // B + pos[1] = pw[0]; // P + types[1] = (int) DISJOINT; + } + } + } + } else { // s1 == 0 + // Q = k (tritri-430###) + types[0] = (int) TOUCHEDGE; + pos[0] = (iu == 0 ? pu[2] : mi1mo3[pu[2]]); // [C, A] + pos[1] = pw[1]; // Q + types[1] = (int) DISJOINT; + } + } + + return 1; + } // if (z1 == 4) +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// insphere_sos() Insphere test with symbolic perturbation. // +// // +// Given four points pa, pb, pc, and pd, test if the point pe lies inside or // +// outside the circumscirbed sphere of the four points. Here we assume that // +// the orientation of the sequence {pa, pb, pc, pd} is negative (NOT zero), // +// i.e., pd lies at the negative side of the plane defined by pa, pb, and pc.// +// // +// Return a positive value (> 0) if pe lies outside, a negative value (< 0) // +// if pe lies inside the sphere, the returned value will not be zero. // +// // +/////////////////////////////////////////////////////////////////////////////// + +REAL tetgenmesh::insphere_sos(point pa, point pb, point pc, point pd, point pe) +{ + REAL sign; + + inspherecount++; + + sign = insphere(pa, pb, pc, pd, pe); + if (sign != 0.0) { + return sign; + } + + insphere_sos_count++; + + // Symbolic perturbation. + point pt[5], swappt; + REAL oriA, oriB; + int swaps, count; + int n, i; + + pt[0] = pa; + pt[1] = pb; + pt[2] = pc; + pt[3] = pd; + pt[4] = pe; + + // Sort the five points such that their indices are in the increasing + // order. An optimized bubble sort algorithm is used, i.e., it has + // the worst case O(n^2) runtime, but it is usually much faster. + swaps = 0; // Record the total number of swaps. + n = 5; + do { + count = 0; + n = n - 1; + for (i = 0; i < n; i++) { + if (pointmark(pt[i]) > pointmark(pt[i+1])) { + swappt = pt[i]; pt[i] = pt[i+1]; pt[i+1] = swappt; + count++; + } + } + swaps += count; + } while (count > 0); // Continue if some points are swapped. + + oriA = orient3d(pt[1], pt[2], pt[3], pt[4]); + if (oriA != 0.0) { + // Flip the sign if there are odd number of swaps. + if ((swaps % 2) != 0) oriA = -oriA; + return oriA; + } + + oriB = -orient3d(pt[0], pt[2], pt[3], pt[4]); + assert(oriB != 0.0); // SELF_CHECK + // Flip the sign if there are odd number of swaps. + if ((swaps % 2) != 0) oriB = -oriB; + return oriB; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// isincircle() Test if d lies inside the circumcircle of abc. // +// // +// Return a positive value (> 0) if pd lies outside, a negative value (< 0) // +// if pd lies inside the circle, a zero if pd lies on the circle. // +// // +/////////////////////////////////////////////////////////////////////////////// + +/* This version (which uses insphere test) is not save. It may give + undesired result when four points are nearly coplanar. + An example is in fig/dump-incircle3d-case1.*/ + +/*REAL tetgenmesh::incircle3d(point pa, point pb, point pc, point pd) +{ + REAL area2[2], n1[3], n2[3], pe[3]; + REAL sign, L, len; + + // Calculate the areas of the two triangles [a, b, c] and [b, a, d]. + facenormal(pa, pb, pc, n1, 1); + area2[0] = DOT(n1, n1); + facenormal(pb, pa, pd, n2, 1); + area2[1] = DOT(n2, n2); + L = DIST(pa, pb); + + if (area2[0] > area2[1]) { + // Choose [a, b, c] as the base triangle. + len = sqrt(area2[0]); + n1[0] /= len; + n1[1] /= len; + n1[2] /= len; + L += DIST(pb, pc); + L += DIST(pc, pa); + L /= 3.0; + pe[0] = pa[0] + L * n1[0]; + pe[1] = pa[1] + L * n1[1]; + pe[2] = pa[2] + L * n1[2]; + sign = insphere(pa, pb, pc, pe, pd); + } else { + // Choose [b, a, d] as the base triangle. + len = sqrt(area2[1]); + n2[0] /= len; + n2[1] /= len; + n2[2] /= len; + L += DIST(pa, pd); + L += DIST(pd, pb); + L /= 3.0; + pe[0] = pa[0] + L * n2[0]; + pe[1] = pa[1] + L * n2[1]; + pe[2] = pa[2] + L * n2[2]; + sign = insphere(pb, pa, pd, pe, pc); + } + + return sign; +}*/ + +/* This code had problem with file2.poly*/ +REAL tetgenmesh::incircle3d(point pa, point pb, point pc, point pd) +{ + REAL area2[2], n1[3], n2[3], c[3]; + REAL sign, r, d; + + // Calculate the areas of the two triangles [a, b, c] and [b, a, d]. + facenormal(pa, pb, pc, n1, 1); + area2[0] = DOT(n1, n1); + facenormal(pb, pa, pd, n2, 1); + area2[1] = DOT(n2, n2); + + if (area2[0] > area2[1]) { + // Choose [a, b, c] as the base triangle. + assert(area2[0] > 0); // SELF_CHECK + circumsphere(pa, pb, pc, NULL, c, &r); + d = DIST(c, pd); + } else { + // Choose [b, a, d] as the base triangle. + assert(area2[1] > 0); // SELF_CHECK + circumsphere(pb, pa, pd, NULL, c, &r); + d = DIST(c, pc); + } + + sign = d - r; + if (fabs(sign) / r < b->epsilon) { + sign = 0; + } + + return sign; +} + +/* +REAL tetgenmesh::incircle3d(point pa, point pb, point pc, point pd) +{ + point pk, pl, pm, pn; + REAL area2[4], n[3], c[3]; + REAL amax, sign, r, l, q; + int imax; + + // Calculate the areas of the four triangles in a, b, c, and d. + // Get the base triangle which has the largest area. + facenormal(pa, pb, pc, n, 1); + area2[0] = DOT(n, n); + facenormal(pb, pa, pd, n, 1); + area2[1] = DOT(n, n); + if (area2[0] < area2[1]) { + amax = area2[1]; imax = 1; + } else { + amax = area2[0]; imax = 0; + } + facenormal(pc, pd, pb, n, 1); + area2[2] = DOT(n, n); + if (amax < area2[2]) { + amax = area2[2]; imax = 2; + } + facenormal(pd, pc, pa, n, 1); + area2[3] = DOT(n, n); + if (amax < area2[3]) { + amax = area2[3]; imax = 3; + } + + // Permute the vertices s. t. the base triangle is (pk, pl, pm). + if (imax == 0) { + pk = pa; pl = pb; pm = pc; pn = pd; sign = 1.0; + } else if (imax == 1) { + pk = pb; pl = pa; pm = pd; pn = pc; sign = 1.0; + } else if (imax == 2) { + pk = pc; pl = pd; pm = pb; pn = pa; sign = -1.0; + } else { + pk = pd; pl = pc; pm = pa; pn = pb; sign = -1.0; + } + + // Make sure that the base triangle is not degenerate. + l = DIST(pk, pl); + l += DIST(pl, pm); + l += DIST(pm, pk); + l /= 3.0; + + if (sqrt(amax) > (l * l * b->epsilon)) { + // Calculate the circumcenter and radius. + circumsphere(pk, pl, pm, NULL, c, &r); + l = DIST(c, pn); + q = fabs((l - r) / r); + } else { + // A (nearly) degenerate base triangle. + assert(0); // Not handle yet. + q = 0; + } + + if (q > b->epsilon) { + return (l - r) * sign; // Adjust the sign. + } else { + return 0; // Round to zero. + } +}*/ + +/////////////////////////////////////////////////////////////////////////////// +// // +// iscoplanar() Check if four points are approximately coplanar. // +// // +// 'tol' is the relative error tolerance. The coplanarity is determined by // +// the equation q < tol, where q = fabs(6 * vol) / L^3, vol is the volume of // +// the tet klmn, and L is the average edge length of the tet. // +// // +/////////////////////////////////////////////////////////////////////////////// + +bool tetgenmesh::iscoplanar(point k, point l, point m, point n, REAL ori) +{ + REAL L, q; + + L = DIST(k, l); + L += DIST(l, m); + L += DIST(m, k); + L += DIST(k, n); + L += DIST(l, n); + L += DIST(m, n); + assert(L > 0.0); // SELF_CHECK + L /= 6.0; + + q = fabs(ori) / (L * L * L); + + return q <= b->epsilon; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// interiorangle() Return the interior angle (0 - 2 * PI) between vectors // +// o->p1 and o->p2. // +// // +// 'n' is the normal of the plane containing face (o, p1, p2). The interior // +// angle is the total angle rotating from o->p1 around n to o->p2. Exchange // +// the position of p1 and p2 will get the complement angle of the other one. // +// i.e., interiorangle(o, p1, p2) = 2 * PI - interiorangle(o, p2, p1). Set // +// 'n' be NULL if you only want the interior angle between 0 - PI. // +// // +/////////////////////////////////////////////////////////////////////////////// + +REAL tetgenmesh::interiorangle(point o, point p1, point p2, REAL* n) +{ + REAL v1[3], v2[3], np[3]; + REAL theta, costheta, lenlen; + REAL ori, len1, len2; + + // Get the interior angle (0 - PI) between o->p1, and o->p2. + v1[0] = p1[0] - o[0]; + v1[1] = p1[1] - o[1]; + v1[2] = p1[2] - o[2]; + v2[0] = p2[0] - o[0]; + v2[1] = p2[1] - o[1]; + v2[2] = p2[2] - o[2]; + len1 = sqrt(DOT(v1, v1)); + len2 = sqrt(DOT(v2, v2)); + lenlen = len1 * len2; + assert(lenlen != 0.0); // SELF_CHECK + costheta = DOT(v1, v2) / lenlen; + if (costheta > 1.0) { + costheta = 1.0; // Roundoff. + } else if (costheta < -1.0) { + costheta = -1.0; // Roundoff. + } + theta = acos(costheta); + if (n != NULL) { + // Get a point above the face (o, p1, p2); + np[0] = o[0] + n[0]; + np[1] = o[1] + n[1]; + np[2] = o[2] + n[2]; + // Adjust theta (0 - 2 * PI). + ori = orient3d(p1, o, np, p2); + if (ori > 0.0) { + theta = 2 * PI - theta; + } + } + + return theta; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// facenormal() Calculate the normal of the face. // +// // +// The normal of the face abc can be calculated by the cross product of 2 of // +// its 3 edge vectors. A better choice of two edge vectors will reduce the // +// numerical error during the calculation. Burdakov proved that the optimal // +// basis problem is equivalent to the minimum spanning tree problem with the // +// edge length be the functional, see Burdakov, "A greedy algorithm for the // +// optimal basis problem", BIT 37:3 (1997), 591-599. If 'pivot' > 0, the two // +// short edges in abc are chosen for the calculation. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::facenormal(point pa, point pb, point pc, REAL *n, int pivot) +{ + REAL v1[3], v2[3], v3[3], *pv1, *pv2; + REAL L1, L2, L3; + + v1[0] = pb[0] - pa[0]; // edge vector v1: a->b + v1[1] = pb[1] - pa[1]; + v1[2] = pb[2] - pa[2]; + v2[0] = pa[0] - pc[0]; // edge vector v2: c->a + v2[1] = pa[1] - pc[1]; + v2[2] = pa[2] - pc[2]; + + // Default, normal is calculated by: v1 x (-v2) (see Fig. fnormal). + if (pivot > 0) { + // Choose edge vectors by Burdakov's algorithm. + v3[0] = pc[0] - pb[0]; // edge vector v3: b->c + v3[1] = pc[1] - pb[1]; + v3[2] = pc[2] - pb[2]; + L1 = DOT(v1, v1); + L2 = DOT(v2, v2); + L3 = DOT(v3, v3); + // Sort the three edge lengths. + if (L1 < L2) { + if (L2 < L3) { + pv1 = v1; pv2 = v2; // n = v1 x (-v2). + } else { + pv1 = v3; pv2 = v1; // n = v3 x (-v1). + } + } else { + if (L1 < L3) { + pv1 = v1; pv2 = v2; // n = v1 x (-v2). + } else { + pv1 = v2; pv2 = v3; // n = v2 x (-v3). + } + } + } else { + pv1 = v1; pv2 = v2; // n = v1 x (-v2). + } + + // Calculate the face normal. + CROSS(pv1, pv2, n); + // Inverse the direction; + n[0] = -n[0]; + n[1] = -n[1]; + n[2] = -n[2]; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// circumsphere() Calculate the smallest circumsphere (center and radius) // +// of the given three or four points. // +// // +// The circumsphere of four points (a tetrahedron) is unique if they are not // +// degenerate. If 'pd == NULL', the smallest circumsphere of three points is // +// the diametral sphere of the triangle (pa, pb, pc). // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::circumsphere(point pa, point pb, point pc, point pd, + REAL* cent, REAL* radius) +{ + REAL A[4][4], rhs[4], D; + int indx[4]; + + // Compute the coefficient matrix A (3x3). + A[0][0] = pb[0] - pa[0]; + A[0][1] = pb[1] - pa[1]; + A[0][2] = pb[2] - pa[2]; + A[1][0] = pc[0] - pa[0]; + A[1][1] = pc[1] - pa[1]; + A[1][2] = pc[2] - pa[2]; + if (pd != NULL) { + A[2][0] = pd[0] - pa[0]; + A[2][1] = pd[1] - pa[1]; + A[2][2] = pd[2] - pa[2]; + } else { + CROSS(A[0], A[1], A[2]); + } + + // Compute the right hand side vector b (3x1). + rhs[0] = 0.5 * DOT(A[0], A[0]); + rhs[1] = 0.5 * DOT(A[1], A[1]); + if (pd != NULL) { + rhs[2] = 0.5 * DOT(A[2], A[2]); + } else { + rhs[2] = 0.0; + } + + // Solve the 3 by 3 equations use LU decomposition with partial pivoting + // and backward and forward substitute.. + if (!lu_decmp(A, 3, indx, &D, 0)) { + assert(0); // No solution. + } + lu_solve(A, 3, indx, rhs, 0); + if (cent != (REAL *) NULL) { + cent[0] = pa[0] + rhs[0]; + cent[1] = pa[1] + rhs[1]; + cent[2] = pa[2] + rhs[2]; + } + if (radius != (REAL *) NULL) { + *radius = sqrt(rhs[0] * rhs[0] + rhs[1] * rhs[1] + rhs[2] * rhs[2]); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// lu_decmp() Compute the LU decomposition of a matrix. // +// // +// Compute the LU decomposition of a (non-singular) square matrix A using // +// partial pivoting and implicit row exchanges. The result is: // +// A = P * L * U, // +// where P is a permutation matrix, L is unit lower triangular, and U is // +// upper triangular. The factored form of A is used in combination with // +// 'lu_solve()' to solve linear equations: Ax = b, or invert a matrix. // +// // +// The inputs are a square matrix 'lu[N..n+N-1][N..n+N-1]', it's size is 'n'.// +// On output, 'lu' is replaced by the LU decomposition of a rowwise permuta- // +// tion of itself, 'ps[N..n+N-1]' is an output vector that records the row // +// permutation effected by the partial pivoting, effectively, 'ps' array // +// tells the user what the permutation matrix P is; 'd' is output as +1/-1 // +// depending on whether the number of row interchanges was even or odd, // +// respectively. // +// // +// Return true if the LU decomposition is successfully computed, otherwise, // +// return false in case that A is a singular matrix. // +// // +/////////////////////////////////////////////////////////////////////////////// + +bool tetgenmesh::lu_decmp(REAL lu[4][4], int n, int* ps, REAL* d, int N) +{ + REAL scales[4]; + REAL pivot, biggest, mult, tempf; + int pivotindex = 0; + int i, j, k; + + *d = 1.0; // No row interchanges yet. + + for (i = N; i < n + N; i++) { // For each row. + // Find the largest element in each row for row equilibration + biggest = 0.0; + for (j = N; j < n + N; j++) + if (biggest < (tempf = fabs(lu[i][j]))) + biggest = tempf; + if (biggest != 0.0) + scales[i] = 1.0 / biggest; + else { + scales[i] = 0.0; + return false; // Zero row: singular matrix. + } + ps[i] = i; // Initialize pivot sequence. + } + + for (k = N; k < n + N - 1; k++) { // For each column. + // Find the largest element in each column to pivot around. + biggest = 0.0; + for (i = k; i < n + N; i++) { + if (biggest < (tempf = fabs(lu[ps[i]][k]) * scales[ps[i]])) { + biggest = tempf; + pivotindex = i; + } + } + if (biggest == 0.0) { + return false; // Zero column: singular matrix. + } + if (pivotindex != k) { // Update pivot sequence. + j = ps[k]; + ps[k] = ps[pivotindex]; + ps[pivotindex] = j; + *d = -(*d); // ...and change the parity of d. + } + + // Pivot, eliminating an extra variable each time + pivot = lu[ps[k]][k]; + for (i = k + 1; i < n + N; i++) { + lu[ps[i]][k] = mult = lu[ps[i]][k] / pivot; + if (mult != 0.0) { + for (j = k + 1; j < n + N; j++) + lu[ps[i]][j] -= mult * lu[ps[k]][j]; + } + } + } + + // (lu[ps[n + N - 1]][n + N - 1] == 0.0) ==> A is singular. + return lu[ps[n + N - 1]][n + N - 1] != 0.0; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// lu_solve() Solves the linear equation: Ax = b, after the matrix A // +// has been decomposed into the lower and upper triangular // +// matrices L and U, where A = LU. // +// // +// 'lu[N..n+N-1][N..n+N-1]' is input, not as the matrix 'A' but rather as // +// its LU decomposition, computed by the routine 'lu_decmp'; 'ps[N..n+N-1]' // +// is input as the permutation vector returned by 'lu_decmp'; 'b[N..n+N-1]' // +// is input as the right-hand side vector, and returns with the solution // +// vector. 'lu', 'n', and 'ps' are not modified by this routine and can be // +// left in place for successive calls with different right-hand sides 'b'. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::lu_solve(REAL lu[4][4], int n, int* ps, REAL* b, int N) +{ + int i, j; + REAL X[4], dot; + + for (i = N; i < n + N; i++) X[i] = 0.0; + + // Vector reduction using U triangular matrix. + for (i = N; i < n + N; i++) { + dot = 0.0; + for (j = N; j < i + N; j++) + dot += lu[ps[i]][j] * X[j]; + X[i] = b[ps[i]] - dot; + } + + // Back substitution, in L triangular matrix. + for (i = n + N - 1; i >= N; i--) { + dot = 0.0; + for (j = i + 1; j < n + N; j++) + dot += lu[ps[i]][j] * X[j]; + X[i] = (X[i] - dot) / lu[ps[i]][i]; + } + + for (i = N; i < n + N; i++) b[i] = X[i]; +} + +#endif // #ifndef geomCXX \ No newline at end of file diff --git a/contrib/TetgenNew/io.cxx b/contrib/TetgenNew/io.cxx new file mode 100644 index 0000000000..9cf198a6bd --- /dev/null +++ b/contrib/TetgenNew/io.cxx @@ -0,0 +1,3148 @@ +#ifndef tetgenioCXX +#define tetgenioCXX + +#include "tetgen.h" + +/////////////////////////////////////////////////////////////////////////////// +// // +// initialize() Initialize all variables of 'tetgenio'. // +// // +// It is called by the only class constructor 'tetgenio()' implicitly. Thus, // +// all variables are guaranteed to be initialized. Each array is initialized // +// to be a 'NULL' pointer, and its length is equal zero. Some variables have // +// their default value, 'firstnumber' equals zero, 'mesh_dim' equals 3, and // +// 'numberofcorners' equals 4. Another possible use of this routine is to // +// call it before to re-use an object. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenio::initialize() +{ + firstnumber = 0; // Default item index is numbered from Zero. + mesh_dim = 3; // Default mesh dimension is 3. + useindex = true; + + pointlist = (REAL *) NULL; + pointattributelist = (REAL *) NULL; + pointmtrlist = (REAL *) NULL; + pointmarkerlist = (int *) NULL; + numberofpoints = 0; + numberofpointattributes = 0; + numberofpointmtrs = 0; + + tetrahedronlist = (int *) NULL; + tetrahedronattributelist = (REAL *) NULL; + tetrahedronvolumelist = (REAL *) NULL; + neighborlist = (int *) NULL; + numberoftetrahedra = 0; + numberofcorners = 4; // Default is 4 nodes per element. + numberoftetrahedronattributes = 0; + + trifacelist = (int *) NULL; + adjtetlist = (int *) NULL; + trifacemarkerlist = (int *) NULL; + numberoftrifaces = 0; + + facetlist = (facet *) NULL; + facetmarkerlist = (int *) NULL; + numberoffacets = 0; + + edgelist = (int *) NULL; + edgemarkerlist = (int *) NULL; + numberofedges = 0; + + holelist = (REAL *) NULL; + numberofholes = 0; + + regionlist = (REAL *) NULL; + numberofregions = 0; + + facetconstraintlist = (REAL *) NULL; + numberoffacetconstraints = 0; + segmentconstraintlist = (REAL *) NULL; + numberofsegmentconstraints = 0; + + pbcgrouplist = (pbcgroup *) NULL; + numberofpbcgroups = 0; + + vpointlist = (REAL *) NULL; + vedgelist = (voroedge *) NULL; + vfacetlist = (vorofacet *) NULL; + vcelllist = (int **) NULL; + numberofvpoints = 0; + numberofvedges = 0; + numberofvfacets = 0; + numberofvcells = 0; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// deinitialize() Free the memory allocated in 'tetgenio'. // +// // +// It is called by the class destructor '~tetgenio()' implicitly. Hence, the // +// occupied memory by arrays of an object will be automatically released on // +// the deletion of the object. However, this routine assumes that the memory // +// is allocated by C++ memory allocation operator 'new', thus it is freed by // +// the C++ array deletor 'delete []'. If one uses the C/C++ library function // +// 'malloc()' to allocate memory for arrays, one has to free them with the // +// 'free()' function, and call routine 'initialize()' once to disable this // +// routine on deletion of the object. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenio::deinitialize() +{ + facet *f; + polygon *p; + pbcgroup *pg; + int i, j; + + if (pointlist != (REAL *) NULL) { + delete [] pointlist; + } + if (pointattributelist != (REAL *) NULL) { + delete [] pointattributelist; + } + if (pointmtrlist != (REAL *) NULL) { + delete [] pointmtrlist; + } + if (pointmarkerlist != (int *) NULL) { + delete [] pointmarkerlist; + } + + if (tetrahedronlist != (int *) NULL) { + delete [] tetrahedronlist; + } + if (tetrahedronattributelist != (REAL *) NULL) { + delete [] tetrahedronattributelist; + } + if (tetrahedronvolumelist != (REAL *) NULL) { + delete [] tetrahedronvolumelist; + } + if (neighborlist != (int *) NULL) { + delete [] neighborlist; + } + + if (trifacelist != (int *) NULL) { + delete [] trifacelist; + } + if (adjtetlist != (int *) NULL) { + delete [] adjtetlist; + } + if (trifacemarkerlist != (int *) NULL) { + delete [] trifacemarkerlist; + } + + if (edgelist != (int *) NULL) { + delete [] edgelist; + } + if (edgemarkerlist != (int *) NULL) { + delete [] edgemarkerlist; + } + + if (facetlist != (facet *) NULL) { + for (i = 0; i < numberoffacets; i++) { + f = &facetlist[i]; + for (j = 0; j < f->numberofpolygons; j++) { + p = &f->polygonlist[j]; + delete [] p->vertexlist; + } + delete [] f->polygonlist; + if (f->holelist != (REAL *) NULL) { + delete [] f->holelist; + } + } + delete [] facetlist; + } + if (facetmarkerlist != (int *) NULL) { + delete [] facetmarkerlist; + } + + if (holelist != (REAL *) NULL) { + delete [] holelist; + } + if (regionlist != (REAL *) NULL) { + delete [] regionlist; + } + if (facetconstraintlist != (REAL *) NULL) { + delete [] facetconstraintlist; + } + if (segmentconstraintlist != (REAL *) NULL) { + delete [] segmentconstraintlist; + } + if (pbcgrouplist != (pbcgroup *) NULL) { + for (i = 0; i < numberofpbcgroups; i++) { + pg = &(pbcgrouplist[i]); + if (pg->pointpairlist != (int *) NULL) { + delete [] pg->pointpairlist; + } + } + delete [] pbcgrouplist; + } + if (vpointlist != (REAL *) NULL) { + delete [] vpointlist; + } + if (vedgelist != (voroedge *) NULL) { + delete [] vedgelist; + } + if (vfacetlist != (vorofacet *) NULL) { + for (i = 0; i < numberofvfacets; i++) { + delete [] vfacetlist[i].elist; + } + delete [] vfacetlist; + } + if (vcelllist != (int **) NULL) { + for (i = 0; i < numberofvcells; i++) { + delete [] vcelllist[i]; + } + delete [] vcelllist; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// load_node_call() Load a list of nodes. // +// // +// It is a support routine for routines: 'load_nodes()', 'load_poly()', and // +// 'load_tetmesh()'. 'infile' is the file handle contains the node list. It // +// may point to a .node, or .poly or .smesh file. 'markers' indicates each // +// node contains an additional marker (integer) or not. 'infilename' is the // +// name of the file being read, it is only appeared in error message. // +// // +// The 'firstnumber' (0 or 1) is automatically determined by the number of // +// the first index of the first point. // +// // +/////////////////////////////////////////////////////////////////////////////// + +bool tetgenio::load_node_call(FILE* infile,int markers,const char* infilename) +{ + char inputline[INPUTLINESIZE]; + char *stringptr; + REAL x, y, z, attrib; + int firstnode, currentmarker; + int index, attribindex; + int i, j; + + // Initialize 'pointlist', 'pointattributelist', and 'pointmarkerlist'. + pointlist = new REAL[numberofpoints * 3]; + if (pointlist == (REAL *) NULL) { + printf("Error: Out of memory.\n"); + terminatetetgen(1); + } + if (numberofpointattributes > 0) { + pointattributelist = new REAL[numberofpoints * numberofpointattributes]; + if (pointattributelist == (REAL *) NULL) { + printf("Error: Out of memory.\n"); + terminatetetgen(1); + } + } + if (markers) { + pointmarkerlist = new int[numberofpoints]; + if (pointmarkerlist == (int *) NULL) { + printf("Error: Out of memory.\n"); + terminatetetgen(1); + } + } + + // Read the point section. + index = 0; + attribindex = 0; + for (i = 0; i < numberofpoints; i++) { + stringptr = readnumberline(inputline, infile, infilename); + if (useindex) { + if (i == 0) { + firstnode = (int) strtol (stringptr, &stringptr, 0); + if ((firstnode == 0) || (firstnode == 1)) { + firstnumber = firstnode; + } + } + stringptr = findnextnumber(stringptr); + } // if (useindex) + if (*stringptr == '\0') { + printf("Error: Point %d has no x coordinate.\n", firstnumber + i); + break; + } + x = (REAL) strtod(stringptr, &stringptr); + stringptr = findnextnumber(stringptr); + if (*stringptr == '\0') { + printf("Error: Point %d has no y coordinate.\n", firstnumber + i); + break; + } + y = (REAL) strtod(stringptr, &stringptr); + if (mesh_dim == 3) { + stringptr = findnextnumber(stringptr); + if (*stringptr == '\0') { + printf("Error: Point %d has no z coordinate.\n", firstnumber + i); + break; + } + z = (REAL) strtod(stringptr, &stringptr); + } else { + z = 0.0; // mesh_dim == 2; + } + pointlist[index++] = x; + pointlist[index++] = y; + pointlist[index++] = z; + // Read the point attributes. + for (j = 0; j < numberofpointattributes; j++) { + stringptr = findnextnumber(stringptr); + if (*stringptr == '\0') { + attrib = 0.0; + } else { + attrib = (REAL) strtod(stringptr, &stringptr); + } + pointattributelist[attribindex++] = attrib; + } + if (markers) { + // Read a point marker. + stringptr = findnextnumber(stringptr); + if (*stringptr == '\0') { + currentmarker = 0; + } else { + currentmarker = (int) strtol (stringptr, &stringptr, 0); + } + pointmarkerlist[i] = currentmarker; + } + } + if (i < numberofpoints) { + // Failed to read points due to some error. + delete [] pointlist; + pointlist = (REAL *) NULL; + if (markers) { + delete [] pointmarkerlist; + pointmarkerlist = (int *) NULL; + } + if (numberofpointattributes > 0) { + delete [] pointattributelist; + pointattributelist = (REAL *) NULL; + } + numberofpoints = 0; + return false; + } + return true; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// load_node() Load a list of nodes from a .node file. // +// // +// 'filename' is the inputfile without suffix. The node list is in 'filename.// +// node'. On completion, the node list is returned in 'pointlist'. // +// // +/////////////////////////////////////////////////////////////////////////////// + +bool tetgenio::load_node(const char* filename) +{ + FILE *infile; + char innodefilename[FILENAMESIZE]; + char inputline[INPUTLINESIZE]; + char *stringptr; + int markers; + + // Assembling the actual file names we want to open. + strcpy(innodefilename, filename); + strcat(innodefilename, ".node"); + + // Try to open a .node file. + infile = fopen(innodefilename, "r"); + if (infile == (FILE *) NULL) { + printf("File I/O Error: Cannot access file %s.\n", innodefilename); + return false; + } + printf("Opening %s.\n", innodefilename); + // Read the first line of the file. + stringptr = readnumberline(inputline, infile, innodefilename); + // Is this list of points generated from rbox? + stringptr = strstr(inputline, "rbox"); + if (stringptr == NULL) { + // Read number of points, number of dimensions, number of point + // attributes, and number of boundary markers. + stringptr = inputline; + numberofpoints = (int) strtol (stringptr, &stringptr, 0); + stringptr = findnextnumber(stringptr); + if (*stringptr == '\0') { + mesh_dim = 3; + } else { + mesh_dim = (int) strtol (stringptr, &stringptr, 0); + } + stringptr = findnextnumber(stringptr); + if (*stringptr == '\0') { + numberofpointattributes = 0; + } else { + numberofpointattributes = (int) strtol (stringptr, &stringptr, 0); + } + stringptr = findnextnumber(stringptr); + if (*stringptr == '\0') { + markers = 0; + } else { + markers = (int) strtol (stringptr, &stringptr, 0); + } + } else { + // It is a rbox (qhull) input file. + stringptr = inputline; + // Get the dimension. + mesh_dim = (int) strtol (stringptr, &stringptr, 0); + // Get the number of points. + stringptr = readnumberline(inputline, infile, innodefilename); + numberofpoints = (int) strtol (stringptr, &stringptr, 0); + // There is no index column. + useindex = 0; + } + + if (numberofpoints < (mesh_dim + 1)) { + printf("Input error: TetGen needs at least %d points.\n", mesh_dim + 1); + fclose(infile); + return false; + } + + // Load the list of nodes. + if (!load_node_call(infile, markers, innodefilename)) { + fclose(infile); + return false; + } + fclose(infile); + return true; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// load_pbc() Load a list of pbc groups into 'pbcgrouplist'. // +// // +// 'filename' is the filename of the original inputfile without suffix. The // +// pbc groups are found in file 'filename.pbc'. // +// // +// This routine will be called both in load_poly() and load_tetmesh(). // +// // +/////////////////////////////////////////////////////////////////////////////// + +bool tetgenio::load_pbc(const char* filename) +{ + FILE *infile; + char pbcfilename[FILENAMESIZE]; + char inputline[INPUTLINESIZE]; + char *stringptr; + pbcgroup *pg; + int index, p1, p2; + int i, j, k; + + // Pbc groups are saved in file "filename.pbc". + strcpy(pbcfilename, filename); + strcat(pbcfilename, ".pbc"); + infile = fopen(pbcfilename, "r"); + if (infile != (FILE *) NULL) { + printf("Opening %s.\n", pbcfilename); + } else { + // No such file. Return. + return false; + } + + // Read the number of pbc groups. + stringptr = readnumberline(inputline, infile, pbcfilename); + numberofpbcgroups = (int) strtol (stringptr, &stringptr, 0); + if (numberofpbcgroups == 0) { + // It looks this file contains no point. + fclose(infile); + return false; + } + // Initialize 'pbcgrouplist'; + pbcgrouplist = new pbcgroup[numberofpbcgroups]; + + // Read the list of pbc groups. + for (i = 0; i < numberofpbcgroups; i++) { + pg = &(pbcgrouplist[i]); + // Initialize pbcgroup i; + pg->numberofpointpairs = 0; + pg->pointpairlist = (int *) NULL; + // Read 'fmark1', 'fmark2'. + stringptr = readnumberline(inputline, infile, pbcfilename); + if (*stringptr == '\0') break; + pg->fmark1 = (int) strtol(stringptr, &stringptr, 0); + stringptr = findnextnumber(stringptr); + if (*stringptr == '\0') break; + pg->fmark2 = (int) strtol(stringptr, &stringptr, 0); + // Read 'transmat'. + do { + stringptr = readline(inputline, infile, NULL); + } while ((*stringptr != '[') && (*stringptr != '\0')); + if (*stringptr == '\0') break; + for (j = 0; j < 4; j++) { + for (k = 0; k < 4; k++) { + // Read the entry of [j, k]. + stringptr = findnextnumber(stringptr); + if (*stringptr == '\0') { + // Try to read another line. + stringptr = readnumberline(inputline, infile, pbcfilename); + if (*stringptr == '\0') break; + } + pg->transmat[j][k] = (REAL) strtod(stringptr, &stringptr); + } + if (k < 4) break; // Not complete! + } + if (j < 4) break; // Not complete! + // Read 'numberofpointpairs'. + stringptr = readnumberline(inputline, infile, pbcfilename); + if (*stringptr == '\0') break; + pg->numberofpointpairs = (int) strtol(stringptr, &stringptr, 0); + if (pg->numberofpointpairs > 0) { + pg->pointpairlist = new int[pg->numberofpointpairs * 2]; + // Read the point pairs. + index = 0; + for (j = 0; j < pg->numberofpointpairs; j++) { + stringptr = readnumberline(inputline, infile, pbcfilename); + p1 = (int) strtol(stringptr, &stringptr, 0); + stringptr = findnextnumber(stringptr); + p2 = (int) strtol(stringptr, &stringptr, 0); + pg->pointpairlist[index++] = p1; + pg->pointpairlist[index++] = p2; + } + } + } + fclose(infile); + + if (i < numberofpbcgroups) { + // Failed to read to additional points due to some error. + delete [] pbcgrouplist; + pbcgrouplist = (pbcgroup *) NULL; + numberofpbcgroups = 0; + return false; + } + return true; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// load_var() Load variant constraints applied on facets, segments, nodes.// +// // +// 'filename' is the filename of the original inputfile without suffix. The // +// constraints are found in file 'filename.var'. // +// // +/////////////////////////////////////////////////////////////////////////////// + +bool tetgenio::load_var(const char* filename) +{ + FILE *infile; + char varfilename[FILENAMESIZE]; + char inputline[INPUTLINESIZE]; + char *stringptr; + int index; + int i; + + // Variant constraints are saved in file "filename.var". + strcpy(varfilename, filename); + strcat(varfilename, ".var"); + infile = fopen(varfilename, "r"); + if (infile != (FILE *) NULL) { + printf("Opening %s.\n", varfilename); + } else { + // No such file. Return. + return false; + } + + // Read the facet constraint section. + stringptr = readnumberline(inputline, infile, varfilename); + if (*stringptr != '\0') { + numberoffacetconstraints = (int) strtol (stringptr, &stringptr, 0); + } else { + numberoffacetconstraints = 0; + } + if (numberoffacetconstraints > 0) { + // Initialize 'facetconstraintlist'. + facetconstraintlist = new REAL[numberoffacetconstraints * 2]; + index = 0; + for (i = 0; i < numberoffacetconstraints; i++) { + stringptr = readnumberline(inputline, infile, varfilename); + stringptr = findnextnumber(stringptr); + if (*stringptr == '\0') { + printf("Error: facet constraint %d has no facet marker.\n", + firstnumber + i); + break; + } else { + facetconstraintlist[index++] = (REAL) strtod(stringptr, &stringptr); + } + stringptr = findnextnumber(stringptr); + if (*stringptr == '\0') { + printf("Error: facet constraint %d has no maximum area bound.\n", + firstnumber + i); + break; + } else { + facetconstraintlist[index++] = (REAL) strtod(stringptr, &stringptr); + } + } + if (i < numberoffacetconstraints) { + // This must be caused by an error. + fclose(infile); + return false; + } + } + + // Read the segment constraint section. + stringptr = readnumberline(inputline, infile, varfilename); + if (*stringptr != '\0') { + numberofsegmentconstraints = (int) strtol (stringptr, &stringptr, 0); + } else { + numberofsegmentconstraints = 0; + } + if (numberofsegmentconstraints > 0) { + // Initialize 'segmentconstraintlist'. + segmentconstraintlist = new REAL[numberofsegmentconstraints * 3]; + index = 0; + for (i = 0; i < numberofsegmentconstraints; i++) { + stringptr = readnumberline(inputline, infile, varfilename); + stringptr = findnextnumber(stringptr); + if (*stringptr == '\0') { + printf("Error: segment constraint %d has no frist endpoint.\n", + firstnumber + i); + break; + } else { + segmentconstraintlist[index++] = (REAL) strtod(stringptr, &stringptr); + } + stringptr = findnextnumber(stringptr); + if (*stringptr == '\0') { + printf("Error: segment constraint %d has no second endpoint.\n", + firstnumber + i); + break; + } else { + segmentconstraintlist[index++] = (REAL) strtod(stringptr, &stringptr); + } + stringptr = findnextnumber(stringptr); + if (*stringptr == '\0') { + printf("Error: segment constraint %d has no maximum length bound.\n", + firstnumber + i); + break; + } else { + segmentconstraintlist[index++] = (REAL) strtod(stringptr, &stringptr); + } + } + if (i < numberofsegmentconstraints) { + // This must be caused by an error. + fclose(infile); + return false; + } + } + + fclose(infile); + return true; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// load_mtr() Load a size specification map from file. // +// // +// 'filename' is the filename of the original inputfile without suffix. The // +// size map is found in file 'filename.mtr'. // +// // +/////////////////////////////////////////////////////////////////////////////// + +bool tetgenio::load_mtr(const char* filename) +{ + FILE *infile; + char mtrfilename[FILENAMESIZE]; + char inputline[INPUTLINESIZE]; + char *stringptr; + REAL mtr; + int mtrindex; + int i, j; + + strcpy(mtrfilename, filename); + strcat(mtrfilename, ".mtr"); + infile = fopen(mtrfilename, "r"); + if (infile != (FILE *) NULL) { + printf("Opening %s.\n", mtrfilename); + } else { + // No such file. Return. + return false; + } + + // Read number of points, number of columns (1, 3, or 6). + stringptr = readnumberline(inputline, infile, mtrfilename); + stringptr = findnextnumber(stringptr); // Skip number of points. + if (*stringptr != '\0') { + numberofpointmtrs = (int) strtol (stringptr, &stringptr, 0); + } + if (numberofpointmtrs == 0) { + // Column number doesn't match. Set a default number (1). + numberofpointmtrs = 1; + } + + // Allocate space for pointmtrlist. + pointmtrlist = new REAL[numberofpoints * numberofpointmtrs]; + if (pointmtrlist == (REAL *) NULL) { + printf("Error: Out of memory.\n"); + terminatetetgen(1); + } + mtrindex = 0; + for (i = 0; i < numberofpoints; i++) { + // Read metrics. + stringptr = readnumberline(inputline, infile, mtrfilename); + for (j = 0; j < numberofpointmtrs; j++) { + if (*stringptr == '\0') { + printf("Error: Metric %d is missing value #%d in %s.\n", + i + firstnumber, j + 1, mtrfilename); + terminatetetgen(1); + } + mtr = (REAL) strtod(stringptr, &stringptr); + pointmtrlist[mtrindex++] = mtr; + stringptr = findnextnumber(stringptr); + } + } + + fclose(infile); + return true; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// load_poly() Load a piecewise linear complex from a .poly or .smesh. // +// // +// 'filename' is the inputfile without suffix. The PLC is in 'filename.poly' // +// or 'filename.smesh', and possibly plus 'filename.node' (when the first // +// line of the file starts with a zero). // +// // +/////////////////////////////////////////////////////////////////////////////// + +bool tetgenio::load_poly(const char* filename) +{ + FILE *infile, *polyfile; + char innodefilename[FILENAMESIZE]; + char inpolyfilename[FILENAMESIZE]; + char insmeshfilename[FILENAMESIZE]; + char inputline[INPUTLINESIZE]; + char *stringptr, *infilename; + int smesh, markers, currentmarker; + int readnodefile, index; + int i, j, k; + + // Assembling the actual file names we want to open. + strcpy(innodefilename, filename); + strcpy(inpolyfilename, filename); + strcpy(insmeshfilename, filename); + strcat(innodefilename, ".node"); + strcat(inpolyfilename, ".poly"); + strcat(insmeshfilename, ".smesh"); + + // First assume it is a .poly file. + smesh = 0; + // Try to open a .poly file. + polyfile = fopen(inpolyfilename, "r"); + if (polyfile == (FILE *) NULL) { + // .poly doesn't exist! Try to open a .smesh file. + polyfile = fopen(insmeshfilename, "r"); + if (polyfile == (FILE *) NULL) { + printf("File I/O Error: Cannot access file %s and %s.\n", + inpolyfilename, insmeshfilename); + return false; + } else { + printf("Opening %s.\n", insmeshfilename); + } + smesh = 1; + } else { + printf("Opening %s.\n", inpolyfilename); + } + // Initialize the default values. + mesh_dim = 3; // Three-dimemsional accoordinates. + numberofpointattributes = 0; // no point attribute. + markers = 0; // no boundary marker. + // Read number of points, number of dimensions, number of point + // attributes, and number of boundary markers. + stringptr = readnumberline(inputline, polyfile, inpolyfilename); + numberofpoints = (int) strtol (stringptr, &stringptr, 0); + stringptr = findnextnumber(stringptr); + if (*stringptr != '\0') { + mesh_dim = (int) strtol (stringptr, &stringptr, 0); + } + stringptr = findnextnumber(stringptr); + if (*stringptr != '\0') { + numberofpointattributes = (int) strtol (stringptr, &stringptr, 0); + } + stringptr = findnextnumber(stringptr); + if (*stringptr != '\0') { + markers = (int) strtol (stringptr, &stringptr, 0); + } + if (numberofpoints > 0) { + readnodefile = 0; + if (smesh) { + infilename = insmeshfilename; + } else { + infilename = inpolyfilename; + } + infile = polyfile; + } else { + // If the .poly or .smesh file claims there are zero points, that + // means the points should be read from a separate .node file. + readnodefile = 1; + infilename = innodefilename; + } + + if (readnodefile) { + // Read the points from the .node file. + printf("Opening %s.\n", innodefilename); + infile = fopen(innodefilename, "r"); + if (infile == (FILE *) NULL) { + printf("File I/O Error: Cannot access file %s.\n", innodefilename); + return false; + } + // Initialize the default values. + mesh_dim = 3; // Three-dimemsional accoordinates. + numberofpointattributes = 0; // no point attribute. + markers = 0; // no boundary marker. + // Read number of points, number of dimensions, number of point + // attributes, and number of boundary markers. + stringptr = readnumberline(inputline, infile, innodefilename); + numberofpoints = (int) strtol (stringptr, &stringptr, 0); + stringptr = findnextnumber(stringptr); + if (*stringptr != '\0') { + mesh_dim = (int) strtol (stringptr, &stringptr, 0); + } + stringptr = findnextnumber(stringptr); + if (*stringptr != '\0') { + numberofpointattributes = (int) strtol (stringptr, &stringptr, 0); + } + stringptr = findnextnumber(stringptr); + if (*stringptr != '\0') { + markers = (int) strtol (stringptr, &stringptr, 0); + } + } + + if ((mesh_dim != 3) && (mesh_dim != 2)) { + printf("Input error: TetGen only works for 2D & 3D point sets.\n"); + fclose(infile); + return false; + } + if (numberofpoints < (mesh_dim + 1)) { + printf("Input error: TetGen needs at least %d points.\n", mesh_dim + 1); + fclose(infile); + return false; + } + + // Load the list of nodes. + if (!load_node_call(infile, markers, infilename)) { + fclose(infile); + return false; + } + + if (readnodefile) { + fclose(infile); + } + + facet *f; + polygon *p; + + if (mesh_dim == 3) { + + // Read number of facets and number of boundary markers. + stringptr = readnumberline(inputline, polyfile, inpolyfilename); + numberoffacets = (int) strtol (stringptr, &stringptr, 0); + if (numberoffacets <= 0) { + // No facet list, return. + fclose(polyfile); + return true; + } + stringptr = findnextnumber(stringptr); + if (*stringptr == '\0') { + markers = 0; // no boundary marker. + } else { + markers = (int) strtol (stringptr, &stringptr, 0); + } + + // Initialize the 'facetlist', 'facetmarkerlist'. + facetlist = new facet[numberoffacets]; + if (markers == 1) { + facetmarkerlist = new int[numberoffacets]; + } + + // Read data into 'facetlist', 'facetmarkerlist'. + if (smesh == 0) { + // Facets are in .poly file format. + for (i = 1; i <= numberoffacets; i++) { + f = &(facetlist[i - 1]); + init(f); + f->numberofholes = 0; + currentmarker = 0; + // Read number of polygons, number of holes, and a boundary marker. + stringptr = readnumberline(inputline, polyfile, inpolyfilename); + f->numberofpolygons = (int) strtol (stringptr, &stringptr, 0); + stringptr = findnextnumber(stringptr); + if (*stringptr != '\0') { + f->numberofholes = (int) strtol (stringptr, &stringptr, 0); + if (markers == 1) { + stringptr = findnextnumber(stringptr); + if (*stringptr != '\0') { + currentmarker = (int) strtol(stringptr, &stringptr, 0); + } + } + } + // Initialize facetmarker if it needs. + if (markers == 1) { + facetmarkerlist[i - 1] = currentmarker; + } + // Each facet should has at least one polygon. + if (f->numberofpolygons <= 0) { + printf("Error: Wrong number of polygon in %d facet.\n", i); + break; + } + // Initialize the 'f->polygonlist'. + f->polygonlist = new polygon[f->numberofpolygons]; + // Go through all polygons, read in their vertices. + for (j = 1; j <= f->numberofpolygons; j++) { + p = &(f->polygonlist[j - 1]); + init(p); + // Read number of vertices of this polygon. + stringptr = readnumberline(inputline, polyfile, inpolyfilename); + p->numberofvertices = (int) strtol(stringptr, &stringptr, 0); + if (p->numberofvertices < 1) { + printf("Error: Wrong polygon %d in facet %d\n", j, i); + break; + } + // Initialize 'p->vertexlist'. + p->vertexlist = new int[p->numberofvertices]; + // Read all vertices of this polygon. + for (k = 1; k <= p->numberofvertices; k++) { + stringptr = findnextnumber(stringptr); + if (*stringptr == '\0') { + // Try to load another non-empty line and continue to read the + // rest of vertices. + stringptr = readnumberline(inputline, polyfile, inpolyfilename); + if (*stringptr == '\0') { + printf("Error: Missing %d endpoints of polygon %d in facet %d", + p->numberofvertices - k, j, i); + break; + } + } + p->vertexlist[k - 1] = (int) strtol (stringptr, &stringptr, 0); + } + } + if (j <= f->numberofpolygons) { + // This must be caused by an error. However, there're j - 1 + // polygons have been read. Reset the 'f->numberofpolygon'. + if (j == 1) { + // This is the first polygon. + delete [] f->polygonlist; + } + f->numberofpolygons = j - 1; + // No hole will be read even it exists. + f->numberofholes = 0; + break; + } + // If this facet has hole pints defined, read them. + if (f->numberofholes > 0) { + // Initialize 'f->holelist'. + f->holelist = new REAL[f->numberofholes * 3]; + // Read the holes' coordinates. + index = 0; + for (j = 1; j <= f->numberofholes; j++) { + stringptr = readnumberline(inputline, polyfile, inpolyfilename); + for (k = 1; k <= 3; k++) { + stringptr = findnextnumber(stringptr); + if (*stringptr == '\0') { + printf("Error: Hole %d in facet %d has no coordinates", j, i); + break; + } + f->holelist[index++] = (REAL) strtod (stringptr, &stringptr); + } + if (k <= 3) { + // This must be caused by an error. + break; + } + } + if (j <= f->numberofholes) { + // This must be caused by an error. + break; + } + } + } + if (i <= numberoffacets) { + // This must be caused by an error. + numberoffacets = i - 1; + fclose(polyfile); + return false; + } + } else { // poly == 0 + // Read the facets from a .smesh file. + for (i = 1; i <= numberoffacets; i++) { + f = &(facetlist[i - 1]); + init(f); + // Initialize 'f->facetlist'. In a .smesh file, each facetlist only + // contains exactly one polygon, no hole. + f->numberofpolygons = 1; + f->polygonlist = new polygon[f->numberofpolygons]; + p = &(f->polygonlist[0]); + init(p); + // Read number of vertices of this polygon. + stringptr = readnumberline(inputline, polyfile, insmeshfilename); + p->numberofvertices = (int) strtol (stringptr, &stringptr, 0); + if (p->numberofvertices < 1) { + printf("Error: Wrong number of vertex in facet %d\n", i); + break; + } + // Initialize 'p->vertexlist'. + p->vertexlist = new int[p->numberofvertices]; + for (k = 1; k <= p->numberofvertices; k++) { + stringptr = findnextnumber(stringptr); + if (*stringptr == '\0') { + // Try to load another non-empty line and continue to read the + // rest of vertices. + stringptr = readnumberline(inputline, polyfile, inpolyfilename); + if (*stringptr == '\0') { + printf("Error: Missing %d endpoints in facet %d", + p->numberofvertices - k, i); + break; + } + } + p->vertexlist[k - 1] = (int) strtol (stringptr, &stringptr, 0); + } + if (k <= p->numberofvertices) { + // This must be caused by an error. + break; + } + // Read facet's boundary marker at last. + if (markers == 1) { + stringptr = findnextnumber(stringptr); + if (*stringptr == '\0') { + currentmarker = 0; + } else { + currentmarker = (int) strtol(stringptr, &stringptr, 0); + } + facetmarkerlist[i - 1] = currentmarker; + } + } + if (i <= numberoffacets) { + // This must be caused by an error. + numberoffacets = i - 1; + fclose(polyfile); + return false; + } + } + + // Read the hole section. + stringptr = readnumberline(inputline, polyfile, inpolyfilename); + if (*stringptr != '\0') { + numberofholes = (int) strtol (stringptr, &stringptr, 0); + } else { + numberofholes = 0; + } + if (numberofholes > 0) { + // Initialize 'holelist'. + holelist = new REAL[numberofholes * 3]; + for (i = 0; i < 3 * numberofholes; i += 3) { + stringptr = readnumberline(inputline, polyfile, inpolyfilename); + stringptr = findnextnumber(stringptr); + if (*stringptr == '\0') { + printf("Error: Hole %d has no x coord.\n", firstnumber + (i / 3)); + break; + } else { + holelist[i] = (REAL) strtod(stringptr, &stringptr); + } + stringptr = findnextnumber(stringptr); + if (*stringptr == '\0') { + printf("Error: Hole %d has no y coord.\n", firstnumber + (i / 3)); + break; + } else { + holelist[i + 1] = (REAL) strtod(stringptr, &stringptr); + } + stringptr = findnextnumber(stringptr); + if (*stringptr == '\0') { + printf("Error: Hole %d has no z coord.\n", firstnumber + (i / 3)); + break; + } else { + holelist[i + 2] = (REAL) strtod(stringptr, &stringptr); + } + } + if (i < 3 * numberofholes) { + // This must be caused by an error. + fclose(polyfile); + return false; + } + } + + // Read the region section. The 'region' section is optional, if we + // don't reach the end-of-file, try read it in. + stringptr = readnumberline(inputline, polyfile, NULL); + if (stringptr != (char *) NULL && *stringptr != '\0') { + numberofregions = (int) strtol (stringptr, &stringptr, 0); + } else { + numberofregions = 0; + } + if (numberofregions > 0) { + // Initialize 'regionlist'. + regionlist = new REAL[numberofregions * 5]; + index = 0; + for (i = 0; i < numberofregions; i++) { + stringptr = readnumberline(inputline, polyfile, inpolyfilename); + stringptr = findnextnumber(stringptr); + if (*stringptr == '\0') { + printf("Error: Region %d has no x coordinate.\n", firstnumber + i); + break; + } else { + regionlist[index++] = (REAL) strtod(stringptr, &stringptr); + } + stringptr = findnextnumber(stringptr); + if (*stringptr == '\0') { + printf("Error: Region %d has no y coordinate.\n", firstnumber + i); + break; + } else { + regionlist[index++] = (REAL) strtod(stringptr, &stringptr); + } + stringptr = findnextnumber(stringptr); + if (*stringptr == '\0') { + printf("Error: Region %d has no z coordinate.\n", firstnumber + i); + break; + } else { + regionlist[index++] = (REAL) strtod(stringptr, &stringptr); + } + stringptr = findnextnumber(stringptr); + if (*stringptr == '\0') { + printf("Error: Region %d has no region attrib.\n", firstnumber + i); + break; + } else { + regionlist[index++] = (REAL) strtod(stringptr, &stringptr); + } + stringptr = findnextnumber(stringptr); + if (*stringptr == '\0') { + regionlist[index] = regionlist[index - 1]; + } else { + regionlist[index] = (REAL) strtod(stringptr, &stringptr); + } + index++; + } + if (i < numberofregions) { + // This must be caused by an error. + fclose(polyfile); + return false; + } + } + + /*// Read the edge (segment) section. This section is optional, if we + // don't reach the end-of-file, try read it in. + stringptr = readnumberline(inputline, polyfile, NULL); + if (stringptr != (char *) NULL && *stringptr != '\0') { + numberofedges = (int) strtol (stringptr, &stringptr, 0); + } else { + numberofedges = 0; + } + if (numberofedges > 0) { + // Initialize 'edgelist'. + edgelist = new int[numberofedges * 2]; + edgemarkerlist = new int[numberofedges]; + index = 0; + for (i = 0; i < numberofedges; i++) { + stringptr = readnumberline(inputline, polyfile, inpolyfilename); + for (j = 0; j < 2; j++) { + stringptr = findnextnumber(stringptr); + if (*stringptr == '\0') { + printf("Error: Segment %d has no endpoint.\n", firstnumber + i); + break; + } else { + edgelist[index++] = (int) strtol(stringptr, &stringptr, 0); + } + } + if (j < 2) break; + stringptr = findnextnumber(stringptr); + if (*stringptr == '\0') { + edgemarkerlist[i] = 0; // Set a default segment marker. + } else { + edgemarkerlist[i] = (int) strtol(stringptr, &stringptr, 0); + } + } + if (i < numberofedges) { + // This must be caused by an error. + fclose(polyfile); + return false; + } + } + */ + + } else { + + // Read a PSLG from Triangle's poly file. + assert(mesh_dim == 2); + // A PSLG is a facet of a PLC. + numberoffacets = 1; + // Initialize the 'facetlist'. + facetlist = new facet[numberoffacets]; + facetmarkerlist = (int *) NULL; // No facet markers. + f = &(facetlist[0]); + init(f); + // Read number of segments. + stringptr = readnumberline(inputline, polyfile, inpolyfilename); + // Segments are degenerate polygons. + f->numberofpolygons = (int) strtol (stringptr, &stringptr, 0); + if (f->numberofpolygons > 0) { + f->polygonlist = new polygon[f->numberofpolygons]; + } + // Go through all segments, read in their vertices. + for (j = 0; j < f->numberofpolygons; j++) { + p = &(f->polygonlist[j]); + init(p); + // Read in a segment. + stringptr = readnumberline(inputline, polyfile, inpolyfilename); + stringptr = findnextnumber(stringptr); // Skip its index. + p->numberofvertices = 2; // A segment always has two vertices. + p->vertexlist = new int[p->numberofvertices]; + p->vertexlist[0] = (int) strtol (stringptr, &stringptr, 0); + stringptr = findnextnumber(stringptr); + p->vertexlist[1] = (int) strtol (stringptr, &stringptr, 0); + } + // Read number of holes. + stringptr = readnumberline(inputline, polyfile, inpolyfilename); + f->numberofholes = (int) strtol (stringptr, &stringptr, 0); + if (f->numberofholes > 0) { + // Initialize 'f->holelist'. + f->holelist = new REAL[f->numberofholes * 3]; + // Read the holes' coordinates. + for (j = 0; j < f->numberofholes; j++) { + // Read a 2D hole point. + stringptr = readnumberline(inputline, polyfile, inpolyfilename); + stringptr = findnextnumber(stringptr); // Skip its index. + f->holelist[j * 3] = (REAL) strtod (stringptr, &stringptr); + stringptr = findnextnumber(stringptr); + f->holelist[j * 3 + 1] = (REAL) strtod (stringptr, &stringptr); + f->holelist[j * 3 + 2] = 0.0; // The z-coord. + } + } + // The regions are skipped. + + } + + // End of reading poly/smesh file. + fclose(polyfile); + + // Try to load a .var file if it exists. + load_var(filename); + // Try to load a .mtr file if it exists. + load_mtr(filename); + // Try to read a .pbc file if it exists. + load_pbc(filename); + + return true; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// load_off() Load a polyhedron described in a .off file. // +// // +// The .off format is one of file formats of the Geomview, an interactive // +// program for viewing and manipulating geometric objects. More information // +// is available form: http://www.geomview.org. // +// // +// 'filename' is a input filename with extension .off or without extension ( // +// the .off will be added in this case). On completion, the polyhedron is // +// returned in 'pointlist' and 'facetlist'. // +// // +/////////////////////////////////////////////////////////////////////////////// + +bool tetgenio::load_off(const char* filename) +{ + FILE *fp; + tetgenio::facet *f; + tetgenio::polygon *p; + char infilename[FILENAMESIZE]; + char buffer[INPUTLINESIZE]; + char *bufferp; + double *coord; + int nverts = 0, iverts = 0; + int nfaces = 0, ifaces = 0; + int nedges = 0; + int line_count = 0, i; + + strncpy(infilename, filename, 1024 - 1); + infilename[FILENAMESIZE - 1] = '\0'; + if (infilename[0] == '\0') { + printf("Error: No filename.\n"); + return false; + } + if (strcmp(&infilename[strlen(infilename) - 4], ".off") != 0) { + strcat(infilename, ".off"); + } + + if (!(fp = fopen(infilename, "r"))) { + printf("File I/O Error: Unable to open file %s\n", infilename); + return false; + } + printf("Opening %s.\n", infilename); + + // OFF requires the index starts from '0'. + firstnumber = 0; + + while ((bufferp = readline(buffer, fp, &line_count)) != NULL) { + // Check section + if (nverts == 0) { + // Read header + bufferp = strstr(bufferp, "OFF"); + if (bufferp != NULL) { + // Read mesh counts + bufferp = findnextnumber(bufferp); // Skip field "OFF". + if (*bufferp == '\0') { + // Read a non-empty line. + bufferp = readline(buffer, fp, &line_count); + } + if ((sscanf(bufferp, "%d%d%d", &nverts, &nfaces, &nedges) != 3) + || (nverts == 0)) { + printf("Syntax error reading header on line %d in file %s\n", + line_count, infilename); + fclose(fp); + return false; + } + // Allocate memory for 'tetgenio' + if (nverts > 0) { + numberofpoints = nverts; + pointlist = new REAL[nverts * 3]; + } + if (nfaces > 0) { + numberoffacets = nfaces; + facetlist = new tetgenio::facet[nfaces]; + } + } + } else if (iverts < nverts) { + // Read vertex coordinates + coord = &pointlist[iverts * 3]; + for (i = 0; i < 3; i++) { + if (*bufferp == '\0') { + printf("Syntax error reading vertex coords on line %d in file %s\n", + line_count, infilename); + fclose(fp); + return false; + } + coord[i] = (REAL) strtod(bufferp, &bufferp); + bufferp = findnextnumber(bufferp); + } + iverts++; + } else if (ifaces < nfaces) { + // Get next face + f = &facetlist[ifaces]; + init(f); + // In .off format, each facet has one polygon, no hole. + f->numberofpolygons = 1; + f->polygonlist = new tetgenio::polygon[1]; + p = &f->polygonlist[0]; + init(p); + // Read the number of vertices, it should be greater than 0. + p->numberofvertices = (int) strtol(bufferp, &bufferp, 0); + if (p->numberofvertices == 0) { + printf("Syntax error reading polygon on line %d in file %s\n", + line_count, infilename); + fclose(fp); + return false; + } + // Allocate memory for face vertices + p->vertexlist = new int[p->numberofvertices]; + for (i = 0; i < p->numberofvertices; i++) { + bufferp = findnextnumber(bufferp); + if (*bufferp == '\0') { + printf("Syntax error reading polygon on line %d in file %s\n", + line_count, infilename); + fclose(fp); + return false; + } + p->vertexlist[i] = (int) strtol(bufferp, &bufferp, 0); + } + ifaces++; + } else { + // Should never get here + printf("Found extra text starting at line %d in file %s\n", line_count, + infilename); + break; + } + } + + // Close file + fclose(fp); + + // Check whether read all points + if (iverts != nverts) { + printf("Expected %d vertices, but read only %d vertices in file %s\n", + nverts, iverts, infilename); + return false; + } + + // Check whether read all faces + if (ifaces != nfaces) { + printf("Expected %d faces, but read only %d faces in file %s\n", + nfaces, ifaces, infilename); + return false; + } + + return true; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// load_ply() Load a polyhedron described in a .ply file. // +// // +// 'filename' is the file name with extension .ply or without extension (the // +// .ply will be added in this case). // +// // +// This is a simplified version of reading .ply files, which only reads the // +// set of vertices and the set of faces. Other informations (such as color, // +// material, texture, etc) in .ply file are ignored. Complete routines for // +// reading and writing ,ply files are available from: // +// http://www.cc.gatech.edu/projects/large_models/ply.html // +// Except the header section,ply file format has exactly the same format for // +// listing vertices and polygons as off file format. // +// // +// On completion, 'pointlist' and 'facetlist' together return the polyhedron.// +// // +/////////////////////////////////////////////////////////////////////////////// + +bool tetgenio::load_ply(const char* filename) +{ + FILE *fp; + tetgenio::facet *f; + tetgenio::polygon *p; + char infilename[FILENAMESIZE]; + char buffer[INPUTLINESIZE]; + char *bufferp, *str; + double *coord; + int endheader = 0, format = 0; + int nverts = 0, iverts = 0; + int nfaces = 0, ifaces = 0; + int line_count = 0, i; + + strncpy(infilename, filename, FILENAMESIZE - 1); + infilename[FILENAMESIZE - 1] = '\0'; + if (infilename[0] == '\0') { + printf("Error: No filename.\n"); + return false; + } + if (strcmp(&infilename[strlen(infilename) - 4], ".ply") != 0) { + strcat(infilename, ".ply"); + } + + if (!(fp = fopen(infilename, "r"))) { + printf("Error: Unable to open file %s\n", infilename); + return false; + } + printf("Opening %s.\n", infilename); + + // PLY requires the index starts from '0'. + firstnumber = 0; + + while ((bufferp = readline(buffer, fp, &line_count)) != NULL) { + if (!endheader) { + // Find if it is the keyword "end_header". + str = strstr(bufferp, "end_header"); + // strstr() is case sensitive. + if (!str) str = strstr(bufferp, "End_header"); + if (!str) str = strstr(bufferp, "End_Header"); + if (str) { + // This is the end of the header section. + endheader = 1; + continue; + } + // Parse the number of vertices and the number of faces. + if (nverts == 0 || nfaces == 0) { + // Find if it si the keyword "element". + str = strstr(bufferp, "element"); + if (!str) str = strstr(bufferp, "Element"); + if (str) { + bufferp = findnextfield(str); + if (*bufferp == '\0') { + printf("Syntax error reading element type on line%d in file %s\n", + line_count, infilename); + fclose(fp); + return false; + } + if (nverts == 0) { + // Find if it is the keyword "vertex". + str = strstr(bufferp, "vertex"); + if (!str) str = strstr(bufferp, "Vertex"); + if (str) { + bufferp = findnextnumber(str); + if (*bufferp == '\0') { + printf("Syntax error reading vertex number on line"); + printf(" %d in file %s\n", line_count, infilename); + fclose(fp); + return false; + } + nverts = (int) strtol(bufferp, &bufferp, 0); + // Allocate memory for 'tetgenio' + if (nverts > 0) { + numberofpoints = nverts; + pointlist = new REAL[nverts * 3]; + } + } + } + if (nfaces == 0) { + // Find if it is the keyword "face". + str = strstr(bufferp, "face"); + if (!str) str = strstr(bufferp, "Face"); + if (str) { + bufferp = findnextnumber(str); + if (*bufferp == '\0') { + printf("Syntax error reading face number on line"); + printf(" %d in file %s\n", line_count, infilename); + fclose(fp); + return false; + } + nfaces = (int) strtol(bufferp, &bufferp, 0); + // Allocate memory for 'tetgenio' + if (nfaces > 0) { + numberoffacets = nfaces; + facetlist = new tetgenio::facet[nfaces]; + } + } + } + } // It is not the string "element". + } + if (format == 0) { + // Find the keyword "format". + str = strstr(bufferp, "format"); + if (!str) str = strstr(bufferp, "Format"); + if (str) { + format = 1; + bufferp = findnextfield(str); + // Find if it is the string "ascii". + str = strstr(bufferp, "ascii"); + if (!str) str = strstr(bufferp, "ASCII"); + if (!str) { + printf("This routine only reads ascii format of ply files.\n"); + printf("Hint: You can convert the binary to ascii format by\n"); + printf(" using the provided ply tools:\n"); + printf(" ply2ascii < %s > ascii_%s\n", infilename, infilename); + fclose(fp); + return false; + } + } + } + } else if (iverts < nverts) { + // Read vertex coordinates + coord = &pointlist[iverts * 3]; + for (i = 0; i < 3; i++) { + if (*bufferp == '\0') { + printf("Syntax error reading vertex coords on line %d in file %s\n", + line_count, infilename); + fclose(fp); + return false; + } + coord[i] = (REAL) strtod(bufferp, &bufferp); + bufferp = findnextnumber(bufferp); + } + iverts++; + } else if (ifaces < nfaces) { + // Get next face + f = &facetlist[ifaces]; + init(f); + // In .off format, each facet has one polygon, no hole. + f->numberofpolygons = 1; + f->polygonlist = new tetgenio::polygon[1]; + p = &f->polygonlist[0]; + init(p); + // Read the number of vertices, it should be greater than 0. + p->numberofvertices = (int) strtol(bufferp, &bufferp, 0); + if (p->numberofvertices == 0) { + printf("Syntax error reading polygon on line %d in file %s\n", + line_count, infilename); + fclose(fp); + return false; + } + // Allocate memory for face vertices + p->vertexlist = new int[p->numberofvertices]; + for (i = 0; i < p->numberofvertices; i++) { + bufferp = findnextnumber(bufferp); + if (*bufferp == '\0') { + printf("Syntax error reading polygon on line %d in file %s\n", + line_count, infilename); + fclose(fp); + return false; + } + p->vertexlist[i] = (int) strtol(bufferp, &bufferp, 0); + } + ifaces++; + } else { + // Should never get here + printf("Found extra text starting at line %d in file %s\n", line_count, + infilename); + break; + } + } + + // Close file + fclose(fp); + + // Check whether read all points + if (iverts != nverts) { + printf("Expected %d vertices, but read only %d vertices in file %s\n", + nverts, iverts, infilename); + return false; + } + + // Check whether read all faces + if (ifaces != nfaces) { + printf("Expected %d faces, but read only %d faces in file %s\n", + nfaces, ifaces, infilename); + return false; + } + + return true; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// load_stl() Load a surface mesh described in a .stl file. // +// // +// 'filename' is the file name with extension .stl or without extension (the // +// .stl will be added in this case). // +// // +// The .stl or stereolithography format is an ASCII or binary file used in // +// manufacturing. It is a list of the triangular surfaces that describe a // +// computer generated solid model. This is the standard input for most rapid // +// prototyping machines. // +// // +// On completion, 'pointlist' and 'facetlist' together return the polyhedron.// +// Note: After load_stl(), there exist many duplicated points in 'pointlist'.// +// They will be unified during the Delaunay tetrahedralization process. // +// // +/////////////////////////////////////////////////////////////////////////////// + +bool tetgenio::load_stl(const char* filename) +{ + FILE *fp; + facet *f; + polygon *p; + char infilename[FILENAMESIZE]; + char buffer[INPUTLINESIZE]; + char *bufferp, *str; + double *tmplist, *coord; + int solid = 0; + int maxverts = 1024, nverts = 0, iverts = 0; + int nfaces = 0; + int line_count = 0, i; + + strncpy(infilename, filename, FILENAMESIZE - 1); + infilename[FILENAMESIZE - 1] = '\0'; + if (infilename[0] == '\0') { + printf("Error: No filename.\n"); + return false; + } + if (strcmp(&infilename[strlen(infilename) - 4], ".stl") != 0) { + strcat(infilename, ".stl"); + } + + if (!(fp = fopen(infilename, "r"))) { + printf("Error: Unable to open file %s\n", infilename); + return false; + } + printf("Opening %s.\n", infilename); + + // STL file has no number of points available. Use a list to read points. + tmplist = (double *) malloc(sizeof(double) * 3 * maxverts); + + while ((bufferp = readline(buffer, fp, &line_count)) != NULL) { + // The ASCII .stl file must start with the lower case keyword solid and + // end with endsolid. + if (solid == 0) { + // Read header + bufferp = strstr(bufferp, "solid"); + if (bufferp != NULL) { + solid = 1; + } + } else { + // We're inside the block of the solid. + str = bufferp; + // Is this the end of the solid. + bufferp = strstr(bufferp, "endsolid"); + if (bufferp != NULL) { + solid = 0; + } else { + // Read the XYZ coordinates if it is a vertex. + bufferp = str; + bufferp = strstr(bufferp, "vertex"); + if (bufferp != NULL) { + // Check if we have enough memory. + if (nverts == maxverts) { + maxverts += 1024; + tmplist = (double *) realloc(tmplist, sizeof(double)*3*maxverts); + } + coord = &(tmplist[nverts * 3]); + for (i = 0; i < 3; i++) { + bufferp = findnextnumber(bufferp); + if (*bufferp == '\0') { + printf("Syntax error reading vertex coords on line %d\n", + line_count); + free(tmplist); + fclose(fp); + return false; + } + coord[i] = (REAL) strtod(bufferp, &bufferp); + } + nverts++; + } + } + } + } + fclose(fp); + + // nverts should be an integer times 3 (every 3 vertices denote a face). + if (nverts == 0 || (nverts % 3 != 0)) { + printf("Error: Wrong number of vertices in file %s.\n", infilename); + free(tmplist); + return false; + } + numberofpoints = nverts; + pointlist = new REAL[nverts * 3]; + for (i = 0; i < nverts; i++) { + coord = &(tmplist[i * 3]); + iverts = i * 3; + pointlist[iverts] = (REAL) coord[0]; + pointlist[iverts + 1] = (REAL) coord[1]; + pointlist[iverts + 2] = (REAL) coord[2]; + } + + nfaces = (int) (nverts / 3); + numberoffacets = nfaces; + facetlist = new facet[nfaces]; + + // Default use '1' as the array starting index. + firstnumber = 1; + iverts = firstnumber; + for (i = 0; i < nfaces; i++) { + f = &facetlist[i]; + init(f); + // In .stl format, each facet has one polygon, no hole. + f->numberofpolygons = 1; + f->polygonlist = new polygon[1]; + p = &f->polygonlist[0]; + init(p); + // Each polygon has three vertices. + p->numberofvertices = 3; + p->vertexlist = new int[p->numberofvertices]; + p->vertexlist[0] = iverts; + p->vertexlist[1] = iverts + 1; + p->vertexlist[2] = iverts + 2; + iverts += 3; + } + + free(tmplist); + return true; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// load_medit() Load a surface mesh described in .mesh file. // +// // +// 'filename' is the file name with extension .mesh or without entension ( // +// the .mesh will be added in this case). .mesh is the file format of Medit, // +// a user-friendly interactive mesh viewing program. // +// // +// This routine ONLY reads the sections containing vertices, triangles, and // +// quadrilaters. Other sections (such as tetrahedra, edges, ...) are ignored.// +// // +/////////////////////////////////////////////////////////////////////////////// + +bool tetgenio::load_medit(const char* filename) +{ + FILE *fp; + tetgenio::facet *tmpflist, *f; + tetgenio::polygon *p; + char infilename[FILENAMESIZE]; + char buffer[INPUTLINESIZE]; + char *bufferp, *str; + double *coord; + int *tmpfmlist; + int dimension = 0; + int nverts = 0; + int nfaces = 0; + int line_count = 0; + int corners = 0; // 3 (triangle) or 4 (quad). + int i, j; + + strncpy(infilename, filename, FILENAMESIZE - 1); + infilename[FILENAMESIZE - 1] = '\0'; + if (infilename[0] == '\0') { + printf("Error: No filename.\n"); + return false; + } + if (strcmp(&infilename[strlen(infilename) - 5], ".mesh") != 0) { + strcat(infilename, ".mesh"); + } + + if (!(fp = fopen(infilename, "r"))) { + printf("Error: Unable to open file %s\n", infilename); + return false; + } + printf("Opening %s.\n", infilename); + + // Default uses the index starts from '1'. + firstnumber = 1; + + while ((bufferp = readline(buffer, fp, &line_count)) != NULL) { + if (*bufferp == '#') continue; // A comment line is skipped. + if (dimension == 0) { + // Find if it is the keyword "Dimension". + str = strstr(bufferp, "Dimension"); + if (!str) str = strstr(bufferp, "dimension"); + if (!str) str = strstr(bufferp, "DIMENSION"); + if (str) { + // Read the dimensions + bufferp = findnextnumber(str); // Skip field "Dimension". + if (*bufferp == '\0') { + // Read a non-empty line. + bufferp = readline(buffer, fp, &line_count); + } + dimension = (int) strtol(bufferp, &bufferp, 0); + if (dimension != 2 && dimension != 3) { + printf("Unknown dimension in file on line %d in file %s\n", + line_count, infilename); + fclose(fp); + return false; + } + mesh_dim = dimension; + } + } + if (nverts == 0) { + // Find if it is the keyword "Vertices". + str = strstr(bufferp, "Vertices"); + if (!str) str = strstr(bufferp, "vertices"); + if (!str) str = strstr(bufferp, "VERTICES"); + if (str) { + // Read the number of vertices. + bufferp = findnextnumber(str); // Skip field "Vertices". + if (*bufferp == '\0') { + // Read a non-empty line. + bufferp = readline(buffer, fp, &line_count); + } + nverts = (int) strtol(bufferp, &bufferp, 0); + // Allocate memory for 'tetgenio' + if (nverts > 0) { + numberofpoints = nverts; + pointlist = new REAL[nverts * 3]; + } + // Read the follwoing node list. + for (i = 0; i < nverts; i++) { + bufferp = readline(buffer, fp, &line_count); + if (bufferp == NULL) { + printf("Unexpected end of file on line %d in file %s\n", + line_count, infilename); + fclose(fp); + return false; + } + // Read vertex coordinates + coord = &pointlist[i * 3]; + for (j = 0; j < 3; j++) { + if (*bufferp == '\0') { + printf("Syntax error reading vertex coords on line"); + printf(" %d in file %s\n", line_count, infilename); + fclose(fp); + return false; + } + if ((j < 2) || (dimension == 3)) { + coord[j] = (REAL) strtod(bufferp, &bufferp); + } else { + assert((j == 2) && (dimension == 2)); + coord[j] = 0.0; + } + bufferp = findnextnumber(bufferp); + } + } + continue; + } + } + if (nfaces == 0) { + // Find if it is the keyword "Triangles" or "Quadrilaterals". + corners = 0; + str = strstr(bufferp, "Triangles"); + if (!str) str = strstr(bufferp, "triangles"); + if (!str) str = strstr(bufferp, "TRIANGLES"); + if (str) { + corners = 3; + } else { + str = strstr(bufferp, "Quadrilaterals"); + if (!str) str = strstr(bufferp, "quadrilaterals"); + if (!str) str = strstr(bufferp, "QUADRILATERALS"); + if (str) { + corners = 4; + } + } + if (corners == 3 || corners == 4) { + // Read the number of triangles (or quadrilaterals). + bufferp = findnextnumber(str); // Skip field "Triangles". + if (*bufferp == '\0') { + // Read a non-empty line. + bufferp = readline(buffer, fp, &line_count); + } + nfaces = strtol(bufferp, &bufferp, 0); + // Allocate memory for 'tetgenio' + if (nfaces > 0) { + if (numberoffacets > 0) { + // facetlist has already been allocated. Enlarge arrays. + tmpflist = new tetgenio::facet[numberoffacets + nfaces]; + tmpfmlist = new int[numberoffacets + nfaces]; + // Copy the data of old arrays into new arrays. + for (i = 0; i < numberoffacets; i++) { + f = &(tmpflist[i]); + tetgenio::init(f); + *f = facetlist[i]; + tmpfmlist[i] = facetmarkerlist[i]; + } + // Release old arrays. + delete [] facetlist; + delete [] facetmarkerlist; + // Remember the new arrays. + facetlist = tmpflist; + facetmarkerlist = tmpfmlist; + } else { + // This is the first time to allocate facetlist. + facetlist = new tetgenio::facet[nfaces]; + facetmarkerlist = new int[nfaces]; + } + } + // Read the following list of faces. + for (i = numberoffacets; i < numberoffacets + nfaces; i++) { + bufferp = readline(buffer, fp, &line_count); + if (bufferp == NULL) { + printf("Unexpected end of file on line %d in file %s\n", + line_count, infilename); + fclose(fp); + return false; + } + f = &facetlist[i]; + tetgenio::init(f); + // In .mesh format, each facet has one polygon, no hole. + f->numberofpolygons = 1; + f->polygonlist = new tetgenio::polygon[1]; + p = &f->polygonlist[0]; + tetgenio::init(p); + p->numberofvertices = corners; + // Allocate memory for face vertices + p->vertexlist = new int[p->numberofvertices]; + // Read the vertices of the face. + for (j = 0; j < corners; j++) { + if (*bufferp == '\0') { + printf("Syntax error reading face on line %d in file %s\n", + line_count, infilename); + fclose(fp); + return false; + } + p->vertexlist[j] = (int) strtol(bufferp, &bufferp, 0); + if (firstnumber == 1) { + // Check if a '0' index appears. + if (p->vertexlist[j] == 0) { + // The first index is set to be 0. + firstnumber = 0; + } + } + bufferp = findnextnumber(bufferp); + } + // Read the marker of the face if it exists. + facetmarkerlist[i] = 0; + if (*bufferp != '\0') { + facetmarkerlist[i] = (int) strtol(bufferp, &bufferp, 0); + } + } + // Have read in a list of triangles/quads. + numberoffacets += nfaces; + nfaces = 0; + } + } + // if (nverts > 0 && nfaces > 0) break; // Ignore other data. + } + + // Close file + fclose(fp); + + return true; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// load_vtk() Load VTK surface mesh from file (.vtk ascii or binary). // +// // +// 'filename' is a string containing the file name with or without suffix. // +// // +// This function is contributed by user: Bryn Lloyd (May 7, 2007). // +// // +/////////////////////////////////////////////////////////////////////////////// + +void swapBytes(unsigned char* var, int size) +{ + int i = 0; + int j = size - 1; + char c; + + while (i < j) { + c = var[i]; var[i] = var[j]; var[j] = c; + i++, j--; + } +} + +bool testIsBigEndian() +{ + short word = 0x4321; + if((*(char *)& word) != 0x21) + return true; + else + return false; +} + +bool tetgenio::load_vtk(const char* filename) +{ + FILE *fp; + tetgenio::facet *f; + tetgenio::polygon *p; + char infilename[FILENAMESIZE]; + char line[INPUTLINESIZE]; + char mode[128], id[256], fmt[64]; + char *bufferp; + double *coord; + float _x, _y, _z; + int nverts = 0; + int nfaces = 0; + int line_count = 0; + int dummy; + int id1, id2, id3; + int nn = -1; + int nn_old = -1; + int i, j; + bool ImALittleEndian = !testIsBigEndian(); + + strncpy(infilename, filename, FILENAMESIZE - 1); + infilename[FILENAMESIZE - 1] = '\0'; + if (infilename[0] == '\0') { + printf("Error: No filename.\n"); + return false; + } + if (strcmp(&infilename[strlen(infilename) - 4], ".vtk") != 0) { + strcat(infilename, ".vtk"); + } + if (!(fp = fopen(infilename, "r"))) { + printf("Error: Unable to open file %s\n", infilename); + return false; + } + printf("Opening %s.\n", infilename); + + // Default uses the index starts from '0'. + firstnumber = 0; + strcpy(mode, "BINARY"); + + while((bufferp = readline(line, fp, &line_count)) != NULL) { + if(strlen(line) == 0) continue; + //swallow lines beginning with a comment sign or white space + if(line[0] == '#' || line[0]=='\n' || line[0] == 10 || line[0] == 13 || + line[0] == 32) continue; + + sscanf(line, "%s", id); + if(!strcmp(id, "ASCII")) { + strcpy(mode, "ASCII"); + } + + if(!strcmp(id, "POINTS")) { + sscanf(line, "%s %d %s", id, &nverts, fmt); + if (nverts > 0) { + numberofpoints = nverts; + pointlist = new REAL[nverts * 3]; + } + + if(!strcmp(mode, "BINARY")) { + for(i = 0; i < nverts; i++) { + coord = &pointlist[i * 3]; + if(!strcmp(fmt, "double")) { + fread((char*)(&(coord[0])), sizeof(double), 1, fp); + fread((char*)(&(coord[1])), sizeof(double), 1, fp); + fread((char*)(&(coord[2])), sizeof(double), 1, fp); + if(ImALittleEndian){ + swapBytes((unsigned char *) &(coord[0]), sizeof(coord[0])); + swapBytes((unsigned char *) &(coord[1]), sizeof(coord[1])); + swapBytes((unsigned char *) &(coord[2]), sizeof(coord[2])); + } + } else if(!strcmp(fmt, "float")) { + fread((char*)(&_x), sizeof(float), 1, fp); + fread((char*)(&_y), sizeof(float), 1, fp); + fread((char*)(&_z), sizeof(float), 1, fp); + if(ImALittleEndian){ + swapBytes((unsigned char *) &_x, sizeof(_x)); + swapBytes((unsigned char *) &_y, sizeof(_y)); + swapBytes((unsigned char *) &_z, sizeof(_z)); + } + coord[0] = double(_x); + coord[1] = double(_y); + coord[2] = double(_z); + } else { + printf("Error: Only float or double formats are supported!\n"); + return false; + } + } + } else if(!strcmp(mode, "ASCII")) { + for(i = 0; i < nverts; i++){ + bufferp = readline(line, fp, &line_count); + if (bufferp == NULL) { + printf("Unexpected end of file on line %d in file %s\n", + line_count, infilename); + fclose(fp); + return false; + } + // Read vertex coordinates + coord = &pointlist[i * 3]; + for (j = 0; j < 3; j++) { + if (*bufferp == '\0') { + printf("Syntax error reading vertex coords on line"); + printf(" %d in file %s\n", line_count, infilename); + fclose(fp); + return false; + } + coord[j] = (REAL) strtod(bufferp, &bufferp); + bufferp = findnextnumber(bufferp); + } + } + } + continue; + } + + if(!strcmp(id, "POLYGONS")) { + sscanf(line, "%s %d %d", id, &nfaces, &dummy); + if (nfaces > 0) { + numberoffacets = nfaces; + facetlist = new tetgenio::facet[nfaces]; + } + + if(!strcmp(mode, "BINARY")) { + for(i = 0; i < nfaces; i++){ + fread((char*)(&nn), sizeof(int), 1, fp); + if(ImALittleEndian){ + swapBytes((unsigned char *) &nn, sizeof(nn)); + } + if (i == 0) + nn_old = nn; + if (nn != nn_old) { + printf("Error: No mixed cells are allowed.\n"); + return false; + } + + if(nn == 3){ + fread((char*)(&id1), sizeof(int), 1, fp); + fread((char*)(&id2), sizeof(int), 1, fp); + fread((char*)(&id3), sizeof(int), 1, fp); + if(ImALittleEndian){ + swapBytes((unsigned char *) &id1, sizeof(id1)); + swapBytes((unsigned char *) &id2, sizeof(id2)); + swapBytes((unsigned char *) &id3, sizeof(id3)); + } + f = &facetlist[i]; + init(f); + // In .off format, each facet has one polygon, no hole. + f->numberofpolygons = 1; + f->polygonlist = new tetgenio::polygon[1]; + p = &f->polygonlist[0]; + init(p); + // Set number of vertices + p->numberofvertices = 3; + // Allocate memory for face vertices + p->vertexlist = new int[p->numberofvertices]; + p->vertexlist[0] = id1; + p->vertexlist[1] = id2; + p->vertexlist[2] = id3; + } else { + printf("Error: Only triangles are supported\n"); + return false; + } + } + } else if(!strcmp(mode, "ASCII")) { + for(i = 0; i < nfaces; i++) { + bufferp = readline(line, fp, &line_count); + nn = (int) strtol(bufferp, &bufferp, 0); + if (i == 0) + nn_old = nn; + if (nn != nn_old) { + printf("Error: No mixed cells are allowed.\n"); + return false; + } + + if (nn == 3) { + bufferp = findnextnumber(bufferp); // Skip the first field. + id1 = (int) strtol(bufferp, &bufferp, 0); + bufferp = findnextnumber(bufferp); + id2 = (int) strtol(bufferp, &bufferp, 0); + bufferp = findnextnumber(bufferp); + id3 = (int) strtol(bufferp, &bufferp, 0); + f = &facetlist[i]; + init(f); + // In .off format, each facet has one polygon, no hole. + f->numberofpolygons = 1; + f->polygonlist = new tetgenio::polygon[1]; + p = &f->polygonlist[0]; + init(p); + // Set number of vertices + p->numberofvertices = 3; + // Allocate memory for face vertices + p->vertexlist = new int[p->numberofvertices]; + p->vertexlist[0] = id1; + p->vertexlist[1] = id2; + p->vertexlist[2] = id3; + } else { + printf("Error: Only triangles are supported.\n"); + return false; + } + } + } + + fclose(fp); + return true; + } + + if(!strcmp(id,"LINES") || !strcmp(id,"CELLS")){ + printf("Warning: load_vtk(): cannot read formats LINES, CELLS.\n"); + } + } // while () + + return true; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// load_plc() Load a piecewise linear complex from file. // +// // +// This is main entrance for loading plcs from different file formats into // +// tetgenio. 'filename' is the input file name without extention. 'object' // +// indicates which file format is used to describ the plc. // +// // +/////////////////////////////////////////////////////////////////////////////// + +bool tetgenio::load_plc(const char* filename, int object) +{ + enum tetgenbehavior::objecttype type; + + type = (enum tetgenbehavior::objecttype) object; + switch (type) { + case tetgenbehavior::NODES: + return load_node(filename); + case tetgenbehavior::POLY: + return load_poly(filename); + case tetgenbehavior::OFF: + return load_off(filename); + case tetgenbehavior::PLY: + return load_ply(filename); + case tetgenbehavior::STL: + return load_stl(filename); + case tetgenbehavior::MEDIT: + return load_medit(filename); + case tetgenbehavior::VTK: + return load_vtk(filename); + default: + return load_poly(filename); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// load_tetmesh() Load a tetrahedral mesh from files. // +// // +// 'filename' is the inputfile without suffix. The nodes of the tetrahedral // +// mesh is in "filename.node", the elements is in "filename.ele", if the // +// "filename.face" and "filename.vol" exists, they will also be read. // +// // +/////////////////////////////////////////////////////////////////////////////// + +bool tetgenio::load_tetmesh(const char* filename) +{ + FILE *infile; + char innodefilename[FILENAMESIZE]; + char inelefilename[FILENAMESIZE]; + char infacefilename[FILENAMESIZE]; + char inedgefilename[FILENAMESIZE]; + char involfilename[FILENAMESIZE]; + char inputline[INPUTLINESIZE]; + char *stringptr, *infilename; + REAL attrib, volume; + int volelements; + int markers, corner; + int index, attribindex; + int i, j; + + // Assembling the actual file names we want to open. + strcpy(innodefilename, filename); + strcpy(inelefilename, filename); + strcpy(infacefilename, filename); + strcpy(inedgefilename, filename); + strcpy(involfilename, filename); + strcat(innodefilename, ".node"); + strcat(inelefilename, ".ele"); + strcat(infacefilename, ".face"); + strcat(inedgefilename, ".edge"); + strcat(involfilename, ".vol"); + + // Read the points from a .node file. + infilename = innodefilename; + printf("Opening %s.\n", infilename); + infile = fopen(infilename, "r"); + if (infile == (FILE *) NULL) { + printf("File I/O Error: Cannot access file %s.\n", infilename); + return false; + } + // Read the first line of the file. + stringptr = readnumberline(inputline, infile, infilename); + // Is this list of points generated from rbox? + stringptr = strstr(inputline, "rbox"); + if (stringptr == NULL) { + // Read number of points, number of dimensions, number of point + // attributes, and number of boundary markers. + stringptr = inputline; + numberofpoints = (int) strtol (stringptr, &stringptr, 0); + stringptr = findnextnumber(stringptr); + if (*stringptr == '\0') { + mesh_dim = 3; + } else { + mesh_dim = (int) strtol (stringptr, &stringptr, 0); + } + stringptr = findnextnumber(stringptr); + if (*stringptr == '\0') { + numberofpointattributes = 0; + } else { + numberofpointattributes = (int) strtol (stringptr, &stringptr, 0); + } + stringptr = findnextnumber(stringptr); + if (*stringptr == '\0') { + markers = 0; // Default value. + } else { + markers = (int) strtol (stringptr, &stringptr, 0); + } + } else { + // It is a rbox (qhull) input file. + stringptr = inputline; + // Get the dimension. + mesh_dim = (int) strtol (stringptr, &stringptr, 0); + // Get the number of points. + stringptr = readnumberline(inputline, infile, infilename); + numberofpoints = (int) strtol (stringptr, &stringptr, 0); + // There is no index column. + useindex = 0; + } + + // Load the list of nodes. + if (!load_node_call(infile, markers, infilename)) { + fclose(infile); + return false; + } + fclose(infile); + + // Read the elements from an .ele file. + if (mesh_dim == 3) { + infilename = inelefilename; + infile = fopen(infilename, "r"); + if (infile != (FILE *) NULL) { + printf("Opening %s.\n", infilename); + // Read number of elements, number of corners (4 or 10), number of + // element attributes. + stringptr = readnumberline(inputline, infile, infilename); + numberoftetrahedra = (int) strtol (stringptr, &stringptr, 0); + stringptr = findnextnumber(stringptr); + if (*stringptr == '\0') { + numberofcorners = 4; // Default read 4 nodes per element. + } else { + numberofcorners = (int) strtol(stringptr, &stringptr, 0); + } + stringptr = findnextnumber(stringptr); + if (*stringptr == '\0') { + numberoftetrahedronattributes = 0; // Default no attribute. + } else { + numberoftetrahedronattributes = (int) strtol(stringptr, &stringptr, 0); + } + if (numberofcorners != 4 && numberofcorners != 10) { + printf("Error: Wrong number of corners %d (should be 4 or 10).\n", + numberofcorners); + fclose(infile); + return false; + } + // Allocate memory for tetrahedra. + if (numberoftetrahedra > 0) { + tetrahedronlist = new int[numberoftetrahedra * numberofcorners]; + if (tetrahedronlist == (int *) NULL) { + printf("Error: Out of memory.\n"); + terminatetetgen(1); + } + // Allocate memory for output tetrahedron attributes if necessary. + if (numberoftetrahedronattributes > 0) { + tetrahedronattributelist = new REAL[numberoftetrahedra * + numberoftetrahedronattributes]; + if (tetrahedronattributelist == (REAL *) NULL) { + printf("Error: Out of memory.\n"); + terminatetetgen(1); + } + } + } + // Read the list of tetrahedra. + index = 0; + attribindex = 0; + for (i = 0; i < numberoftetrahedra; i++) { + // Read tetrahedron index and the tetrahedron's corners. + stringptr = readnumberline(inputline, infile, infilename); + for (j = 0; j < numberofcorners; j++) { + stringptr = findnextnumber(stringptr); + if (*stringptr == '\0') { + printf("Error: Tetrahedron %d is missing vertex %d in %s.\n", + i + firstnumber, j + 1, infilename); + terminatetetgen(1); + } + corner = (int) strtol(stringptr, &stringptr, 0); + if (corner < firstnumber || corner >= numberofpoints + firstnumber) { + printf("Error: Tetrahedron %d has an invalid vertex index.\n", + i + firstnumber); + terminatetetgen(1); + } + tetrahedronlist[index++] = corner; + } + // Read the tetrahedron's attributes. + for (j = 0; j < numberoftetrahedronattributes; j++) { + stringptr = findnextnumber(stringptr); + if (*stringptr == '\0') { + attrib = 0.0; + } else { + attrib = (REAL) strtod(stringptr, &stringptr); + } + tetrahedronattributelist[attribindex++] = attrib; + } + } + fclose(infile); + } + } // if (meshdim == 3) + + // Read the hullfaces or subfaces from a .face file if it exists. + if (mesh_dim == 3) { + infilename = infacefilename; + } else { + infilename = inelefilename; + } + infile = fopen(infilename, "r"); + if (infile != (FILE *) NULL) { + printf("Opening %s.\n", infilename); + // Read number of faces, boundary markers. + stringptr = readnumberline(inputline, infile, infilename); + numberoftrifaces = (int) strtol (stringptr, &stringptr, 0); + stringptr = findnextnumber(stringptr); + if (mesh_dim == 2) { + // Skip a number. + stringptr = findnextnumber(stringptr); + } + if (*stringptr == '\0') { + markers = 0; // Default there is no marker per face. + } else { + markers = (int) strtol (stringptr, &stringptr, 0); + } + if (numberoftrifaces > 0) { + trifacelist = new int[numberoftrifaces * 3]; + if (trifacelist == (int *) NULL) { + printf("Error: Out of memory.\n"); + terminatetetgen(1); + } + if (markers) { + trifacemarkerlist = new int[numberoftrifaces * 3]; + if (trifacemarkerlist == (int *) NULL) { + printf("Error: Out of memory.\n"); + terminatetetgen(1); + } + } + } + // Read the list of faces. + index = 0; + for (i = 0; i < numberoftrifaces; i++) { + // Read face index and the face's three corners. + stringptr = readnumberline(inputline, infile, infilename); + for (j = 0; j < 3; j++) { + stringptr = findnextnumber(stringptr); + if (*stringptr == '\0') { + printf("Error: Face %d is missing vertex %d in %s.\n", + i + firstnumber, j + 1, infilename); + terminatetetgen(1); + } + corner = (int) strtol(stringptr, &stringptr, 0); + if (corner < firstnumber || corner >= numberofpoints + firstnumber) { + printf("Error: Face %d has an invalid vertex index.\n", + i + firstnumber); + terminatetetgen(1); + } + trifacelist[index++] = corner; + } + // Read the boundary marker if it exists. + if (markers) { + stringptr = findnextnumber(stringptr); + if (*stringptr == '\0') { + attrib = 0.0; + } else { + attrib = (REAL) strtod(stringptr, &stringptr); + } + trifacemarkerlist[i] = (int) attrib; + } + } + fclose(infile); + } + + // Read the boundary edges from a .edge file if it exists. + infilename = inedgefilename; + infile = fopen(infilename, "r"); + if (infile != (FILE *) NULL) { + printf("Opening %s.\n", infilename); + // Read number of boundary edges. + stringptr = readnumberline(inputline, infile, infilename); + numberofedges = (int) strtol (stringptr, &stringptr, 0); + if (numberofedges > 0) { + edgelist = new int[numberofedges * 2]; + if (edgelist == (int *) NULL) { + printf("Error: Out of memory.\n"); + terminatetetgen(1); + } + stringptr = findnextnumber(stringptr); + if (*stringptr == '\0') { + markers = 0; // Default value. + } else { + markers = (int) strtol (stringptr, &stringptr, 0); + } + if (markers > 0) { + edgemarkerlist = new int[numberofedges]; + } + } + // Read the list of faces. + index = 0; + for (i = 0; i < numberofedges; i++) { + // Read face index and the edge's two endpoints. + stringptr = readnumberline(inputline, infile, infilename); + for (j = 0; j < 2; j++) { + stringptr = findnextnumber(stringptr); + if (*stringptr == '\0') { + printf("Error: Edge %d is missing vertex %d in %s.\n", + i + firstnumber, j + 1, infilename); + terminatetetgen(1); + } + corner = (int) strtol(stringptr, &stringptr, 0); + if (corner < firstnumber || corner >= numberofpoints + firstnumber) { + printf("Error: Edge %d has an invalid vertex index.\n", + i + firstnumber); + terminatetetgen(1); + } + edgelist[index++] = corner; + } + // Read the edge marker if it has. + if (markers) { + stringptr = findnextnumber(stringptr); + edgemarkerlist[i] = (int) strtol(stringptr, &stringptr, 0); + } + } + fclose(infile); + } + + // Read the volume constraints from a .vol file if it exists. + infilename = involfilename; + infile = fopen(infilename, "r"); + if (infile != (FILE *) NULL) { + printf("Opening %s.\n", infilename); + // Read number of tetrahedra. + stringptr = readnumberline(inputline, infile, infilename); + volelements = (int) strtol (stringptr, &stringptr, 0); + if (volelements != numberoftetrahedra) { + printf("Warning: %s and %s disagree on number of tetrahedra.\n", + inelefilename, involfilename); + volelements = 0; + } + if (volelements > 0) { + tetrahedronvolumelist = new REAL[volelements]; + if (tetrahedronvolumelist == (REAL *) NULL) { + printf("Error: Out of memory.\n"); + terminatetetgen(1); + } + } + // Read the list of volume constraints. + for (i = 0; i < volelements; i++) { + stringptr = readnumberline(inputline, infile, infilename); + stringptr = findnextnumber(stringptr); + if (*stringptr == '\0') { + volume = -1.0; // No constraint on this tetrahedron. + } else { + volume = (REAL) strtod(stringptr, &stringptr); + } + tetrahedronvolumelist[i] = volume; + } + fclose(infile); + } + + // Try to load a .mtr file if it exists. + load_mtr(filename); + // Try to read a .pbc file if it exists. + load_pbc(filename); + + return true; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// load_voronoi() Load a Voronoi diagram from files. // +// // +// 'filename' is the inputfile without suffix. The Voronoi diagram is read // +// from files: filename.v.node, filename.v.edge, and filename.v.face. // +// // +/////////////////////////////////////////////////////////////////////////////// + +bool tetgenio::load_voronoi(const char* filename) +{ + FILE *infile; + char innodefilename[FILENAMESIZE]; + char inedgefilename[FILENAMESIZE]; + char inputline[INPUTLINESIZE]; + char *stringptr, *infilename; + voroedge *vedge; + REAL x, y, z; + int firstnode, corner; + int index; + int i, j; + + // Assembling the actual file names we want to open. + strcpy(innodefilename, filename); + strcpy(inedgefilename, filename); + strcat(innodefilename, ".v.node"); + strcat(inedgefilename, ".v.edge"); + + // Read the points from a .v.node file. + infilename = innodefilename; + printf("Opening %s.\n", infilename); + infile = fopen(infilename, "r"); + if (infile == (FILE *) NULL) { + printf("File I/O Error: Cannot access file %s.\n", infilename); + return false; + } + // Read the first line of the file. + stringptr = readnumberline(inputline, infile, infilename); + // Is this list of points generated from rbox? + stringptr = strstr(inputline, "rbox"); + if (stringptr == NULL) { + // Read number of points, number of dimensions, number of point + // attributes, and number of boundary markers. + stringptr = inputline; + numberofvpoints = (int) strtol (stringptr, &stringptr, 0); + stringptr = findnextnumber(stringptr); + if (*stringptr == '\0') { + mesh_dim = 3; // Default. + } else { + mesh_dim = (int) strtol (stringptr, &stringptr, 0); + } + useindex = 1; // There is an index column. + } else { + // It is a rbox (qhull) input file. + stringptr = inputline; + // Get the dimension. + mesh_dim = (int) strtol (stringptr, &stringptr, 0); + // Get the number of points. + stringptr = readnumberline(inputline, infile, infilename); + numberofvpoints = (int) strtol (stringptr, &stringptr, 0); + useindex = 0; // No index column. + } + // Initialize 'vpointlist'. + vpointlist = new REAL[numberofvpoints * 3]; + if (vpointlist == (REAL *) NULL) { + printf("Error: Out of memory.\n"); + terminatetetgen(1); + } + // Read the point section. + index = 0; + for (i = 0; i < numberofvpoints; i++) { + stringptr = readnumberline(inputline, infile, infilename); + if (useindex) { + if (i == 0) { + firstnode = (int) strtol (stringptr, &stringptr, 0); + if ((firstnode == 0) || (firstnode == 1)) { + firstnumber = firstnode; + } + } + stringptr = findnextnumber(stringptr); + } // if (useindex) + if (*stringptr == '\0') { + printf("Error: Point %d has no x coordinate.\n", firstnumber + i); + terminatetetgen(1); + } + x = (REAL) strtod(stringptr, &stringptr); + stringptr = findnextnumber(stringptr); + if (*stringptr == '\0') { + printf("Error: Point %d has no y coordinate.\n", firstnumber + i); + terminatetetgen(1); + } + y = (REAL) strtod(stringptr, &stringptr); + if (mesh_dim == 3) { + stringptr = findnextnumber(stringptr); + if (*stringptr == '\0') { + printf("Error: Point %d has no z coordinate.\n", firstnumber + i); + terminatetetgen(1); + } + z = (REAL) strtod(stringptr, &stringptr); + } else { + z = 0.0; // mesh_dim == 2; + } + vpointlist[index++] = x; + vpointlist[index++] = y; + vpointlist[index++] = z; + } + fclose(infile); + + // Read the Voronoi edges from a .v.edge file if it exists. + infilename = inedgefilename; + infile = fopen(infilename, "r"); + if (infile != (FILE *) NULL) { + printf("Opening %s.\n", infilename); + // Read number of boundary edges. + stringptr = readnumberline(inputline, infile, infilename); + numberofvedges = (int) strtol (stringptr, &stringptr, 0); + if (numberofvedges > 0) { + vedgelist = new voroedge[numberofvedges]; + } + // Read the list of faces. + index = 0; + for (i = 0; i < numberofvedges; i++) { + // Read edge index and the edge's two endpoints. + stringptr = readnumberline(inputline, infile, infilename); + vedge = &(vedgelist[i]); + for (j = 0; j < 2; j++) { + stringptr = findnextnumber(stringptr); + if (*stringptr == '\0') { + printf("Error: Edge %d is missing vertex %d in %s.\n", + i + firstnumber, j + 1, infilename); + terminatetetgen(1); + } + corner = (int) strtol(stringptr, &stringptr, 0); + j == 0 ? vedge->v1 = corner : vedge->v2 = corner; + } + if (vedge->v2 < 0) { + for (j = 0; j < mesh_dim; j++) { + stringptr = findnextnumber(stringptr); + if (*stringptr == '\0') { + printf("Error: Edge %d is missing normal in %s.\n", + i + firstnumber, infilename); + terminatetetgen(1); + } + vedge->vnormal[j] = (REAL) strtod(stringptr, &stringptr); + } + if (mesh_dim == 2) { + vedge->vnormal[2] = 0.0; + } + } else { + vedge->vnormal[0] = 0.0; + vedge->vnormal[1] = 0.0; + vedge->vnormal[2] = 0.0; + } + } + fclose(infile); + } + + return true; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// save_nodes() Save points to a .node file. // +// // +// 'filename' is a string containing the file name without suffix. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenio::save_nodes(const char* filename) +{ + FILE *fout; + char outnodefilename[FILENAMESIZE]; + char outmtrfilename[FILENAMESIZE]; + int i, j; + + sprintf(outnodefilename, "%s.node", filename); + printf("Saving nodes to %s\n", outnodefilename); + fout = fopen(outnodefilename, "w"); + fprintf(fout, "%d %d %d %d\n", numberofpoints, mesh_dim, + numberofpointattributes, pointmarkerlist != NULL ? 1 : 0); + for (i = 0; i < numberofpoints; i++) { + if (mesh_dim == 2) { + fprintf(fout, "%d %.16g %.16g", i + firstnumber, pointlist[i * 3], + pointlist[i * 3 + 1]); + } else { + fprintf(fout, "%d %.16g %.16g %.16g", i + firstnumber, + pointlist[i * 3], pointlist[i * 3 + 1], pointlist[i * 3 + 2]); + } + for (j = 0; j < numberofpointattributes; j++) { + fprintf(fout, " %.16g", + pointattributelist[i * numberofpointattributes + j]); + } + if (pointmarkerlist != NULL) { + fprintf(fout, " %d", pointmarkerlist[i]); + } + fprintf(fout, "\n"); + } + fclose(fout); + + // If the point metrics exist, output them to a .mtr file. + if ((numberofpointmtrs > 0) && (pointmtrlist != (REAL *) NULL)) { + sprintf(outmtrfilename, "%s.mtr", filename); + printf("Saving metrics to %s\n", outmtrfilename); + fout = fopen(outmtrfilename, "w"); + fprintf(fout, "%d %d\n", numberofpoints, numberofpointmtrs); + for (i = 0; i < numberofpoints; i++) { + for (j = 0; j < numberofpointmtrs; j++) { + fprintf(fout, "%.16g ", pointmtrlist[i * numberofpointmtrs + j]); + } + fprintf(fout, "\n"); + } + fclose(fout); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// save_elements() Save elements to a .ele file. // +// // +// 'filename' is a string containing the file name without suffix. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenio::save_elements(const char* filename) +{ + FILE *fout; + char outelefilename[FILENAMESIZE]; + int i, j; + + sprintf(outelefilename, "%s.ele", filename); + printf("Saving elements to %s\n", outelefilename); + fout = fopen(outelefilename, "w"); + if (mesh_dim == 3) { + fprintf(fout, "%d %d %d\n", numberoftetrahedra, numberofcorners, + numberoftetrahedronattributes); + for (i = 0; i < numberoftetrahedra; i++) { + fprintf(fout, "%d", i + firstnumber); + for (j = 0; j < numberofcorners; j++) { + fprintf(fout, " %5d", tetrahedronlist[i * numberofcorners + j]); + } + for (j = 0; j < numberoftetrahedronattributes; j++) { + fprintf(fout, " %g", + tetrahedronattributelist[i * numberoftetrahedronattributes + j]); + } + fprintf(fout, "\n"); + } + } else { + // Save a two-dimensional mesh. + fprintf(fout, "%d %d %d\n", numberoftrifaces,3,trifacemarkerlist ? 1:0); + for (i = 0; i < numberoftrifaces; i++) { + fprintf(fout, "%d", i + firstnumber); + for (j = 0; j < 3; j++) { + fprintf(fout, " %5d", trifacelist[i * 3 + j]); + } + if (trifacemarkerlist != NULL) { + fprintf(fout, " %d", trifacemarkerlist[i]); + } + fprintf(fout, "\n"); + } + } + + fclose(fout); +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// save_faces() Save faces to a .face file. // +// // +// 'filename' is a string containing the file name without suffix. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenio::save_faces(const char* filename) +{ + FILE *fout; + char outfacefilename[FILENAMESIZE]; + int i; + + sprintf(outfacefilename, "%s.face", filename); + printf("Saving faces to %s\n", outfacefilename); + fout = fopen(outfacefilename, "w"); + fprintf(fout, "%d %d\n", numberoftrifaces, + trifacemarkerlist != NULL ? 1 : 0); + for (i = 0; i < numberoftrifaces; i++) { + fprintf(fout, "%d %5d %5d %5d", i + firstnumber, trifacelist[i * 3], + trifacelist[i * 3 + 1], trifacelist[i * 3 + 2]); + if (trifacemarkerlist != NULL) { + fprintf(fout, " %d", trifacemarkerlist[i]); + } + fprintf(fout, "\n"); + } + + fclose(fout); +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// save_edges() Save egdes to a .edge file. // +// // +// 'filename' is a string containing the file name without suffix. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenio::save_edges(const char* filename) +{ + FILE *fout; + char outedgefilename[FILENAMESIZE]; + int i; + + sprintf(outedgefilename, "%s.edge", filename); + printf("Saving edges to %s\n", outedgefilename); + fout = fopen(outedgefilename, "w"); + fprintf(fout, "%d %d\n", numberofedges, edgemarkerlist != NULL ? 1 : 0); + for (i = 0; i < numberofedges; i++) { + fprintf(fout, "%d %4d %4d", i + firstnumber, edgelist[i * 2], + edgelist[i * 2 + 1]); + if (edgemarkerlist != NULL) { + fprintf(fout, " %d", edgemarkerlist[i]); + } + fprintf(fout, "\n"); + } + + fclose(fout); +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// save_neighbors() Save egdes to a .neigh file. // +// // +// 'filename' is a string containing the file name without suffix. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenio::save_neighbors(const char* filename) +{ + FILE *fout; + char outneighborfilename[FILENAMESIZE]; + int i; + + sprintf(outneighborfilename, "%s.neigh", filename); + printf("Saving neighbors to %s\n", outneighborfilename); + fout = fopen(outneighborfilename, "w"); + fprintf(fout, "%d %d\n", numberoftetrahedra, mesh_dim + 1); + for (i = 0; i < numberoftetrahedra; i++) { + if (mesh_dim == 2) { + fprintf(fout, "%d %5d %5d %5d", i + firstnumber, neighborlist[i * 3], + neighborlist[i * 3 + 1], neighborlist[i * 3 + 2]); + } else { + fprintf(fout, "%d %5d %5d %5d %5d", i + firstnumber, + neighborlist[i * 4], neighborlist[i * 4 + 1], + neighborlist[i * 4 + 2], neighborlist[i * 4 + 3]); + } + fprintf(fout, "\n"); + } + + fclose(fout); +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// save_poly() Save segments or facets to a .poly file. // +// // +// 'filename' is a string containing the file name without suffix. It only // +// save the facets, holes and regions. The nodes are saved in a .node file // +// by routine save_nodes(). // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenio::save_poly(const char* filename) +{ + FILE *fout; + facet *f; + polygon *p; + char outpolyfilename[FILENAMESIZE]; + int i, j, k; + + sprintf(outpolyfilename, "%s.poly", filename); + printf("Saving poly to %s\n", outpolyfilename); + fout = fopen(outpolyfilename, "w"); + + // The zero indicates that the vertices are in a separate .node file. + // Followed by number of dimensions, number of vertex attributes, + // and number of boundary markers (zero or one). + fprintf(fout, "%d %d %d %d\n", 0, mesh_dim, numberofpointattributes, + pointmarkerlist != NULL ? 1 : 0); + + // Save segments or facets. + if (mesh_dim == 2) { + // Number of segments, number of boundary markers (zero or one). + fprintf(fout, "%d %d\n", numberofedges, edgemarkerlist != NULL ? 1 : 0); + for (i = 0; i < numberofedges; i++) { + fprintf(fout, "%d %4d %4d", i + firstnumber, edgelist[i * 2], + edgelist[i * 2 + 1]); + if (edgemarkerlist != NULL) { + fprintf(fout, " %d", edgemarkerlist[i]); + } + fprintf(fout, "\n"); + } + } else { + // Number of facets, number of boundary markers (zero or one). + fprintf(fout, "%d %d\n", numberoffacets, facetmarkerlist != NULL ? 1 : 0); + for (i = 0; i < numberoffacets; i++) { + f = &(facetlist[i]); + fprintf(fout, "%d %d %d # %d\n", f->numberofpolygons,f->numberofholes, + facetmarkerlist != NULL ? facetmarkerlist[i] : 0, i + firstnumber); + // Output polygons of this facet. + for (j = 0; j < f->numberofpolygons; j++) { + p = &(f->polygonlist[j]); + fprintf(fout, "%d ", p->numberofvertices); + for (k = 0; k < p->numberofvertices; k++) { + if (((k + 1) % 10) == 0) { + fprintf(fout, "\n "); + } + fprintf(fout, " %d", p->vertexlist[k]); + } + fprintf(fout, "\n"); + } + // Output holes of this facet. + for (j = 0; j < f->numberofholes; j++) { + fprintf(fout, "%d %.12g %.12g %.12g\n", j + firstnumber, + f->holelist[j * 3], f->holelist[j * 3 + 1], f->holelist[j * 3 + 2]); + } + } + } + + // Save holes. + fprintf(fout, "%d\n", numberofholes); + for (i = 0; i < numberofholes; i++) { + // Output x, y coordinates. + fprintf(fout, "%d %.12g %.12g", i + firstnumber, holelist[i * mesh_dim], + holelist[i * mesh_dim + 1]); + if (mesh_dim == 3) { + // Output z coordinate. + fprintf(fout, " %.12g", holelist[i * mesh_dim + 2]); + } + fprintf(fout, "\n"); + } + + // Save regions. + fprintf(fout, "%d\n", numberofregions); + for (i = 0; i < numberofregions; i++) { + if (mesh_dim == 2) { + // Output the index, x, y coordinates, attribute (region number) + // and maximum area constraint (maybe -1). + fprintf(fout, "%d %.12g %.12g %.12g %.12g\n", i + firstnumber, + regionlist[i * 4], regionlist[i * 4 + 1], + regionlist[i * 4 + 2], regionlist[i * 4 + 3]); + } else { + // Output the index, x, y, z coordinates, attribute (region number) + // and maximum volume constraint (maybe -1). + fprintf(fout, "%d %.12g %.12g %.12g %.12g %.12g\n", i + firstnumber, + regionlist[i * 5], regionlist[i * 5 + 1], + regionlist[i * 5 + 2], regionlist[i * 5 + 3], + regionlist[i * 5 + 4]); + } + } + + fclose(fout); +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// readline() Read a nonempty line from a file. // +// // +// A line is considered "nonempty" if it contains something more than white // +// spaces. If a line is considered empty, it will be dropped and the next // +// line will be read, this process ends until reaching the end-of-file or a // +// non-empty line. Return NULL if it is the end-of-file, otherwise, return // +// a pointer to the first non-whitespace character of the line. // +// // +/////////////////////////////////////////////////////////////////////////////// + +char* tetgenio::readline(char *string, FILE *infile, int *linenumber) +{ + char *result; + + // Search for a non-empty line. + do { + result = fgets(string, INPUTLINESIZE - 1, infile); + if (linenumber) (*linenumber)++; + if (result == (char *) NULL) { + return (char *) NULL; + } + // Skip white spaces. + while ((*result == ' ') || (*result == '\t')) result++; + // If it's end of line, read another line and try again. + } while (*result == '\0'); + return result; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// findnextfield() Find the next field of a string. // +// // +// Jumps past the current field by searching for whitespace or a comma, then // +// jumps past the whitespace or the comma to find the next field. // +// // +/////////////////////////////////////////////////////////////////////////////// + +char* tetgenio::findnextfield(char *string) +{ + char *result; + + result = string; + // Skip the current field. Stop upon reaching whitespace or a comma. + while ((*result != '\0') && (*result != ' ') && (*result != '\t') && + (*result != ',') && (*result != ';')) { + result++; + } + // Now skip the whitespace or the comma, stop at anything else that looks + // like a character, or the end of a line. + while ((*result == ' ') || (*result == '\t') || (*result == ',') || + (*result == ';')) { + result++; + } + return result; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// readnumberline() Read a nonempty number line from a file. // +// // +// A line is considered "nonempty" if it contains something that looks like // +// a number. Comments (prefaced by `#') are ignored. // +// // +/////////////////////////////////////////////////////////////////////////////// + +char* tetgenio::readnumberline(char *string, FILE *infile, + const char *infilename) +{ + char *result; + + // Search for something that looks like a number. + do { + result = fgets(string, INPUTLINESIZE, infile); + if (result == (char *) NULL) { + if (infilename != (char *) NULL) { + printf(" Error: Unexpected end of file in %s.\n", infilename); + terminatetetgen(1); + } + return result; + } + // Skip anything that doesn't look like a number, a comment, + // or the end of a line. + while ((*result != '\0') && (*result != '#') + && (*result != '.') && (*result != '+') && (*result != '-') + && ((*result < '0') || (*result > '9'))) { + result++; + } + // If it's a comment or end of line, read another line and try again. + } while ((*result == '#') || (*result == '\0')); + return result; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// findnextnumber() Find the next field of a number string. // +// // +// Jumps past the current field by searching for whitespace or a comma, then // +// jumps past the whitespace or the comma to find the next field that looks // +// like a number. // +// // +/////////////////////////////////////////////////////////////////////////////// + +char* tetgenio::findnextnumber(char *string) +{ + char *result; + + result = string; + // Skip the current field. Stop upon reaching whitespace or a comma. + while ((*result != '\0') && (*result != '#') && (*result != ' ') && + (*result != '\t') && (*result != ',')) { + result++; + } + // Now skip the whitespace and anything else that doesn't look like a + // number, a comment, or the end of a line. + while ((*result != '\0') && (*result != '#') + && (*result != '.') && (*result != '+') && (*result != '-') + && ((*result < '0') || (*result > '9'))) { + result++; + } + // Check for a comment (prefixed with `#'). + if (*result == '#') { + *result = '\0'; + } + return result; +} + +#endif // #ifndef tetgenioCXX \ No newline at end of file diff --git a/contrib/TetgenNew/main.cxx b/contrib/TetgenNew/main.cxx new file mode 100644 index 0000000000..82edf9afd5 --- /dev/null +++ b/contrib/TetgenNew/main.cxx @@ -0,0 +1,387 @@ +#ifndef mainCXX +#define mainCXX + +#include "tetgen.h" + +/////////////////////////////////////////////////////////////////////////////// +// // +// test_tri_tri() Triangle-triangle intersection tests. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void test_tri_tri(tetgenbehavior *b, tetgenio *in) +{ + tetgenmesh m; + tetgenmesh::face s1, s2; + tetgenmesh::point U[3], V[3], T1[3], T2[3]; + tetgenmesh::intersection dir; + int types[2], pos[4]; + int i, j; + + m.b = b; + m.in = in; + exactinit(); + m.initializepools(); + m.transfernodes(); + m.meshsurface(); + + m.subfacepool->traversalinit(); + s1.sh = m.shellfacetraverse(m.subfacepool); + s2.sh = m.shellfacetraverse(m.subfacepool); + + V[0] = (tetgenmesh::point) s1.sh[3]; // P + V[1] = (tetgenmesh::point) s1.sh[4]; // Q + V[2] = (tetgenmesh::point) s1.sh[5]; // R + + U[0] = (tetgenmesh::point) s2.sh[3]; // A + U[1] = (tetgenmesh::point) s2.sh[4]; // B + U[2] = (tetgenmesh::point) s2.sh[5]; // C + + // The permuations of {0, 1, 2} + int perm[6][3] = { + {0, 1, 2}, + {2, 0, 1}, + {1, 2, 0}, + {1, 0, 2}, + {2, 1, 0}, + {0, 2, 1}}; + + b->epsilon = 0; + b->verbose = 3; + + for (j = 0; j < 36; j++) { + + // Get the permuation of the vertices. + T1[0] = U[perm[j/6][0]]; + T1[1] = U[perm[j/6][1]]; + T1[2] = U[perm[j/6][2]]; + + T2[0] = V[perm[j%6][0]]; + T2[1] = V[perm[j%6][1]]; + T2[2] = V[perm[j%6][2]]; + + i = m.tri_tri_test(T1[0], T1[1], T1[2], T2[0], T2[1], T2[2], NULL, 1, + types, pos); + if (i == 0) { + printf(" [%d] DISJOINT.\n", j); + continue; + } + + // Report the intersection types and positions. + for (i = 0; i < 2; i++) { + dir = (enum tetgenmesh::intersection) types[i]; + switch (dir) { + case tetgenmesh::DISJOINT: + printf(" [%d] DISJOINT\n", j); break; + case tetgenmesh::SHAREVERT: + printf(" [%d] SHAREVERT %d %d\n", j, pos[i*2], pos[i*2+1]); break; + case tetgenmesh::SHAREEDGE: + printf(" [%d] SHAREEDGE %d %d\n", j, pos[i*2], pos[i*2+1]); break; + case tetgenmesh::SHAREFACE: + printf(" [%d] SHAREFACE\n", j); break; + case tetgenmesh::TOUCHEDGE: + printf(" [%d] TOUCHEDGE %d %d\n", j, pos[i*2], pos[i*2+1]); break; + case tetgenmesh::TOUCHFACE: + printf(" [%d] TOUCHFACE %d %d\n", j, pos[i*2], pos[i*2+1]); break; + case tetgenmesh::ACROSSVERT: + printf(" [%d] ACROSSVERT %d %d\n", j, pos[i*2], pos[i*2+1]); break; + case tetgenmesh::ACROSSEDGE: + printf(" [%d] ACROSSEDGE %d %d\n", j, pos[i*2], pos[i*2+1]); break; + case tetgenmesh::ACROSSFACE: + printf(" [%d] ACROSSFACE %d %d\n", j, pos[i*2], pos[i*2+1]); break; + case tetgenmesh::ACROSSTET: + printf(" [%d] ACROSSTET\n", j); break; + case tetgenmesh::TRIEDGEINT: + printf(" [%d] TRIEDGEINT %d %d\n", j, pos[i*2], pos[i*2+1]); break; + case tetgenmesh::EDGETRIINT: + printf(" [%d] EDGETRIINT %d %d\n", j, pos[i*2], pos[i*2+1]); break; + } + } + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// tetrahedralize() The interface for users using TetGen library to // +// generate tetrahedral meshes with all features. // +// // +// The sequence is roughly as follows. Many of these steps can be skipped, // +// depending on the command line switches. // +// // +// - Initialize constants and parse the command line. // +// - Read the vertices from a file and either // +// - tetrahedralize them (no -r), or // +// - read an old mesh from files and reconstruct it (-r). // +// - Insert the PLC segments and facets (-p). // +// - Read the holes (-p), regional attributes (-pA), and regional volume // +// constraints (-pa). Carve the holes and concavities, and spread the // +// regional attributes and volume constraints. // +// - Enforce the constraints on minimum quality bound (-q) and maximum // +// volume (-a). Also enforce the conforming Delaunay property (-q and -a). // +// - Promote the mesh's linear tetrahedra to higher order elements (-o). // +// - Write the output files and print the statistics. // +// - Check the consistency and Delaunay property of the mesh (-C). // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetrahedralize(tetgenbehavior *b, tetgenio *in, tetgenio *out, + tetgenio *addin, tetgenio *bgmin) +{ + tetgenmesh m; + // Variables for timing the performance of TetGen (defined in time.h). + clock_t tv[17]; + + tv[0] = clock(); + + m.b = b; + m.in = in; + exactinit(); + m.initializepools(); + m.transfernodes(); + + tv[1] = clock(); + + if (b->refine) { + // m.reconstructmesh(); + } else { + m.incrementaldelaunay(); + } + + tv[2] = clock(); + + if (!b->quiet) { + if (b->refine) { + printf("Mesh reconstruction seconds:"); + } else { + printf("Delaunay seconds:"); + } + printf(" %g\n", (tv[2] - tv[1]) / (REAL) CLOCKS_PER_SEC); + } + + if (b->plc) { // if has -p option + m.meshsurface(); + } + + tv[3] = clock(); + + if (!b->quiet) { + if (b->plc) { + printf("Surface meshing seconds: %g\n", + (tv[3] - tv[2]) / (REAL) CLOCKS_PER_SEC); + } + } + + if (b->plc) { // if has -p option + m.formskeleton(); + } + + tv[4] = clock(); + + if (!b->quiet) { + if (b->plc) { + if (b->diagnose != 1) { + printf("Boundary recovery "); + } else { + printf("Intersection "); + } + printf("seconds: %g\n", (tv[4] - tv[3]) / (REAL) CLOCKS_PER_SEC); + } + } + + if (b->quality) { + m.enforcequality(); + } + + tv[5] = clock(); + + if (!b->quiet) { + if (b->quality) { + printf("Quality seconds: %g\n", (tv[5] - tv[4])/(REAL) CLOCKS_PER_SEC); + } + } + + if (b->plc) { + if (b->convexity == 0) { // if has no -c option. + m.carveholes(); + } + } + + tv[6] = clock(); + + if (!b->quiet) { + if (b->plc) { + if (b->convexity == 0) { // if has no -c option. + printf("Holes and region seconds: %g\n", + (tv[6] - tv[5]) / (REAL) CLOCKS_PER_SEC); + } + } + } + + printf("\n"); + + if (out != (tetgenio *) NULL) { + out->firstnumber = in->firstnumber; + out->mesh_dim = in->mesh_dim; + } + + if (b->nonodewritten || b->noiterationnum) { + if (!b->quiet) { + printf("NOT writing a .node file.\n"); + } + } else { + m.outnodes(out); + } + + if (b->noelewritten == 1) { + if (!b->quiet) { + printf("NOT writing an .ele file.\n"); + } + m.numberedges(); + } else { + m.outelements(out); + } + + if (b->nofacewritten) { + if (!b->quiet) { + printf("NOT writing an .face file.\n"); + } + if (b->plc || b->refine) { + m.numbersubedges(); + } + } else { + if (b->facesout) { + if (m.tetrahedronpool->items > 0l) { + m.outfaces(out); // Output all faces. + } + if (b->plc || b->refine) { + m.numberedges(); + } + } else { + if (b->plc || b->refine) { + if (m.subfacepool->items > 0l) { + m.outsubfaces(out); // Output boundary faces. + } + } else { + if (m.tetrahedronpool->items > 0l) { + m.outhullfaces(out); // Output convex hull faces. + } + } + } + } + + if (b->edgesout) { + if (b->edgesout > 1) { + m.outedges(out); // -ee, output all mesh edges. + } else { + m.outsubsegments(out); // -e, only output subsegments. + } + } + + if (b->neighout) { + m.outneighbors(out); + } + + if (b->voroout) { + // m.outvoronoi(out); + } + + tv[6] = clock(); + + if (!b->quiet) { + printf("\nOutput seconds: %g\n", + (tv[6] - tv[5]) / (REAL) CLOCKS_PER_SEC); + printf("Total running seconds: %g\n\n", + (tv[6] - tv[0]) / (REAL) CLOCKS_PER_SEC); + } + + if (b->docheck) { + if (b->plc) { + if (m.checkshells(1) > 0) assert(0); + if (m.checksegments() > 0) assert(0); + } + if (m.checkdelaunay(b->plc) > 0) assert(0); + } + + if (!b->quiet) { + m.statistics(); + } +} + +#ifndef TETLIBRARY + +/////////////////////////////////////////////////////////////////////////////// +// // +// main() The entrance for running TetGen from command line. // +// // +/////////////////////////////////////////////////////////////////////////////// + +int main(int argc, char *argv[]) + +#else // with TETLIBRARY + +/////////////////////////////////////////////////////////////////////////////// +// // +// tetrahedralize() The entrance for calling TetGen from another program. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetrahedralize(char *switches, tetgenio *in, tetgenio *out, + tetgenio *addin, tetgenio *bgmin) + +#endif // not TETLIBRARY + +{ + tetgenbehavior b; + +#ifndef TETLIBRARY + + tetgenio in, addin, bgmin; + + if (!b.parse_commandline(argc, argv)) { + terminatetetgen(1); + } + if (b.refine) { + if (!in.load_tetmesh(b.infilename)) { + terminatetetgen(1); + } + } else { + if (!in.load_plc(b.infilename, (int) b.object)) { + terminatetetgen(1); + } + } + if (b.insertaddpoints) { + if (!addin.load_node(b.addinfilename)) { + addin.numberofpoints = 0l; + } + } + if (b.metric) { + if (!bgmin.load_tetmesh(b.bgmeshfilename)) { + bgmin.numberoftetrahedra = 0l; + } + } + + // FOR DEBUG -S1 option. + if (b.steinerleft > 0) { + test_tri_tri(&b, &in); + terminatetetgen(0); + } + + if (bgmin.numberoftetrahedra > 0l) { + tetrahedralize(&b, &in, NULL, &addin, &bgmin); + } else { + tetrahedralize(&b, &in, NULL, &addin, NULL); + } + + return 0; + +#else // with TETLIBRARY + + if (!b.parse_commandline(switches)) { + terminatetetgen(1); + } + tetrahedralize(&b, in, out, addin, bgmin); + +#endif // not TETLIBRARY +} + +#endif // #ifndef mainCXX \ No newline at end of file diff --git a/contrib/TetgenNew/memorypool.cxx b/contrib/TetgenNew/memorypool.cxx new file mode 100644 index 0000000000..21a0ff623e --- /dev/null +++ b/contrib/TetgenNew/memorypool.cxx @@ -0,0 +1,981 @@ +#ifndef memorypoolCXX +#define memorypoolCXX + +#include "tetgen.h" + +/////////////////////////////////////////////////////////////////////////////// +// // +// restart() Deallocate all objects in this pool. // +// // +// The pool returns to a fresh state, like after it was initialized, except // +// that no memory is freed to the operating system. Rather, the previously // +// allocated blocks are ready to be used. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::arraypool::restart() +{ + objects = 0l; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// poolinit() Initialize an arraypool for allocation of objects. // +// // +// Before the pool may be used, it must be initialized by this procedure. // +// After initialization, memory can be allocated and freed in this pool. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::arraypool::poolinit(int sizeofobject, int log2objperblk) +{ + // Each object must be at least one byte long. + objectbytes = sizeofobject > 1 ? sizeofobject : 1; + + log2objectsperblock = log2objperblk; + // Compute the number of objects in each block. + objectsperblock = ((int) 1) << log2objectsperblock; + + // No memory has been allocated. + totalmemory = 0l; + // The top array has not been allocated yet. + toparray = (char **) NULL; + toparraylen = 0; + + // Ready all indices to be allocated. + restart(); +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// arraypool() The constructor and destructor. // +// // +/////////////////////////////////////////////////////////////////////////////// + +tetgenmesh::arraypool::arraypool(int sizeofobject, int log2objperblk) +{ + poolinit(sizeofobject, log2objperblk); +} + +tetgenmesh::arraypool::~arraypool() +{ + int i; + + // Has anything been allocated at all? + if (toparray != (char **) NULL) { + // Walk through the top array. + for (i = 0; i < toparraylen; i++) { + // Check every pointer; NULLs may be scattered randomly. + if (toparray[i] != (char *) NULL) { + // Free an allocated block. + free((void *) toparray[i]); + } + } + // Free the top array. + free((void *) toparray); + } + + // The top array is no longer allocated. + toparray = (char **) NULL; + toparraylen = 0; + objects = 0; + totalmemory = 0; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// getblock() Return (and perhaps create) the block containing the object // +// with a given index. // +// // +// This function takes care of allocating or resizing the top array if nece- // +// ssary, and of allocating the block if it hasn't yet been allocated. // +// // +// Return a pointer to the beginning of the block (NOT the object). // +// // +/////////////////////////////////////////////////////////////////////////////// + +char* tetgenmesh::arraypool::getblock(int objectindex) +{ + char **newarray; + char *block; + int newsize; + int topindex; + int i; + + // Compute the index in the top array (upper bits). + topindex = objectindex >> log2objectsperblock; + // Does the top array need to be allocated or resized? + if (toparray == (char **) NULL) { + // Allocate the top array big enough to hold 'topindex', and NULL out + // its contents. + newsize = topindex + 128; + toparray = (char **) malloc((size_t) (newsize * sizeof(char *))); + toparraylen = newsize; + for (i = 0; i < newsize; i++) { + toparray[i] = (char *) NULL; + } + // Account for the memory. + totalmemory = newsize * (unsigned long) sizeof(char *); + } else if (topindex >= toparraylen) { + // Resize the top array, making sure it holds 'topindex'. + newsize = 3 * toparraylen; + if (topindex >= newsize) { + newsize = topindex + 128; + } + // Allocate the new array, copy the contents, NULL out the rest, and + // free the old array. + newarray = (char **) malloc((size_t) (newsize * sizeof(char *))); + for (i = 0; i < toparraylen; i++) { + newarray[i] = toparray[i]; + } + for (i = toparraylen; i < newsize; i++) { + newarray[i] = (char *) NULL; + } + free(toparray); + // Account for the memory. + totalmemory += (newsize - toparraylen) * sizeof(char *); + toparray = newarray; + toparraylen = newsize; + } + + // Find the block, or learn that it hasn't been allocated yet. + block = toparray[topindex]; + if (block == (char *) NULL) { + // Allocate a block at this index. + block = (char *) malloc((size_t) (objectsperblock * objectbytes)); + toparray[topindex] = block; + // Account for the memory. + totalmemory += objectsperblock * objectbytes; + } + + // Return a pointer to the block. + return block; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// lookup() Return the pointer to the object with a given index, or NULL // +// if the object's block doesn't exist yet. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void* tetgenmesh::arraypool::lookup(int objectindex) +{ + char *block; + int topindex; + + // Has the top array been allocated yet? + if (toparray == (char **) NULL) { + return (void *) NULL; + } + + // Compute the index in the top array (upper bits). + topindex = objectindex >> log2objectsperblock; + // Does the top index fit in the top array? + if (topindex >= toparraylen) { + return (void *) NULL; + } + + // Find the block, or learn that it hasn't been allocated yet. + block = toparray[topindex]; + if (block == (char *) NULL) { + return (void *) NULL; + } + + // Compute a pointer to the object with the given index. Note that + // 'objectsperblock' is a power of two, so the & operation is a bit mask + // that preserves the lower bits. + return (void *)(block + (objectindex & (objectsperblock - 1)) * objectbytes); +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// newindex() Allocate space for a fresh object from the pool. // +// // +/////////////////////////////////////////////////////////////////////////////// + +int tetgenmesh::arraypool::newindex(void **newptr) +{ + void *newobject; + int newindex; + + // Allocate an object at index 'firstvirgin'. + newindex = objects; + newobject = (void *) (getblock(objects) + + (objects & (objectsperblock - 1)) * objectbytes); + objects++; + + // If 'newptr' is not NULL, use it to return a pointer to the object. + if (newptr != (void **) NULL) { + *newptr = newobject; + } + return newindex; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// restart() Deallocate all items in this pool. // +// // +// The pool is returned to its starting state, except that no memory is // +// freed to the operating system. Rather, the previously allocated blocks // +// are ready to be reused. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::memorypool::restart() +{ + unsigned long alignptr; + + items = 0; + maxitems = 0; + + // Set the currently active block. + nowblock = firstblock; + // Find the first item in the pool. Increment by the size of (void *). + alignptr = (unsigned long) (nowblock + 1); + // Align the item on an `alignbytes'-byte boundary. + nextitem = (void *) + (alignptr + (unsigned long) alignbytes - + (alignptr % (unsigned long) alignbytes)); + // There are lots of unallocated items left in this block. + unallocateditems = itemsperblock; + // The stack of deallocated items is empty. + deaditemstack = (void *) NULL; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// alloc() Allocate space for an item. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void* tetgenmesh::memorypool::alloc() +{ + void *newitem; + void **newblock; + unsigned long alignptr; + + // First check the linked list of dead items. If the list is not + // empty, allocate an item from the list rather than a fresh one. + if (deaditemstack != (void *) NULL) { + newitem = deaditemstack; // Take first item in list. + deaditemstack = * (void **) deaditemstack; + } else { + // Check if there are any free items left in the current block. + if (unallocateditems == 0) { + // Check if another block must be allocated. + if (*nowblock == (void *) NULL) { + // Allocate a new block of items, pointed to by the previous block. + newblock = (void **) malloc(itemsperblock * itembytes + sizeof(void *) + + alignbytes); + if (newblock == (void **) NULL) { + printf("Error: Out of memory.\n"); + terminatetetgen(1); + } + *nowblock = (void *) newblock; + // The next block pointer is NULL. + *newblock = (void *) NULL; + } + // Move to the new block. + nowblock = (void **) *nowblock; + // Find the first item in the block. + // Increment by the size of (void *). + alignptr = (unsigned long) (nowblock + 1); + // Align the item on an `alignbytes'-byte boundary. + nextitem = (void *) + (alignptr + (unsigned long) alignbytes - + (alignptr % (unsigned long) alignbytes)); + // There are lots of unallocated items left in this block. + unallocateditems = itemsperblock; + } + // Allocate a new item. + newitem = nextitem; + // Advance `nextitem' pointer to next free item in block. + if (itemwordtype == POINTER) { + nextitem = (void *) ((void **) nextitem + itemwords); + } else { + nextitem = (void *) ((REAL *) nextitem + itemwords); + } + unallocateditems--; + maxitems++; + } + items++; + return newitem; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// dealloc() Deallocate space for an item. // +// // +// The deallocated space is stored in a queue for later reuse. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::memorypool::dealloc(void *dyingitem) +{ + // Push freshly killed item onto stack. + *((void **) dyingitem) = deaditemstack; + deaditemstack = dyingitem; + items--; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// traversalinit() Prepare to traverse the entire list of items. // +// // +// This routine is used in conjunction with traverse(). // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::memorypool::traversalinit() +{ + unsigned long alignptr; + + // Begin the traversal in the first block. + pathblock = firstblock; + // Find the first item in the block. Increment by the size of (void *). + alignptr = (unsigned long) (pathblock + 1); + // Align with item on an `alignbytes'-byte boundary. + pathitem = (void *) + (alignptr + (unsigned long) alignbytes - + (alignptr % (unsigned long) alignbytes)); + // Set the number of items left in the current block. + pathitemsleft = itemsperblock; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// traverse() Find the next item in the list. // +// // +// This routine is used in conjunction with traversalinit(). Be forewarned // +// that this routine successively returns all items in the list, including // +// deallocated ones on the deaditemqueue. It's up to you to figure out which // +// ones are actually dead. It can usually be done more space-efficiently by // +// a routine that knows something about the structure of the item. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void* tetgenmesh::memorypool::traverse() +{ + void *newitem; + unsigned long alignptr; + + // Stop upon exhausting the list of items. + if (pathitem == nextitem) { + return (void *) NULL; + } + // Check whether any untraversed items remain in the current block. + if (pathitemsleft == 0) { + // Find the next block. + pathblock = (void **) *pathblock; + // Find the first item in the block. Increment by the size of (void *). + alignptr = (unsigned long) (pathblock + 1); + // Align with item on an `alignbytes'-byte boundary. + pathitem = (void *) + (alignptr + (unsigned long) alignbytes - + (alignptr % (unsigned long) alignbytes)); + // Set the number of items left in the current block. + pathitemsleft = itemsperblock; + } + newitem = pathitem; + // Find the next item in the block. + if (itemwordtype == POINTER) { + pathitem = (void *) ((void **) pathitem + itemwords); + } else { + pathitem = (void *) ((REAL *) pathitem + itemwords); + } + pathitemsleft--; + return newitem; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// poolinit() Initialize a pool of memory for allocation of items. // +// // +// A 'pool' is created whose records have size at least 'bytecount'. Items // +// will be allocated in `itemcount'-item blocks. Each item is assumed to be // +// a collection of words, and either pointers or floating-point values are // +// assumed to be the "primary" word type. (The "primary" word type is used // +// to determine alignment of items.) If 'alignment' isn't zero, all items // +// will be `alignment'-byte aligned in memory. 'alignment' must be either a // +// multiple or a factor of the primary word size; powers of two are safe. // +// 'alignment' is normally used to create a few unused bits at the bottom of // +// each item's pointer, in which information may be stored. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::memorypool::poolinit(int bytecount, int itemcount, + enum wordtype wtype, int alignment) +{ + int wordsize; + + // Initialize values in the pool. + itemwordtype = wtype; + wordsize = (itemwordtype == POINTER) ? sizeof(void *) : sizeof(REAL); + // Find the proper alignment, which must be at least as large as: + // - The parameter `alignment'. + // - The primary word type, to avoid unaligned accesses. + // - sizeof(void *), so the stack of dead items can be maintained + // without unaligned accesses. + if (alignment > wordsize) { + alignbytes = alignment; + } else { + alignbytes = wordsize; + } + if ((int) sizeof(void *) > alignbytes) { + alignbytes = (int) sizeof(void *); + } + itemwords = ((bytecount + alignbytes - 1) / alignbytes) + * (alignbytes / wordsize); + itembytes = itemwords * wordsize; + itemsperblock = itemcount; + + // Allocate a block of items. Space for `itemsperblock' items and one + // pointer (to point to the next block) are allocated, as well as space + // to ensure alignment of the items. + firstblock = (void **) malloc(itemsperblock * itembytes + sizeof(void *) + + alignbytes); + if (firstblock == (void **) NULL) { + printf("Error: Out of memory.\n"); + terminatetetgen(1); + } + // Set the next block pointer to NULL. + *(firstblock) = (void *) NULL; + restart(); +} + +tetgenmesh::memorypool::memorypool(int bytecount, int itemcount, + enum wordtype wtype, int alignment) +{ + poolinit(bytecount, itemcount, wtype, alignment); +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// ~memorypool() Free to the operating system all memory taken by a pool. // +// // +/////////////////////////////////////////////////////////////////////////////// + +tetgenmesh::memorypool::~memorypool() +{ + while (firstblock != (void **) NULL) { + nowblock = (void **) *(firstblock); + free(firstblock); + firstblock = nowblock; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// initializepools() Initialize pools of mesh elements. // +// // +// The sizes of the tetrahedron, shellface, and point will be calculated. // +// Some class variables, such as 'pointmarkindex', 'elemmarkindex', 'volume- // +// boundindex', 'dummypoint', etc, are initialized. The pools of tetrahedra, // +// points, subfaces, and segments are allocated. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::initializepools() +{ + enum wordtype wtype; + int ptsize, elesize, shsize; + + // A point contains 3 coordinates, 1 weight, plus 'n' attributes in REALs, + // and other fields, such as pointers, boundary markers, etc. The total + // size (in byte) of a point is calcualted below. + ptsize = (4 + in->numberofpointattributes) * sizeof(REAL); + // The index within each point at which an element pointer is found, where + // the index is measured in pointers. Ensure the index is aligned to a + // sizeof(tetrahedron)-byte address. + point2tetindex = (ptsize + sizeof(tetrahedron) - 1) / sizeof(tetrahedron); + // Increase the point size by two pointers, which are + // - a pointer to a tet, read by point2tet(), or + // - a pointer to a parent point, read by point2ppt(). + ptsize = (point2tetindex + 2) * sizeof(tetrahedron); + // The index within each point at which the boundary marker is found, + // Ensure the marker is aligned to a sizeof(int)-byte address. + pointmarkindex = (ptsize + sizeof(int) - 1) / sizeof(int); + // Increase the point size by two integers, which are: + // - an integer for boundary marker, read by pointmark(); + // - an integer for vertex type, read by pointtype(); + ptsize = (pointmarkindex + 2) * sizeof(int); + // Decide the wordtype used in point pool. + wtype = (sizeof(REAL) >= sizeof(tetrahedron)) ? FLOATINGPOINT : POINTER; + // Initialize the pool of vertices. + pointpool = new memorypool(ptsize, VERPERBLOCK, wtype, 0); + + // Initialize spaces for 'dummypoint'. + dummypoint = new REAL[(ptsize + sizeof(REAL) - 1) / sizeof(REAL)]; + dummypoint[0] = dummypoint[1] = dummypoint[2] = dummypoint[3] = 0.0; + pointmark(dummypoint) = -1; + + // The number of bytes occupied by a tetrahedron. There are 4 pointers + // to other tetrahedra, 4 pointers to corners, and possibly 1 pointers + // to an array of subfaces/subsegments. + elesize = (8 + (b->useshelles ? 2 : 0)) * sizeof(tetrahedron); + // The index within each element at which its attributes are found, where + // the index is measured in REALs. + elemattribindex = (elesize + sizeof(REAL) - 1) / sizeof(REAL); + // The index within each element at which the maximum voulme bound is + // found, where the index is measured in REALs. Note that if the + // `b->regionattrib' flag is set, an additional attribute will be added. + volumeboundindex = elemattribindex + in->numberoftetrahedronattributes + + (b->regionattrib > 0); + // If element attributes or an constraint are needed, increase the number + // of bytes occupied by an element. + elesize = (volumeboundindex + (b->varvolume > 0)) * sizeof(REAL); + // The index within each element at which its marker is found, where the + // index is measured in ints. + elemmarkerindex = (elesize + sizeof(int) - 1) / sizeof(int); + // Increase the size by one interger. + elesize = (elemmarkerindex + 1) * sizeof(int); + // If -o2 switch is used, an additional pointer pointed to the list of + // higher order nodes is allocated for each element. + highorderindex = (elesize + sizeof(tetrahedron) - 1) / sizeof(tetrahedron); + if (b->order == 2) { + elesize = (highorderindex + 1) * sizeof(tetrahedron); + } + // Having determined the memory size of an element, initialize the pools. + tetrahedronpool = new memorypool(elesize, ELEPERBLOCK, POINTER, 16); + + if (b->useshelles) { + // The number of bytes occupied by a subface. The list of pointers + // stored in a subface are: three to other subfaces, three to corners, + // three to subsegments, one to an adjacent tetrahedron. + shsize = 10 * sizeof(shellface); + // The index within each subface at which the maximum area bound is + // found, where the index is measured in REALs. + areaboundindex = (shsize + sizeof(REAL) - 1) / sizeof(REAL); + // If -q switch is in use, increase the number of bytes occupied by + // a subface for saving maximum area bound. + if (b->quality && checkconstraints) { + shsize = (areaboundindex + 1) * sizeof(REAL); + } else { + shsize = areaboundindex * sizeof(REAL); + } + // The index within subface at which the facet marker is found. Ensure + // the marker is aligned to a sizeof(int)-byte address. + shmarkindex = (shsize + sizeof(int) - 1) / sizeof(int); + // Increase the number of bytes by two or three integers, one for facet + // marker, one for shellface type, and optionally one for pbc group. + shsize = (shmarkindex + 2 + checkpbcs) * sizeof(int); + // Initialize the pool of subfaces. Each subface record is eight-byte + // aligned so it has room to store an edge version (from 0 to 5) in + // the least three bits. + subfacepool = new memorypool(shsize, SUBPERBLOCK, POINTER, 8); + // Initialize the pool of subsegments. + subsegpool = new memorypool(shsize, SUBPERBLOCK, POINTER, 8); + + // Initialize the pool for tet-subseg connections. + tet2segpool = new memorypool(6*sizeof(shellface), SUBPERBLOCK, POINTER, 0); + // Initialize the pool for tet-subface connections. + tet2subpool = new memorypool(4*sizeof(shellface), SUBPERBLOCK, POINTER, 0); + + // Initialize arraypools for segment & facet recovery. + subsegstack = new arraypool(sizeof(face), 10); + subfacstack = new arraypool(sizeof(face), 10); + + // Initialize arraypools for surface Bowyer-Watson algorithm. + caveshlist = new arraypool(sizeof(face), 8); + caveshbdlist = new arraypool(sizeof(face), 8); + } + + // Initialize the pool for flips. + flippool = new memorypool(sizeof(badface), 1024, POINTER, 0); + + // Initialize the arraypools for Bowyer-Watson algorithm. + cavetetlist = new arraypool(sizeof(triface), 10); + cavebdrylist = new arraypool(sizeof(triface), 10); + caveoldtetlist = new arraypool(sizeof(triface), 10); +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// tetrahedrondealloc() Deallocate space for a tet, marking it dead. // +// // +// Set the first vertex of 'dyingtet' to NULL. So we can detect dead tets // +// when when traversing the list of all tetrahedra. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::tetrahedrondealloc(tetrahedron *dyingtet) +{ + // Mark it as a dead tet. + dyingtet[4] = (tetrahedron) NULL; + + if (b->useshelles) { + // Dealloc the space to subfaces/subsegments. + if (dyingtet[8] != NULL) { + tet2segpool->dealloc((shellface *) dyingtet[8]); + } + if (dyingtet[9] != NULL) { + tet2subpool->dealloc((shellface *) dyingtet[9]); + } + } + + // Actually pool->dealloc(); + *((void **) (dyingtet)) = tetrahedronpool->deaditemstack; + tetrahedronpool->deaditemstack = (void *) (dyingtet); + tetrahedronpool->items--; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// tetrahedrontraverse() Traverse the tetrahedra, skipping dead ones. // +// // +/////////////////////////////////////////////////////////////////////////////// + +tetgenmesh::tetrahedron* tetgenmesh::tetrahedrontraverse() +{ + tetrahedron *newtetrahedron; + + // Skip dead and hull tetrahedra. + do { + newtetrahedron = (tetrahedron *) tetrahedronpool->traverse(); + if (newtetrahedron == (tetrahedron *) NULL) { + return (tetrahedron *) NULL; + } + } while ((newtetrahedron[4] == (tetrahedron) NULL) || + ((point) newtetrahedron[7] == dummypoint)); + return newtetrahedron; +} + +tetgenmesh::tetrahedron* tetgenmesh::alltetrahedrontraverse() +{ + tetrahedron *newtetrahedron; + + do { + newtetrahedron = (tetrahedron *) tetrahedronpool->traverse(); + if (newtetrahedron == (tetrahedron *) NULL) { + return (tetrahedron *) NULL; + } + } while (newtetrahedron[4] == (tetrahedron) NULL); // Skip dead ones. + return newtetrahedron; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// shellfacedealloc() Deallocate space for a shellface, marking it dead. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::shellfacedealloc(memorypool *pool, shellface *dyingsh) +{ + dyingsh[3] = (shellface) NULL; + pool->dealloc((void *) dyingsh); +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// shellfacetraverse() Traverse the subfaces, skipping dead ones. // +// // +/////////////////////////////////////////////////////////////////////////////// + +tetgenmesh::shellface* tetgenmesh::shellfacetraverse(memorypool *pool) +{ + shellface *newshellface; + + do { + newshellface = (shellface *) pool->traverse(); + if (newshellface == (shellface *) NULL) { + return (shellface *) NULL; + } + } while (newshellface[3] == (shellface) NULL); // Skip dead ones. + return newshellface; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// badfacedealloc() Deallocate space for a badface, marking it dead. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::badfacedealloc(memorypool *pool, badface *dying) +{ + dying->forg = (point) NULL; + pool->dealloc((void *) dying); +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// badfacetraverse() Traverse the pools, skipping dead ones. // +// // +/////////////////////////////////////////////////////////////////////////////// + +tetgenmesh::badface* tetgenmesh::badfacetraverse(memorypool *pool) +{ + badface *newsh; + + do { + newsh = (badface *) pool->traverse(); + if (newsh == (badface *) NULL) { + return (badface *) NULL; + } + } while (newsh->forg == (point) NULL); // Skip dead ones. + return newsh; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// pointdealloc() Deallocate space for a point, marking it dead. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::pointdealloc(point dyingpoint) +{ + setpointtype(dyingpoint, DEADVERTEX); + pointpool->dealloc((void *) dyingpoint); +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// pointtraverse() Traverse the points, skipping dead ones. // +// // +/////////////////////////////////////////////////////////////////////////////// + +tetgenmesh::point tetgenmesh::pointtraverse() +{ + point newpoint; + + do { + newpoint = (point) pointpool->traverse(); + if (newpoint == (point) NULL) { + return (point) NULL; + } + } while (getpointtype(newpoint) == DEADVERTEX); // Skip dead ones. + return newpoint; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// maketetrahedron() Create a new tetrahedron. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::maketetrahedron(triface *newtet) +{ + int i; + + newtet->tet = (tetrahedron *) tetrahedronpool->alloc(); + for (i = 0; i < 8; i++) { + newtet->tet[i] = (tetrahedron) NULL; + } + if (b->useshelles) { + newtet->tet[8] = (tetrahedron) NULL; + newtet->tet[9] = (tetrahedron) NULL; + } + for (i = 0; i < in->numberoftetrahedronattributes; i++) { + setelemattribute(newtet->tet, i, 0); + } + if (b->varvolume) { + setvolumebound(newtet->tet, -1.0); + } + elemmarker(newtet->tet) = 0; + newtet->loc = 0; + newtet->ver = 0; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// makeshellface() Create a new shellface and initialize its data fields. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::makeshellface(memorypool* pool, face* newsh) +{ + int i; + + newsh->sh = (shellface *) pool->alloc(); + for (i = 0; i < 10; i++) { + newsh->sh[i] = (shellface) NULL; + } + if (checkconstraints) { + areabound(*newsh) = 0.0; + } + // setshelltype(*newsh, NSHARP); + // if (checkpbcs) { + // setshellpbcgroup(*newsh, -1); + // } + ((int *) (newsh->sh))[shmarkindex] = 0; + // Initialize the version to be Zero. + newsh->shver = 0; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// makepoint() Create a new point. // +// // +// The new point is indexed (starting from 'in->firstnumber'). It's type is // +// initialized as UNUSEDVERTEX. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::makepoint(point* pnewpt) +{ + int i; + + *pnewpt = (point) pointpool->alloc(); + // Initialize the list of coordinates and user-defined attributes. + for (i = 0; i < 4 + in->numberofpointattributes; i++) { + (*pnewpt)[i] = 0.0; + } + // Initialize the point marker (starting from in->firstnumber). + pointmark(*pnewpt) = (int) pointpool->items - (in->firstnumber ? 0 : 1); + point2tet(*pnewpt) = NULL; + setpointtype(*pnewpt, UNUSEDVERTEX); +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// makeindex2pointmap() Make a map from indices to points. // +// // +// The first index of the point is 'in->firstnumber' (0 or 1). '*pidx2ptmap' // +// return this map. NOTE: it is allocated but not deleted in this function. // +// The caller has to deleted it. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::makeindex2pointmap(point*& idx2ptmap) +{ + point pt; + int idx; + + if (b->verbose> 1) { + printf(" Making a map from indices to points.\n"); + } + + idx2ptmap = new point[pointpool->items + 1]; + pointpool->traversalinit(); + idx = in->firstnumber; + pt = pointtraverse(); + while (pt != NULL) { + idx2ptmap[idx++] = pt; + pt = pointtraverse(); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// makesubfacemap() Create a map from vertex to subfaces incident at it. // +// // +// The map is returned in two arrays 'idx2faclist' and 'facperverlist'. All // +// subfaces incident at i-th vertex (i is counted from 0) are found in the // +// array facperverlist[j], where idx2faclist[i] <= j < idx2faclist[i + 1]. // +// Each entry in facperverlist[j] is a subface whose origin is the vertex. // +// // +// NOTE: These two arrays will be created inside this routine, don't forget // +// to free them after using. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::makepoint2submap(memorypool* pool, int*& idx2faclist, + face*& facperverlist) +{ + face shloop; + int i, j, k; + + if (b->verbose > 1) { + printf(" Making a map from points to subfaces.\n"); + } + + // Initialize 'idx2faclist'. + idx2faclist = new int[pointpool->items + 1]; + for (i = 0; i < pointpool->items + 1; i++) idx2faclist[i] = 0; + + // Loop all subfaces, counter the number of subfaces incident at a vertex. + pool->traversalinit(); + shloop.sh = shellfacetraverse(pool); + while (shloop.sh != (shellface *) NULL) { + // Increment the number of incident subfaces for each vertex. + j = pointmark((point) shloop.sh[3]) - in->firstnumber; + idx2faclist[j]++; + j = pointmark((point) shloop.sh[4]) - in->firstnumber; + idx2faclist[j]++; + // Skip the third corner if it is a segment. + if (shloop.sh[5] != NULL) { + j = pointmark((point) shloop.sh[5]) - in->firstnumber; + idx2faclist[j]++; + } + shloop.sh = shellfacetraverse(pool); + } + + // Calculate the total length of array 'facperverlist'. + j = idx2faclist[0]; + idx2faclist[0] = 0; // Array starts from 0 element. + for (i = 0; i < pointpool->items; i++) { + k = idx2faclist[i + 1]; + idx2faclist[i + 1] = idx2faclist[i] + j; + j = k; + } + + // The total length is in the last unit of idx2faclist. + facperverlist = new face[idx2faclist[i]]; + + // Loop all subfaces again, remember the subfaces at each vertex. + pool->traversalinit(); + shloop.sh = shellfacetraverse(pool); + while (shloop.sh != (shellface *) NULL) { + j = pointmark((point) shloop.sh[3]) - in->firstnumber; + shloop.shver = 0; // save the origin. + facperverlist[idx2faclist[j]] = shloop; + idx2faclist[j]++; + // Is it a subface or a subsegment? + if (shloop.sh[5] != NULL) { + j = pointmark((point) shloop.sh[4]) - in->firstnumber; + shloop.shver = 2; // save the origin. + facperverlist[idx2faclist[j]] = shloop; + idx2faclist[j]++; + j = pointmark((point) shloop.sh[5]) - in->firstnumber; + shloop.shver = 4; // save the origin. + facperverlist[idx2faclist[j]] = shloop; + idx2faclist[j]++; + } else { + j = pointmark((point) shloop.sh[4]) - in->firstnumber; + shloop.shver = 1; // save the origin. + facperverlist[idx2faclist[j]] = shloop; + idx2faclist[j]++; + } + shloop.sh = shellfacetraverse(pool); + } + + // Contents in 'idx2faclist' are shifted, now shift them back. + for (i = pointpool->items - 1; i >= 0; i--) { + idx2faclist[i + 1] = idx2faclist[i]; + } + idx2faclist[0] = 0; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// makepoint2tetmap() Make a map from points to tetrahedra. // +// // +// Traverses all the tetrahedra, provides each corner of each tetrahedron // +// with a pointer to that tetrahedera. Some pointers will be overwritten by // +// other pointers because each point may be a corner of several tetrahedra, // +// but in the end every point will point to a tetrahedron that contains it. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::makepoint2tetmap() +{ + tetrahedron *tptr; + point *pt; + + if (b->verbose > 1) { + printf(" Making a map from points to tetrahedra.\n"); + } + + tetrahedronpool->traversalinit(); + tptr = tetrahedrontraverse(); + while (tptr != (tetrahedron *) NULL) { + pt = (point *) tptr; + point2tet(pt[4]) = (tetrahedron) tptr; + point2tet(pt[5]) = (tetrahedron) tptr; + point2tet(pt[6]) = (tetrahedron) tptr; + point2tet(pt[7]) = (tetrahedron) tptr; + tptr = tetrahedrontraverse(); + } +} + +#endif // #ifndef memorypoolCXX \ No newline at end of file diff --git a/contrib/TetgenNew/meshio.cxx b/contrib/TetgenNew/meshio.cxx new file mode 100644 index 0000000000..db3b038d6a --- /dev/null +++ b/contrib/TetgenNew/meshio.cxx @@ -0,0 +1,1237 @@ +#ifndef meshioCXX +#define meshioCXX + +#include "tetgen.h" + +/////////////////////////////////////////////////////////////////////////////// +// // +// transfernodes() Transfer points from 'in->pointlist' to 'pointpool'. // +// // +// While transfering the points, the size of the bounding box (xmax, ...., // +// zmin) is caclulated. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::transfernodes() +{ + point pointloop; + REAL x, y, z; + int coordindex; + int attribindex; + int i, j; + + // Read the points. + coordindex = 0; + attribindex = 0; + for (i = 0; i < in->numberofpoints; i++) { + makepoint(&pointloop); + // Read the point coordinates. + x = pointloop[0] = in->pointlist[coordindex++]; + y = pointloop[1] = in->pointlist[coordindex++]; + z = pointloop[2] = in->pointlist[coordindex++]; + // Read the point attributes. + for (j = 0; j < in->numberofpointattributes; j++) { + pointloop[4 + j] = in->pointattributelist[attribindex++]; + } + // Determine the smallest and largests x, y and z coordinates. + if (i == 0) { + xmin = xmax = x; + ymin = ymax = y; + zmin = zmax = z; + } else { + xmin = (x < xmin) ? x : xmin; + xmax = (x > xmax) ? x : xmax; + ymin = (y < ymin) ? y : ymin; + ymax = (y > ymax) ? y : ymax; + zmin = (z < zmin) ? z : zmin; + zmax = (z > zmax) ? z : zmax; + } + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// jettisonnodes() Jettison unused or duplicated vertices. // +// // +// Unused points are those input points which are outside the mesh domain or // +// have no connection (isolated) to the mesh. Duplicated points exist for // +// example if the input PLC is read from a .stl mesh file (marked during the // +// Delaunay tetrahedralization step. This routine remove these points from // +// points list. All existing points are reindexed. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::jettisonnodes() +{ + point pointloop; + bool jetflag; + int oldidx, newidx; + int remcount; + + if (!b->quiet) { + printf("Jettisoning redundants points.\n"); + } + + pointpool->traversalinit(); + pointloop = pointtraverse(); + oldidx = newidx = 0; // in->firstnumber; + remcount = 0; + while (pointloop != (point) NULL) { + jetflag = (getpointtype(pointloop) == DUPLICATEDVERTEX) || + (getpointtype(pointloop) == UNUSEDVERTEX); + jetflag = (getpointtype(pointloop) == DUPLICATEDVERTEX); + if (jetflag) { + // It is a duplicated point, delete it. + pointdealloc(pointloop); + remcount++; + } else { + // Re-index it. + pointmark(pointloop) = newidx + in->firstnumber; + if (in->pointmarkerlist != (int *) NULL) { + if (oldidx < in->numberofpoints) { + // Re-index the point marker as well. + in->pointmarkerlist[newidx] = in->pointmarkerlist[oldidx]; + } + } + newidx++; + } + oldidx++; + if (oldidx == in->numberofpoints) { + // Update the numbe of input points (Because some were removed). + in->numberofpoints -= remcount; + // Remember this number for output original input nodes. + // jettisoninverts = remcount; + } + pointloop = pointtraverse(); + } + if (b->verbose) { + printf(" %d duplicated vertices have been removed.\n", dupverts); + // printf(" %d unused vertices have been removed.\n", unuverts); + } + dupverts = 0; + // unuverts = 0; + + // The following line ensures that dead items in the pool of nodes cannot + // be allocated for the new created nodes. This ensures that the input + // nodes will occur earlier in the output files, and have lower indices. + pointpool->deaditemstack = (void *) NULL; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// numberedges() Count the number of mesh edges (in 'meshedges'). // +// // +// The edges will be automatically counted in routine 'outelements()'. This // +// routine is needed only -E option is used. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::numberedges() +{ + triface worktet, spintet; + int i; + + meshedges = 0l; + tetrahedronpool->traversalinit(); + worktet.tet = tetrahedrontraverse(); + while (worktet.tet != NULL) { + // Count the number of Voronoi faces. Look at the six edges of this + // tet. Count an edge only if this tet's pointer is smaller than + // those of other non-hull tets which share this edge. + for (i = 0; i < 6; i++) { + worktet.loc = edge2locver[i][0]; + worktet.ver = edge2locver[i][1]; + fnext(worktet, spintet); + do { + if ((point) spintet.tet[7] != dummypoint) { + if (spintet.tet < worktet.tet) break; + } + fnextself(spintet); + } while (spintet.tet != worktet.tet); + // Count this edge if no adjacent tets are smaller than this tet. + if (spintet.tet == worktet.tet) { + meshedges++; + } + } + worktet.tet = tetrahedrontraverse(); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// numbersubedges() Count the number of boundary mesh edges (in // +// 'meshsubedges'). // +// // +// The number of boundary edges will be automatically counted in routine // +// 'outsubfaces()'. This routine is needed only -F option is used. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::numbersubedges() +{ + face faceloop, spinsh; + int i; + + shellface sptr; + + meshsubedges = 0l; + subfacepool->traversalinit(); + faceloop.sh = shellfacetraverse(subfacepool); + while (faceloop.sh != NULL) { + // Count the number of boundary edges. Look at all subfaces sharing at + // this edge. Count it only if this subface's pointer is the smallest. + faceloop.shver = 0; + for (i = 0; i < 3; i++) { + spivot(faceloop, spinsh); + if (spinsh.sh != NULL) { + while (spinsh.sh != faceloop.sh) { + if ((unsigned long) spinsh.sh < (unsigned long) faceloop.sh) break; + spivotself(spinsh); + } + if (spinsh.sh == faceloop.sh) { + meshsubedges++; + } + } else { + meshsubedges++; + } + senextself(faceloop); + } + faceloop.sh = shellfacetraverse(subfacepool); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// outnodes() Output the points to a .node file or a tetgenio structure. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::outnodes(tetgenio* out) +{ + FILE *outfile; + char outnodefilename[FILENAMESIZE]; + point pointloop; + int nextras, bmark, marker; + int coordindex, attribindex; + int pointnumber, firstindex; + int index, i; + + if (out == (tetgenio *) NULL) { + strcpy(outnodefilename, b->outfilename); + strcat(outnodefilename, ".node"); + } + + if (!b->quiet) { + if (out == (tetgenio *) NULL) { + printf("Writing %s.\n", outnodefilename); + } else { + printf("Writing nodes.\n"); + } + } + + nextras = in->numberofpointattributes; + bmark = !b->nobound && in->pointmarkerlist; + + if (out == (tetgenio *) NULL) { + outfile = fopen(outnodefilename, "w"); + if (outfile == (FILE *) NULL) { + printf("File I/O Error: Cannot create file %s.\n", outnodefilename); + terminatetetgen(1); + } + // Number of pointpool, number of dimensions, number of point attributes, + // and number of boundary markers (zero or one). + fprintf(outfile, "%ld %d %d %d\n", pointpool->items, 3, nextras, bmark); + } else { + // Allocate space for 'pointlist'; + out->pointlist = new REAL[pointpool->items * 3]; + if (out->pointlist == (REAL *) NULL) { + printf("Error: Out of memory.\n"); + terminatetetgen(1); + } + // Allocate space for 'pointattributelist' if necessary; + if (nextras > 0) { + out->pointattributelist = new REAL[pointpool->items * nextras]; + if (out->pointattributelist == (REAL *) NULL) { + printf("Error: Out of memory.\n"); + terminatetetgen(1); + } + } + // Allocate space for 'pointmarkerlist' if necessary; + if (bmark) { + out->pointmarkerlist = new int[pointpool->items]; + if (out->pointmarkerlist == (int *) NULL) { + printf("Error: Out of memory.\n"); + terminatetetgen(1); + } + } + out->numberofpoints = pointpool->items; + out->numberofpointattributes = nextras; + coordindex = 0; + attribindex = 0; + } + + // Determine the first index (0 or 1). + firstindex = b->zeroindex ? 0 : in->firstnumber; + + pointpool->traversalinit(); + pointloop = pointtraverse(); + pointnumber = firstindex; // in->firstnumber; + index = 0; + while (pointloop != (point) NULL) { + if (bmark) { + // Default the vertex has a zero marker. + marker = 0; + // Is it an input vertex? + if (index < in->numberofpoints) { + // Input point's marker is directly copied to output. + marker = in->pointmarkerlist[index]; + } + } + if (out == (tetgenio *) NULL) { + // Point number, x, y and z coordinates. + fprintf(outfile, "%4d %.17g %.17g %.17g", pointnumber, + pointloop[0], pointloop[1], pointloop[2]); + for (i = 0; i < nextras; i++) { + // Write an attribute. + fprintf(outfile, " %.17g", pointloop[4 + i]); + } + if (bmark) { + // Write the boundary marker. + fprintf(outfile, " %d", marker); + } + fprintf(outfile, "\n"); + } else { + // X, y, and z coordinates. + out->pointlist[coordindex++] = pointloop[0]; + out->pointlist[coordindex++] = pointloop[1]; + out->pointlist[coordindex++] = pointloop[2]; + // Point attributes. + for (i = 0; i < nextras; i++) { + // Output an attribute. + out->pointattributelist[attribindex++] = pointloop[4 + i]; + } + if (bmark) { + // Output the boundary marker. + out->pointmarkerlist[index] = marker; + } + } + pointloop = pointtraverse(); + pointnumber++; + index++; + } + + if (out == (tetgenio *) NULL) { + fprintf(outfile, "# Generated by %s\n", b->commandline); + fclose(outfile); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// outelements() Output tetrahedra to an .ele file or a tetgenio object. // +// // +// The total number of mesh edges 'meshedges' (the number of Voronoi faces) // +// are counted in this function. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::outelements(tetgenio* out) +{ + FILE *outfile; + char outelefilename[FILENAMESIZE]; + tetrahedron* tptr; + triface worktet, spintet; + point p1, p2, p3, p4; + point *extralist; + REAL *talist; + int *tlist; + long ntets; + int firstindex, shift; + int pointindex, attribindex; + int elementnumber; + int eextras; + int i; + + if (out == (tetgenio *) NULL) { + strcpy(outelefilename, b->outfilename); + strcat(outelefilename, ".ele"); + } + + if (!b->quiet) { + if (out == (tetgenio *) NULL) { + printf("Writing %s.\n", outelefilename); + } else { + printf("Writing elements.\n"); + } + } + + // The number of tets excluding hull tets. + ntets = tetrahedronpool->items - hullsize; + + eextras = in->numberoftetrahedronattributes; + if (out == (tetgenio *) NULL) { + outfile = fopen(outelefilename, "w"); + if (outfile == (FILE *) NULL) { + printf("File I/O Error: Cannot create file %s.\n", outelefilename); + terminatetetgen(1); + } + // Number of tetras, points per tetra, attributes per tetra. + fprintf(outfile, "%ld %d %d\n", ntets, b->order == 1 ? 4 : 10, eextras); + } else { + // Allocate memory for output tetrahedra. + out->tetrahedronlist = new int[ntets * (b->order == 1 ? 4 : 10)]; + if (out->tetrahedronlist == (int *) NULL) { + printf("Error: Out of memory.\n"); + terminatetetgen(1); + } + // Allocate memory for output tetrahedron attributes if necessary. + if (eextras > 0) { + out->tetrahedronattributelist = new REAL[ntets * eextras]; + if (out->tetrahedronattributelist == (REAL *) NULL) { + printf("Error: Out of memory.\n"); + terminatetetgen(1); + } + } + out->numberoftetrahedra = ntets; + out->numberofcorners = b->order == 1 ? 4 : 10; + out->numberoftetrahedronattributes = eextras; + tlist = out->tetrahedronlist; + talist = out->tetrahedronattributelist; + pointindex = 0; + attribindex = 0; + } + + // Determine the first index (0 or 1). + firstindex = b->zeroindex ? 0 : in->firstnumber; + shift = 0; // Default no shiftment. + if ((in->firstnumber == 1) && (firstindex == 0)) { + shift = 1; // Shift the output indices by 1. + } + + // Count the total edge numbers. + meshedges = 0l; + + tetrahedronpool->traversalinit(); + tptr = tetrahedrontraverse(); + elementnumber = firstindex; // in->firstnumber; + while (tptr != (tetrahedron *) NULL) { + // Reverse the orientation so that Orient3D() > 0. + p1 = (point) tptr[5]; + p2 = (point) tptr[4]; + p3 = (point) tptr[6]; + p4 = (point) tptr[7]; + if (out == (tetgenio *) NULL) { + // Tetrahedron number, indices for four points. + fprintf(outfile, "%5d %5d %5d %5d %5d", elementnumber, + pointmark(p1) - shift, pointmark(p2) - shift, + pointmark(p3) - shift, pointmark(p4) - shift); + if (b->order == 2) { + extralist = (point *) tptr[highorderindex]; + // Tetrahedron number, indices for four points plus six extra points. + fprintf(outfile, " %5d %5d %5d %5d %5d %5d", + pointmark(extralist[0]) - shift, pointmark(extralist[1]) - shift, + pointmark(extralist[2]) - shift, pointmark(extralist[3]) - shift, + pointmark(extralist[4]) - shift, pointmark(extralist[5]) - shift); + } + for (i = 0; i < eextras; i++) { + fprintf(outfile, " %.17g", elemattribute(tptr, i)); + } + fprintf(outfile, "\n"); + } else { + tlist[pointindex++] = pointmark(p1) - shift; + tlist[pointindex++] = pointmark(p2) - shift; + tlist[pointindex++] = pointmark(p3) - shift; + tlist[pointindex++] = pointmark(p4) - shift; + if (b->order == 2) { + extralist = (point *) tptr[highorderindex]; + tlist[pointindex++] = pointmark(extralist[0]) - shift; + tlist[pointindex++] = pointmark(extralist[1]) - shift; + tlist[pointindex++] = pointmark(extralist[2]) - shift; + tlist[pointindex++] = pointmark(extralist[3]) - shift; + tlist[pointindex++] = pointmark(extralist[4]) - shift; + tlist[pointindex++] = pointmark(extralist[5]) - shift; + } + for (i = 0; i < eextras; i++) { + talist[attribindex++] = elemattribute(tptr, i); + } + } + if (b->neighout) { + // Remember the index of this element. + * (int *) (tptr + elemmarkerindex) = elementnumber; + } + // Count the number of Voronoi faces. Look at the six edges of this + // tet. Count an edge only if this tet's pointer is smaller than + // those of other non-hull tets which share this edge. + worktet.tet = tptr; + for (i = 0; i < 6; i++) { + worktet.loc = edge2locver[i][0]; + worktet.ver = edge2locver[i][1]; + fnext(worktet, spintet); + do { + if ((point) spintet.tet[7] != dummypoint) { + if (spintet.tet < worktet.tet) break; + } + fnextself(spintet); + } while (spintet.tet != worktet.tet); + // Count this edge if no adjacent tets are smaller than this tet. + if (spintet.tet == worktet.tet) { + meshedges++; + } + } + tptr = tetrahedrontraverse(); + elementnumber++; + } + + if (out == (tetgenio *) NULL) { + fprintf(outfile, "# Generated by %s\n", b->commandline); + fclose(outfile); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// outfaces() Output all faces to a .face file or a tetgenio object. // +// // +// The total number of faces f can be calculated as following: Let t be the // +// total number of tets. Since each tet has 4 faces, the number t * 4 counts // +// each interior face twice and each hull face once. So f = (t * 4 + h) / 2, // +// where h is the total number of hull faces (which is known). // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::outfaces(tetgenio* out) +{ + FILE *outfile; + char facefilename[FILENAMESIZE]; + triface tface, tsymface; + face checkmark; + point torg, tdest, tapex; + long ntets, faces; + int *elist, *emlist; + int neigh1, neigh2; + int bmark, faceid, marker; + int firstindex, shift; + int facenumber; + int index; + + if (out == (tetgenio *) NULL) { + strcpy(facefilename, b->outfilename); + strcat(facefilename, ".face"); + } + + if (!b->quiet) { + if (out == (tetgenio *) NULL) { + printf("Writing %s.\n", facefilename); + } else { + printf("Writing faces.\n"); + } + } + + ntets = tetrahedronpool->items - hullsize; + faces = (ntets * 4l + hullsize) / 2l; + bmark = !b->nobound && in->facetmarkerlist; + + if (out == (tetgenio *) NULL) { + outfile = fopen(facefilename, "w"); + if (outfile == (FILE *) NULL) { + printf("File I/O Error: Cannot create file %s.\n", facefilename); + terminatetetgen(1); + } + fprintf(outfile, "%ld %d\n", faces, bmark); + } else { + // Allocate memory for 'trifacelist'. + out->trifacelist = new int[faces * 3]; + if (out->trifacelist == (int *) NULL) { + printf("Error: Out of memory.\n"); + terminatetetgen(1); + } + // Allocate memory for 'trifacemarkerlist' if necessary. + if (bmark) { + out->trifacemarkerlist = new int[faces]; + if (out->trifacemarkerlist == (int *) NULL) { + printf("Error: Out of memory.\n"); + terminatetetgen(1); + } + } + if (b->neighout > 1) { + // '-nn' switch. + out->adjtetlist = new int[faces * 2]; + if (out->adjtetlist == (int *) NULL) { + printf("Error: Out of memory.\n"); + terminatetetgen(1); + } + } + out->numberoftrifaces = faces; + elist = out->trifacelist; + emlist = out->trifacemarkerlist; + index = 0; + } + + // Determine the first index (0 or 1). + firstindex = b->zeroindex ? 0 : in->firstnumber; + shift = 0; // Default no shiftment. + if ((in->firstnumber == 1) && (firstindex == 0)) { + shift = 1; // Shift the output indices by 1. + } + + tetrahedronpool->traversalinit(); + tface.tet = tetrahedrontraverse(); + facenumber = firstindex; // in->firstnumber; + // To loop over the set of faces, loop over all tetrahedra, and look at + // the four faces of each one. If its adjacent tet is a hull tet, + // operate on the face, otherwise, operate on the face only if the + // current tet has a smaller pointer than its neighbor. + while (tface.tet != (tetrahedron *) NULL) { + for (tface.loc = 0; tface.loc < 4; tface.loc ++) { + sym(tface, tsymface); + if (((point) tsymface.tet[7] == dummypoint) || + (tface.tet < tsymface.tet)) { + torg = org(tface); + tdest = dest(tface); + tapex = apex(tface); + if (bmark) { + // Get the boundary marker of this face. If it is an inner face, + // it has no boundary marker, set it be zero. + if (b->useshelles) { + // Shell face is used. + // tspivot(tface, checkmark); + if (checkmark.sh == NULL) { + marker = 0; // It is an inner face. + } else { + faceid = getshellmark(checkmark) - 1; + marker = in->facetmarkerlist[faceid]; + } + } else { + // Shell face is not used, only distinguish outer and inner face. + marker = tsymface.tet != NULL ? 1 : 0; + } + } + if (b->neighout > 1) { + // '-nn' switch. Output adjacent tets indices. + neigh1 = * (int *)(tface.tet + elemmarkerindex); + if (tsymface.tet != NULL) { + neigh2 = * (int *)(tsymface.tet + elemmarkerindex); + } else { + neigh2 = -1; + } + } + if (out == (tetgenio *) NULL) { + // Face number, indices of three vertices. + fprintf(outfile, "%5d %4d %4d %4d", facenumber, + pointmark(torg) - shift, pointmark(tdest) - shift, + pointmark(tapex) - shift); + if (bmark) { + // Output a boundary marker. + fprintf(outfile, " %d", marker); + } + if (b->neighout > 1) { + fprintf(outfile, " %5d %5d", neigh1, neigh2); + } + fprintf(outfile, "\n"); + } else { + // Output indices of three vertices. + elist[index++] = pointmark(torg) - shift; + elist[index++] = pointmark(tdest) - shift; + elist[index++] = pointmark(tapex) - shift; + if (bmark) { + emlist[facenumber - in->firstnumber] = marker; + } + if (b->neighout > 1) { + out->adjtetlist[(facenumber - in->firstnumber) * 2] = neigh1; + out->adjtetlist[(facenumber - in->firstnumber) * 2 + 1] = neigh2; + } + } + facenumber++; + } + } + tface.tet = tetrahedrontraverse(); + } + + if (out == (tetgenio *) NULL) { + fprintf(outfile, "# Generated by %s\n", b->commandline); + fclose(outfile); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// outhullfaces() Output hull faces to a .face file or a tetgenio object. // +// // +// The normal of each face is pointing to the outside of the domain. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::outhullfaces(tetgenio* out) +{ + FILE *outfile; + char facefilename[FILENAMESIZE]; + triface hulltet; + point torg, tdest, tapex; + int *elist; + int firstindex, shift; + int facenumber; + int index; + + if (out == (tetgenio *) NULL) { + strcpy(facefilename, b->outfilename); + strcat(facefilename, ".face"); + } + + if (!b->quiet) { + if (out == (tetgenio *) NULL) { + printf("Writing %s.\n", facefilename); + } else { + printf("Writing faces.\n"); + } + } + + if (out == (tetgenio *) NULL) { + outfile = fopen(facefilename, "w"); + if (outfile == (FILE *) NULL) { + printf("File I/O Error: Cannot create file %s.\n", facefilename); + terminatetetgen(1); + } + fprintf(outfile, "%ld 0\n", hullsize); + } else { + // Allocate memory for 'trifacelist'. + out->trifacelist = new int[hullsize * 3]; + if (out->trifacelist == (int *) NULL) { + printf("Error: Out of memory.\n"); + terminatetetgen(1); + } + out->numberoftrifaces = hullsize; + elist = out->trifacelist; + index = 0; + } + + // Determine the first index (0 or 1). + firstindex = b->zeroindex ? 0 : in->firstnumber; + shift = 0; // Default no shiftment. + if ((in->firstnumber == 1) && (firstindex == 0)) { + shift = 1; // Shift the output indices by 1. + } + + tetrahedronpool->traversalinit(); + hulltet.tet = alltetrahedrontraverse(); + facenumber = firstindex; + while (hulltet.tet != (tetrahedron *) NULL) { + if ((point) hulltet.tet[7] == dummypoint) { + torg = (point) hulltet.tet[4]; + tdest = (point) hulltet.tet[5]; + tapex = (point) hulltet.tet[6]; + if (out == (tetgenio *) NULL) { + // Face number, indices of three vertices. + fprintf(outfile, "%5d %4d %4d %4d", facenumber, + pointmark(torg) - shift, pointmark(tdest) - shift, + pointmark(tapex) - shift); + fprintf(outfile, "\n"); + } else { + // Output indices of three vertices. + elist[index++] = pointmark(torg) - shift; + elist[index++] = pointmark(tdest) - shift; + elist[index++] = pointmark(tapex) - shift; + } + facenumber++; + } + hulltet.tet = alltetrahedrontraverse(); + } + + if (out == (tetgenio *) NULL) { + fprintf(outfile, "# Generated by %s\n", b->commandline); + fclose(outfile); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// outsubfaces() Output subfaces to a .face file or a tetgenio object. // +// // +// The number of mesh boundary edges ('meshsubedges') will be counted. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::outsubfaces(tetgenio* out) +{ + FILE *outfile; + char facefilename[FILENAMESIZE]; + int *elist; + int *emlist; + int index, index1 = 0, index2 = 0; + triface abuttingtet; + face faceloop, spinsh; + point torg, tdest, tapex; + int bmark, faceid, marker; + int firstindex, shift; + int neigh1, neigh2; + int facenumber, i; + + shellface sptr; + + if (out == (tetgenio *) NULL) { + strcpy(facefilename, b->outfilename); + strcat(facefilename, ".face"); + } + + if (!b->quiet) { + if (out == (tetgenio *) NULL) { + printf("Writing %s.\n", facefilename); + } else { + printf("Writing faces.\n"); + } + } + + bmark = !b->nobound && in->facetmarkerlist; + + if (out == (tetgenio *) NULL) { + outfile = fopen(facefilename, "w"); + if (outfile == (FILE *) NULL) { + printf("File I/O Error: Cannot create file %s.\n", facefilename); + terminatetetgen(1); + } + // Number of subfaces. + fprintf(outfile, "%ld %d\n", subfacepool->items, bmark); + } else { + // Allocate memory for 'trifacelist'. + out->trifacelist = new int[subfacepool->items * 3]; + if (out->trifacelist == (int *) NULL) { + printf("Error: Out of memory.\n"); + terminatetetgen(1); + } + if (bmark) { + // Allocate memory for 'trifacemarkerlist'. + out->trifacemarkerlist = new int[subfacepool->items]; + if (out->trifacemarkerlist == (int *) NULL) { + printf("Error: Out of memory.\n"); + terminatetetgen(1); + } + } + if (b->neighout > 1) { + // '-nn' switch. + out->adjtetlist = new int[subfacepool->items * 2]; + if (out->adjtetlist == (int *) NULL) { + printf("Error: Out of memory.\n"); + terminatetetgen(1); + } + } + out->numberoftrifaces = subfacepool->items; + elist = out->trifacelist; + emlist = out->trifacemarkerlist; + } + + // Determine the first index (0 or 1). + firstindex = b->zeroindex ? 0 : in->firstnumber; + shift = 0; // Default no shiftment. + if ((in->firstnumber == 1) && (firstindex == 0)) { + shift = 1; // Shift the output indices by 1. + } + + meshsubedges = 0l; + + subfacepool->traversalinit(); + faceloop.sh = shellfacetraverse(subfacepool); + facenumber = firstindex; // in->firstnumber; + while (faceloop.sh != (shellface *) NULL) { + abuttingtet.tet = NULL; // stpivot(faceloop, abuttingtet); + if (abuttingtet.tet != NULL) { + // There is a tetrahedron containing this subface, orient it. + abuttingtet.ver = 0; + torg = org(abuttingtet); + tdest = dest(abuttingtet); + tapex = apex(abuttingtet); + } else { + // This may happen when only a surface mesh be generated. + torg = sorg(faceloop); + tdest = sdest(faceloop); + tapex = sapex(faceloop); + } + if (bmark) { + faceid = getshellmark(faceloop) - 1; + marker = in->facetmarkerlist[faceid]; + } + if (b->neighout > 1) { + // '-nn' switch. Output adjacent tets indices. + neigh1 = -1; + // stpivot(faceloop, abuttingtet); + if (abuttingtet.tet != NULL) { + neigh1 = * (int *)(abuttingtet.tet + elemmarkerindex); + } + neigh2 = -1; + sesymself(faceloop); + // stpivot(faceloop, abuttingtet); + if (abuttingtet.tet != NULL) { + neigh2 = * (int *)(abuttingtet.tet + elemmarkerindex); + } + } + if (out == (tetgenio *) NULL) { + fprintf(outfile, "%5d %4d %4d %4d", facenumber, + pointmark(torg) - shift, pointmark(tdest) - shift, + pointmark(tapex) - shift); + if (bmark) { + fprintf(outfile, " %d", marker); + } + if (b->neighout > 1) { + fprintf(outfile, " %5d %5d", neigh1, neigh2); + } + fprintf(outfile, "\n"); + } else { + // Output three vertices of this face; + elist[index++] = pointmark(torg) - shift; + elist[index++] = pointmark(tdest) - shift; + elist[index++] = pointmark(tapex) - shift; + if (bmark) { + emlist[index1++] = marker; + } + if (b->neighout > 1) { + out->adjtetlist[index2++] = neigh1; + out->adjtetlist[index2++] = neigh2; + } + } + // Count the number of boundary edges. Look at all subfaces sharing at + // this edge. Count it only if this subface's pointer is the smallest. + faceloop.shver = 0; + for (i = 0; i < 3; i++) { + spivot(faceloop, spinsh); + if (spinsh.sh != NULL) { + while (spinsh.sh != faceloop.sh) { + if ((unsigned long) spinsh.sh < (unsigned long) faceloop.sh) break; + spivotself(spinsh); + } + if (spinsh.sh == faceloop.sh) { + meshsubedges++; + } + } else { + meshsubedges++; + } + senextself(faceloop); + } + facenumber++; + faceloop.sh = shellfacetraverse(subfacepool); + } + + if (out == (tetgenio *) NULL) { + fprintf(outfile, "# Generated by %s\n", b->commandline); + fclose(outfile); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// outedges() Output all edges to a .edge file or a tetgenio object. // +// // +// Note: This routine must be called after outelements(), so that the total // +// number of edges 'meshedges' has been counted. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::outedges(tetgenio* out) +{ + FILE *outfile; + char edgefilename[FILENAMESIZE]; + triface tetloop, worktet, spintet; + point torg, tdest; + int *elist, *emlist; + int firstindex, shift; + int edgenumber; + int index; + int i; + + if (out == (tetgenio *) NULL) { + strcpy(edgefilename, b->outfilename); + strcat(edgefilename, ".edge"); + } + + if (!b->quiet) { + if (out == (tetgenio *) NULL) { + printf("Writing %s.\n", edgefilename); + } else { + printf("Writing edges.\n"); + } + } + + if (out == (tetgenio *) NULL) { + outfile = fopen(edgefilename, "w"); + if (outfile == (FILE *) NULL) { + printf("File I/O Error: Cannot create file %s.\n", edgefilename); + terminatetetgen(1); + } + // Write the number of edges, boundary markers (0 or 1). + fprintf(outfile, "%ld %d\n", meshedges, !b->nobound); + } else { + // Allocate memory for 'edgelist'. + out->edgelist = new int[meshedges * 2]; + if (out->edgelist == (int *) NULL) { + printf("Error: Out of memory.\n"); + terminatetetgen(1); + } + if (!b->nobound) { + out->edgemarkerlist = new int[meshedges]; + } + out->numberofedges = meshedges; + elist = out->edgelist; + emlist = out->edgemarkerlist; + index = 0; + } + + // Determine the first index (0 or 1). + firstindex = b->zeroindex ? 0 : in->firstnumber; + shift = 0; // Default no shiftment. + if ((in->firstnumber == 1) && (firstindex == 0)) { + shift = 1; // Shift (reduce) the output indices by 1. + } + + tetrahedronpool->traversalinit(); + tetloop.tet = tetrahedrontraverse(); + edgenumber = firstindex; // in->firstnumber; + while (tetloop.tet != (tetrahedron *) NULL) { + // Count the number of Voronoi faces. Look at the six edges of this + // tet. Count an edge only if this tet's pointer is smaller than + // those of other non-hull tets which share this edge. + worktet.tet = tetloop.tet; + for (i = 0; i < 6; i++) { + worktet.loc = edge2locver[i][0]; + worktet.ver = edge2locver[i][1]; + fnext(worktet, spintet); + do { + if ((point) spintet.tet[7] != dummypoint) { + if (spintet.tet < worktet.tet) break; + } + fnextself(spintet); + } while (spintet.tet != worktet.tet); + // Count this edge if no adjacent tets are smaller than this tet. + if (spintet.tet == worktet.tet) { + torg = org(worktet); + tdest = dest(worktet); + if (out == (tetgenio *) NULL) { + fprintf(outfile, "%5d %4d %4d", edgenumber, + pointmark(torg) - shift, pointmark(tdest) - shift); + } else { + // Output three vertices of this face; + elist[index++] = pointmark(torg) - shift; + elist[index++] = pointmark(tdest) - shift; + } + /* + if (!b->nobound) { + // Check if the edge is a segment. + tsspivot(&worktet, &checkseg); + if (checkseg.sh != dummysh) { + marker = shellmark(checkseg); + if (marker == 0) { // Does it have no marker? + marker = 1; // Set the default marker for this segment. + } + } else { + marker = 0; // It's not a segment. + } + if (out == (tetgenio *) NULL) { + fprintf(outfile, " %d", marker); + } else { + emlist[index1++] = marker; + } + } + */ + if (out == (tetgenio *) NULL) { + fprintf(outfile, "\n"); + } + edgenumber++; + } + } + tetloop.tet = tetrahedrontraverse(); + } + + if (out == (tetgenio *) NULL) { + fprintf(outfile, "# Generated by %s\n", b->commandline); + fclose(outfile); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// outsubsegments() Output subsegments into an .edge file or an object. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::outsubsegments(tetgenio* out) +{ + FILE *outfile; + char edgefilename[FILENAMESIZE]; + int *elist; + int index; + face edgeloop; + point torg, tdest; + int firstindex, shift; + int edgenumber; + + if (out == (tetgenio *) NULL) { + strcpy(edgefilename, b->outfilename); + strcat(edgefilename, ".edge"); + } + + if (!b->quiet) { + if (out == (tetgenio *) NULL) { + printf("Writing %s.\n", edgefilename); + } else { + printf("Writing edges.\n"); + } + } + + // Avoid compile warnings. + outfile = (FILE *) NULL; + elist = (int *) NULL; + index = 0; + + if (out == (tetgenio *) NULL) { + outfile = fopen(edgefilename, "w"); + if (outfile == (FILE *) NULL) { + printf("File I/O Error: Cannot create file %s.\n", edgefilename); + terminatetetgen(1); + } + // Number of subsegments. + fprintf(outfile, "%ld\n", subsegpool->items); + } else { + // Allocate memory for 'edgelist'. + out->edgelist = new int[subsegpool->items * 2]; + if (out->edgelist == (int *) NULL) { + printf("Error: Out of memory.\n"); + terminatetetgen(1); + } + out->numberofedges = subsegpool->items; + elist = out->edgelist; + } + + // Determine the first index (0 or 1). + firstindex = b->zeroindex ? 0 : in->firstnumber; + shift = 0; // Default no shiftment. + if ((in->firstnumber == 1) && (firstindex == 0)) { + shift = 1; // Shift the output indices by 1. + } + + subsegpool->traversalinit(); + edgeloop.sh = shellfacetraverse(subsegpool); + edgenumber = firstindex; // in->firstnumber; + while (edgeloop.sh != (shellface *) NULL) { + torg = sorg(edgeloop); + tdest = sdest(edgeloop); + if (out == (tetgenio *) NULL) { + fprintf(outfile, "%5d %4d %4d\n", edgenumber, + pointmark(torg) - shift, pointmark(tdest) - shift); + } else { + // Output three vertices of this face; + elist[index++] = pointmark(torg) - shift; + elist[index++] = pointmark(tdest) - shift; + } + edgenumber++; + edgeloop.sh = shellfacetraverse(subsegpool); + } + + if (out == (tetgenio *) NULL) { + fprintf(outfile, "# Generated by %s\n", b->commandline); + fclose(outfile); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// outneighbors() Output tet neighbors to a .neigh file or an object. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::outneighbors(tetgenio* out) +{ + FILE *outfile; + char neighborfilename[FILENAMESIZE]; + int *nlist; + int index; + triface tetloop, tetsym; + int neighbor1, neighbor2, neighbor3, neighbor4; + int firstindex; + int elementnumber; + long ntets; + + if (out == (tetgenio *) NULL) { + strcpy(neighborfilename, b->outfilename); + strcat(neighborfilename, ".neigh"); + } + + if (!b->quiet) { + if (out == (tetgenio *) NULL) { + printf("Writing %s.\n", neighborfilename); + } else { + printf("Writing neighbors.\n"); + } + } + + ntets = tetrahedronpool->items - hullsize; + + if (out == (tetgenio *) NULL) { + outfile = fopen(neighborfilename, "w"); + if (outfile == (FILE *) NULL) { + printf("File I/O Error: Cannot create file %s.\n", neighborfilename); + terminatetetgen(1); + } + // Number of tetrahedra, four faces per tetrahedron. + fprintf(outfile, "%ld %d\n", ntets, 4); + } else { + // Allocate memory for 'neighborlist'. + out->neighborlist = new int[ntets * 4]; + if (out->neighborlist == (int *) NULL) { + printf("Error: Out of memory.\n"); + terminatetetgen(1); + } + nlist = out->neighborlist; + } + + // Determine the first index (0 or 1). + firstindex = b->zeroindex ? 0 : in->firstnumber; + + tetrahedronpool->traversalinit(); + tetloop.tet = tetrahedrontraverse(); + elementnumber = firstindex; // in->firstnumber; + while (tetloop.tet != (tetrahedron *) NULL) { + tetloop.loc = 2; + sym(tetloop, tetsym); + if ((point) tetsym.tet[7] != dummypoint) { + neighbor1 = * (int *) (tetsym.tet + elemmarkerindex); + } else { + neighbor1 = -1; + } + tetloop.loc = 3; + sym(tetloop, tetsym); + if ((point) tetsym.tet[7] != dummypoint) { + neighbor2 = * (int *) (tetsym.tet + elemmarkerindex); + } else { + neighbor1 = -1; + } + tetloop.loc = 1; + sym(tetloop, tetsym); + if ((point) tetsym.tet[7] != dummypoint) { + neighbor3 = * (int *) (tetsym.tet + elemmarkerindex); + } else { + neighbor1 = -1; + } + tetloop.loc = 0; + sym(tetloop, tetsym); + if ((point) tetsym.tet[7] != dummypoint) { + neighbor4 = * (int *) (tetsym.tet + elemmarkerindex); + } else { + neighbor1 = -1; + } + if (out == (tetgenio *) NULL) { + // Tetrahedra number, neighboring tetrahedron numbers. + fprintf(outfile, "%4d %4d %4d %4d %4d\n", elementnumber, + neighbor1, neighbor2, neighbor3, neighbor4); + } else { + nlist[index++] = neighbor1; + nlist[index++] = neighbor2; + nlist[index++] = neighbor3; + nlist[index++] = neighbor4; + } + tetloop.tet = tetrahedrontraverse(); + elementnumber++; + } + + if (out == (tetgenio *) NULL) { + fprintf(outfile, "# Generated by %s\n", b->commandline); + fclose(outfile); + } +} + +#endif // #ifndef meshioCXX diff --git a/contrib/TetgenNew/meshstat.cxx b/contrib/TetgenNew/meshstat.cxx new file mode 100644 index 0000000000..c5c0c992c1 --- /dev/null +++ b/contrib/TetgenNew/meshstat.cxx @@ -0,0 +1,1764 @@ +#ifndef meshstatCXX +#define meshstatCXX + +#include "tetgen.h" + +/////////////////////////////////////////////////////////////////////////////// +// // +// Initialize fast look-up tables. // +// // +/////////////////////////////////////////////////////////////////////////////// + +int tetgenmesh::ve[6] = { 2, 5, 4, 1, 0, 3 }; +int tetgenmesh::ve2[6] = { 4, 3, 0, 5, 2, 1 }; + +int tetgenmesh::vo[6] = { 0, 1, 1, 2, 2, 0 }; +int tetgenmesh::vd[6] = { 1, 0, 2, 1, 0, 2 }; +int tetgenmesh::va[6] = { 2, 2, 0, 0, 1, 1 }; + +int tetgenmesh::verver2zero[6][6] = { + {0, 0, 2, 2, 4, 4}, + {0, 0, 2, 2, 4, 4}, + {2, 2, 4, 4, 0, 0}, + {2, 2, 4, 4, 0, 0}, + {4, 4, 0, 0, 2, 2}, + {4, 4, 0, 0, 2, 2} +}; + +int tetgenmesh::ver2zero[6] = {0, 0, 2, 2, 4, 4}; + +int tetgenmesh::zero2ver[6][6] = { + {0, 0, 2, 2, 4, 4}, + {0, 0, 2, 2, 4, 4}, + {4, 4, 0, 0, 2, 2}, + {4, 4, 0, 0, 2, 2}, + {2, 2, 4, 4, 0, 0}, + {2, 2, 4, 4, 0, 0} +}; + +int tetgenmesh::locver2org[4][6] = { + {0, 1, 1, 2, 2, 0}, + {0, 3, 3, 1, 1, 0}, + {1, 3, 3, 2, 2, 1}, + {2, 3, 3, 0, 0, 2} +}; + +int tetgenmesh::locver2dest[4][6] = { + {1, 0, 2, 1, 0, 2}, + {3, 0, 1, 3, 0, 1}, + {3, 1, 2, 3, 1, 2}, + {3, 2, 0, 3, 2, 0} +}; + +int tetgenmesh::locver2apex[4][6] = { + {2, 2, 0, 0, 1, 1}, + {1, 1, 0, 0, 3, 3}, + {2, 2, 1, 1, 3, 3}, + {0, 0, 2, 2, 3, 3} +}; + +int tetgenmesh::loc2oppo[4] = { 3, 2, 0, 1 }; + +int tetgenmesh::locver2nextf[32] = { + 1, 5, 2, 5, 3, 5, 0, 0, + 3, 3, 2, 1, 0, 1, 0, 0, + 1, 3, 3, 1, 0, 3, 0, 0, + 2, 3, 1, 1, 0, 5, 0, 0 +}; + +int tetgenmesh::locver2edge[4][6] = { + {0, 0, 1, 1, 2, 2}, + {3, 3, 4, 4, 0, 0}, + {4, 4, 5, 5, 1, 1}, + {5, 5, 3, 3, 2, 2} +}; + +int tetgenmesh::edge2locver[6][2] = { + {0, 0}, // 0 v0 -> v1 + {0, 2}, // 1 v1 -> v2 + {0, 4}, // 2 v2 -> v0 + {1, 0}, // 3 v0 -> v3 + {1, 2}, // 4 v1 -> v3 + {2, 2} // 5 v2 -> v3 +}; + +int tetgenmesh::locpivot[4][3] = { + {1, 2, 3}, + {0, 2, 3}, + {0, 1, 3}, + {0, 1, 2} +}; + +int tetgenmesh::locverpivot[4][6][2] = { + {{2, 3}, {2, 3}, {1, 3}, {1, 3}, {1, 2}, {1, 2}}, + {{0, 2}, {0, 2}, {0, 3}, {0, 3}, {2, 3}, {2, 3}}, + {{0, 3}, {0, 3}, {0, 1}, {0, 1}, {1, 3}, {1, 3}}, + {{0, 1}, {0, 1}, {0, 2}, {0, 2}, {1, 2}, {1, 2}} +}; + +int tetgenmesh::mi1mo3[3] = {2, 0, 1}; + +/////////////////////////////////////////////////////////////////////////////// +// // +// checkmesh() Test mesh for geometrical and topological consistencies. // +// // +/////////////////////////////////////////////////////////////////////////////// + +int tetgenmesh::checkmesh() +{ + triface tetloop; + triface neightet, symtet; + point pa, pb, pc, pd; + REAL ori; + int horrors, i; + + if (!b->quiet) { + printf(" Checking consistency of mesh...\n"); + } + + horrors = 0; + tetloop.ver = 0; + // Run through the list of tetrahedra, checking each one. + tetrahedronpool->traversalinit(); + tetloop.tet = tetrahedrontraverse(); + while (tetloop.tet != (tetrahedron *) NULL) { + // Check all four faces of the tetrahedron. + for (tetloop.loc = 0; tetloop.loc < 4; tetloop.loc++) { + pa = org(tetloop); + pb = dest(tetloop); + pc = apex(tetloop); + pd = oppo(tetloop); + if (tetloop.loc == 0) { // Only test for inversion once. + if (pd != dummypoint) { // Only do test if it is not a hull tet. + ori = orient3d(pa, pb, pc, pd); + if (ori >= 0.0) { + printf(" !! !! %s ", ori > 0.0 ? "Inverted" : "Degenerated"); + printf(" (%d, %d, %d, %d) (ori = %.17g)\n", pointmark(pa), + pointmark(pb), pointmark(pc), pointmark(pd), ori); + horrors++; + } + } + if (infected(tetloop)) { + // This may be a bug. Report it. + printf(" !! (%d, %d, %d, %d) is infected.\n", pointmark(pa), + pointmark(pb), pointmark(pc), pointmark(pd)); + } + if (marktested(tetloop)) { + // This may be a bug. Report it. + printf(" !! (%d, %d, %d, %d) is marked.\n", pointmark(pa), + pointmark(pb), pointmark(pc), pointmark(pd)); + } + } + if (tetloop.tet[tetloop.loc] == NULL) { + printf(" !! !! No neighbor at face (%d, %d, %d).\n", pointmark(pa), + pointmark(pb), pointmark(pc)); + horrors++; + } else { + // Find the neighboring tetrahedron on this face. + symedge(tetloop, neightet); + // Check that the tetrahedron's neighbor knows it's a neighbor. + sym(neightet, symtet); + if ((tetloop.tet != symtet.tet) || (tetloop.loc != symtet.loc)) { + printf(" !! !! Asymmetric tetra-tetra bond:\n"); + if (tetloop.tet == symtet.tet) { + printf(" (Right tetrahedron, wrong orientation)\n"); + } + printf(" First: (%d, %d, %d, %d)\n", pointmark(pa), + pointmark(pb), pointmark(pc), pointmark(pd)); + printf(" Second: (%d, %d, %d, %d)\n", pointmark(org(neightet)), + pointmark(dest(neightet)), pointmark(apex(neightet)), + pointmark(oppo(neightet))); + horrors++; + } + // Check if they have the same edge (the bond() operation). + if ((org(neightet) != pb) || (dest(neightet) != pa)) { + printf(" !! !! Wrong edge-edge bond:\n"); + printf(" First: (%d, %d, %d, %d)\n", pointmark(pa), + pointmark(pb), pointmark(pc), pointmark(pd)); + printf(" Second: (%d, %d, %d, %d)\n", pointmark(org(neightet)), + pointmark(dest(neightet)), pointmark(apex(neightet)), + pointmark(oppo(neightet))); + horrors++; + } + // Check if they have the same apex. + if (apex(neightet) != pc) { + printf(" !! !! Wrong face-face bond:\n"); + printf(" First: (%d, %d, %d, %d)\n", pointmark(pa), + pointmark(pb), pointmark(pc), pointmark(pd)); + printf(" Second: (%d, %d, %d, %d)\n", pointmark(org(neightet)), + pointmark(dest(neightet)), pointmark(apex(neightet)), + pointmark(oppo(neightet))); + horrors++; + } + // Check if they have the same opposite. + if (oppo(neightet) == pd) { + printf(" !! !! Two identical tetra:\n"); + printf(" First: (%d, %d, %d, %d)\n", pointmark(pa), + pointmark(pb), pointmark(pc), pointmark(pd)); + printf(" Second: (%d, %d, %d, %d)\n", pointmark(org(neightet)), + pointmark(dest(neightet)), pointmark(apex(neightet)), + pointmark(oppo(neightet))); + horrors++; + } + } + if (facemarked(tetloop)) { + // This may be a bug. Report it. + printf(" !! tetface (%d, %d, %d) %d is marked.\n", pointmark(pa), + pointmark(pb), pointmark(pc), pointmark(pd)); + } + } + // Check the six edges of this tet. + for (i = 0; i < 6; i++) { + tetloop.loc = edge2locver[i][0]; + tetloop.ver = edge2locver[i][1]; + if (edgemarked(tetloop)) { + // This may be a bug. Report it. + printf(" !! tetedge (%d, %d) %d, %d is marked.\n", + pointmark(org(tetloop)), pointmark(dest(tetloop)), + pointmark(apex(tetloop)), pointmark(oppo(tetloop))); + } + } + tetloop.tet = tetrahedrontraverse(); + } + if (horrors == 0) { + if (!b->quiet) { + printf(" In my studied opinion, the mesh appears to be consistent.\n"); + } + } else { + printf(" !! !! !! !! %d %s witnessed.\n", horrors, + horrors > 1 ? "abnormity" : "abnormities"); + } + + return horrors; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// checkshells() Test the surface mesh for consistencies. // +// // +// If 'sub2tet' > 0, it also checks the subface-to-tet connections. // +// // +/////////////////////////////////////////////////////////////////////////////// + +int tetgenmesh::checkshells(int sub2tet) +{ + triface neightet, symtet; + face shloop, spinsh, nextsh; + face checkseg; + point pa, pb, *ppt; + int count; + int horrors, i; + + tetrahedron ptr; + + if (!b->quiet) { + printf(" Checking consistency of the mesh boundary...\n"); + } + horrors = 0; + + void **bakpathblock = subfacepool->pathblock; + void *bakpathitem = subfacepool->pathitem; + int bakpathitemsleft = subfacepool->pathitemsleft; + int bakalignbytes = subfacepool->alignbytes; + + subfacepool->traversalinit(); + shloop.sh = shellfacetraverse(subfacepool); + while (shloop.sh != NULL) { + shloop.shver = 0; + for (i = 0; i < 3; i++) { + // Check the face ring at this edge. + pa = sorg(shloop); + pb = sdest(shloop); + spinsh = shloop; + spivot(spinsh, nextsh); + while ((nextsh.sh != NULL) && (nextsh.sh != shloop.sh)) { + // check if they have the same edge. + if (!((sorg(nextsh) == pa) && (sdest(nextsh) == pb) || + (sorg(nextsh) == pb) && (sdest(nextsh) == pa))) { + printf(" !! !! Wrong subface-subface connection.\n"); + printf(" First: x%lx (%d, %d, %d).\n", (unsigned long) spinsh.sh, + pmark(sorg(spinsh)), pmark(sdest(spinsh)), pmark(sapex(spinsh))); + printf(" Scond: x%lx (%d, %d, %d).\n", (unsigned long) nextsh.sh, + pmark(sorg(nextsh)), pmark(sdest(nextsh)), pmark(sapex(nextsh))); + horrors++; + } + spinsh = nextsh; + spivot(spinsh, nextsh); + } + // Check subface-subseg bond. + sspivot(shloop, checkseg); + if (checkseg.sh != NULL) { + if (!((sorg(checkseg) == pa) && (sdest(checkseg) == pb) || + (sorg(checkseg) == pb) && (sdest(checkseg) == pa))) { + printf(" !! !! Wrong subface-subseg connection.\n"); + printf(" Sub: x%lx (%d, %d, %d).\n", (unsigned long) shloop.sh, + pmark(sorg(shloop)), pmark(sdest(shloop)), pmark(sapex(shloop))); + printf(" Seg: x%lx (%d, %d).\n", (unsigned long) checkseg.sh, + pmark(sorg(checkseg)), pmark(sdest(checkseg))); + horrors++; + } + } + senextself(shloop); + } + if (sub2tet > 0) { + // Check the tet-subface connections. + stpivot(shloop, neightet); + if (neightet.tet != NULL) { + // Check if the tet and subface have the same vertices. + ppt = (point *) shloop.sh; + for (i = 0; i < 3; i++) { + pinfect(ppt[3 + i]); // Infect the subface vertices. + } + ppt = (point *) neightet.tet; + count = 0; // Count the infected vertices of this tet. + for (i = 0; i < 4; i++) { + if (pinfected(ppt[4 + i])) count++; + } + ppt = (point *) shloop.sh; + for (i = 0; i < 3; i++) { + puninfect(ppt[3 + i]); // Uninfect the subface vertices. + } + if (count != 3) { + printf(" !! !! Wrong tet-sub connection (face does not match).\n"); + printf(" Sub: x%lx (%d, %d, %d).\n", (unsigned long) shloop.sh, + pmark(sorg(shloop)), pmark(sdest(shloop)), pmark(sapex(shloop))); + printf(" Tet: x%lx (%d, %d, %d, %d).\n", + (unsigned long) neightet.tet, pmark(org(neightet)), + pmark(dest(neightet)), pmark(apex(neightet)), + pmark(oppo(neightet))); + horrors++; + } + tspivot(neightet, spinsh); + if (spinsh.sh != shloop.sh) { + printf(" !! !! Wrong tet-sub connection (wrong subfaces).\n"); + printf(" Sub: x%lx (%d, %d, %d).\n", (unsigned long) shloop.sh, + pmark(sorg(shloop)), pmark(sdest(shloop)), pmark(sapex(shloop))); + printf(" Tet: x%lx (%d, %d, %d, %d).\n", + (unsigned long) neightet.tet, pmark(org(neightet)), + pmark(dest(neightet)), pmark(apex(neightet)), + pmark(oppo(neightet))); + horrors++; + } else { + symself(neightet); + tspivot(neightet, spinsh); + if (spinsh.sh != shloop.sh) { + printf(" !! !! Wrong tet-sub connection (wrong subfaces).\n"); + printf(" Sub: x%lx (%d, %d, %d).\n", (unsigned long) shloop.sh, + pmark(sorg(shloop)), pmark(sdest(shloop)), pmark(sapex(shloop))); + printf(" Tet: x%lx (%d, %d, %d, %d).\n", + (unsigned long) neightet.tet, pmark(org(neightet)), + pmark(dest(neightet)), pmark(apex(neightet)), + pmark(oppo(neightet))); + horrors++; + } + } + } else { + // printf(" !! A dangling subface.\n"); + // printf(" Sub: x%lx (%d, %d, %d).\n", (unsigned long) shloop.sh, + // pmark(sorg(shloop)), pmark(sdest(shloop)), pmark(sapex(shloop))); + // horrors++; + } + } + if (sinfected(shloop)) { + // This may be a bug. report it. + printf(" !! A infected subface: (%d, %d, %d).\n", pmark(sorg(shloop)), + pmark(sdest(shloop)), pmark(sapex(shloop))); + } + if (smarktested(shloop)) { + // This may be a bug. report it. + printf(" !! A marked subface: (%d, %d, %d).\n", pmark(sorg(shloop)), + pmark(sdest(shloop)), pmark(sapex(shloop))); + } + shloop.sh = shellfacetraverse(subfacepool); + } + + if (horrors == 0) { + if (!b->quiet) { + printf(" Mesh boundaries connected correctly.\n"); + } + } else { + printf(" !! !! !! !! %d boundary connection viewed with horror.\n", + horrors); + } + + subfacepool->pathblock = bakpathblock; + subfacepool->pathitem = bakpathitem; + subfacepool->pathitemsleft = bakpathitemsleft; + subfacepool->alignbytes = bakalignbytes; + + return horrors; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// checkdelaunay() Ensure that the mesh is (constrained) Delaunay. // +// // +/////////////////////////////////////////////////////////////////////////////// + +int tetgenmesh::checkdelaunay(int constrained) +{ + triface tetloop; + triface symtet; + face checksh; + point pa, pb, pc, pd, pe; + REAL sign; + int horrors; + + if (!b->quiet) { + printf(" Checking %s property of the mesh...\n", constrained > 0 ? + "constrained Delaunay" : "Delaunay"); + } + + horrors = 0; + tetloop.ver = 0; + // Run through the list of triangles, checking each one. + tetrahedronpool->traversalinit(); + tetloop.tet = tetrahedrontraverse(); + while (tetloop.tet != (tetrahedron *) NULL) { + // Check all four faces of the tetrahedron. + for (tetloop.loc = 0; tetloop.loc < 4; tetloop.loc++) { + sym(tetloop, symtet); + // Only do test if its adjoining tet is not a hull tet or its pointer + // is larger (to ensure that each pair isn't tested twice). + if (((point) symtet.tet[7] != dummypoint)&&(tetloop.tet < symtet.tet)) { + pa = org(tetloop); + pb = dest(tetloop); + pc = apex(tetloop); + pd = oppo(tetloop); + pe = oppo(symtet); + sign = insphere_sos(pa, pb, pc, pd, pe); + if (sign < 0.0) { + if (constrained > 0) { + tspivot(tetloop, checksh); + } + if ((constrained == 0) || + ((constrained > 0) && (checksh.sh == NULL))) { + printf(" !! Non-locally Delaunay (%d, %d, %d) - %d, %d\n", + pointmark(pa), pointmark(pb), pointmark(pc), pointmark(pd), + pointmark(pe)); + horrors++; + } + } + } + } + tetloop.tet = tetrahedrontraverse(); + } + + if (horrors == 0) { + if (!b->quiet) { + printf(" The mesh is %s.\n", constrained > 0 ? "constrained Delaunay" + : "Delaunay"); + } + } else { + printf(" !! !! !! !! Found %d non-Delaunay faces.\n", horrors); + } + + return horrors; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// checksegments() Check the connections between tetrahedra and segments. // +// // +/////////////////////////////////////////////////////////////////////////////// + +int tetgenmesh::checksegments() +{ + triface tetloop, neightet; + shellface *segs; + face sseg, checkseg; + point pa, pb; + int horrors, i; + + if (!b->quiet) { + printf(" Checking tet-seg connections...\n"); + } + + horrors = 0; + tetrahedronpool->traversalinit(); + tetloop.tet = tetrahedrontraverse(); + while (tetloop.tet != NULL) { + // Loop the six edges of the tet. + if (tetloop.tet[8] != NULL) { + segs = (shellface *) tetloop.tet[8]; + for (i = 0; i < 6; i++) { + sdecode(segs[i], sseg); + if (sseg.sh != NULL) { + // Get the edge of the tet. + tetloop.loc = edge2locver[i][0]; + tetloop.ver = edge2locver[i][1]; + // Check if they are the same edge. + pa = (point) sseg.sh[3]; + pb = (point) sseg.sh[4]; + if (!(((org(tetloop) == pa) && (dest(tetloop) == pb)) || + ((org(tetloop) == pb) && (dest(tetloop) == pa)))) { + printf(" !! Wrong tet-seg connection.\n"); + printf(" Tet: x%lx (%d, %d, %d, %d) - Seg: x%lx (%d, %d).\n", + (unsigned long) tetloop.tet, pointmark(org(tetloop)), + pointmark(dest(tetloop)), pointmark(apex(tetloop)), + pointmark(oppo(tetloop)), (unsigned long) sseg.sh, + pointmark(pa), pointmark(pb)); + horrors++; + } else { + // Loop all tets sharing at this edge. + neightet = tetloop; + do { + tsspivot(neightet, checkseg); + if (checkseg.sh != sseg.sh) { + printf(" !! Wrong tet->seg connection.\n"); + printf(" Tet: x%lx (%d, %d, %d, %d) - ", + (unsigned long) neightet.tet, pointmark(org(neightet)), + pointmark(dest(neightet)), pointmark(apex(neightet)), + pointmark(oppo(neightet))); + if (checkseg.sh != NULL) { + printf("Seg x%lx (%d, %d).\n", (unsigned long) checkseg.sh, + pointmark(sorg(checkseg)), pointmark(sdest(checkseg))); + } else { + printf("Seg: NULL.\n"); + } + horrors++; + } + fnextself(neightet); + } while (neightet.tet != tetloop.tet); + } + // Check the seg->tet pointer. + stpivot(sseg, neightet); + if (neightet.tet == NULL) { + printf(" !! Wrong seg->tet connection (A NULL tet).\n"); + horrors++; + } else { + if (!(((org(neightet) == pa) && (dest(neightet) == pb)) || + ((org(neightet) == pb) && (dest(neightet) == pa)))) { + printf(" !! Wrong seg->tet connection (Wrong edge).\n"); + printf(" Tet: x%lx (%d, %d, %d, %d) - Seg: x%lx (%d, %d).\n", + (unsigned long) neightet.tet, pointmark(org(neightet)), + pointmark(dest(neightet)), pointmark(apex(neightet)), + pointmark(oppo(neightet)), (unsigned long) sseg.sh, + pointmark(pa), pointmark(pb)); + horrors++; + } + } + } + } + } + tetloop.tet = tetrahedrontraverse(); + } + + if (horrors == 0) { + printf(" Segments are connected properly.\n"); + } else { + printf(" !! !! !! !! Found %d missing connections.\n", horrors); + } + + return horrors; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// checkconforming() Check if the mesh is boundary conforming Delaunay. // +// // +/////////////////////////////////////////////////////////////////////////////// + +int tetgenmesh::checkconforming() +{ + face shloop; + int horrors; + + horrors = 0; + + // Find all encroached segments. + subsegpool->traversalinit(); + shloop.shver = 0; + shloop.sh = shellfacetraverse(subsegpool); + while (shloop.sh != NULL) { + if (checkedge4encroach(shloop, NULL, 0)) { + printf(" !! Segment x%lx (%d, %d) is encroached.\n", + (unsigned long) shloop.sh, pointmark(sorg(shloop)), + pointmark(sdest(shloop))); + horrors++; + } + shloop.sh = shellfacetraverse(subsegpool); + } + + if (horrors == 0) { + printf(" Segments are boundary conforming Delaunay.\n"); + } else { + printf(" !! !! !! !! Found %d encroached segments.\n", horrors); + } + + return horrors; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// algorithmstatistics() Report algorithmic performances. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::algorithmstatistics() +{ + /*// Report memory usages. + unsigned long totalmeshbytes; + printf("Memory allocation statistics:\n\n"); + printf(" Maximum number of vertices: %ld\n", pointpool->maxitems); + totalmeshbytes = pointpool->maxitems * pointpool->itembytes; + printf(" Maximum number of tetrahedra: %ld\n", tetrahedronpool->maxitems); + totalmeshbytes += tetrahedronpool->maxitems * tetrahedronpool->itembytes; + if (subfacepool != (memorypool *) NULL) { + printf(" Maximum number of subfaces: %ld\n", subfacepool->maxitems); + totalmeshbytes += subfacepool->maxitems * subfacepool->itembytes; + } + if (subsegpool != (memorypool *) NULL) { + printf(" Maximum number of segments: %ld\n", subsegpool->maxitems); + totalmeshbytes += subsegpool->maxitems * subsegpool->itembytes; + } + printf(" Heap memory used by the mesh (K bytes): %g\n\n", + ((double) totalmeshbytes) / 1024.0); + */ + + printf("Algorithmic statistics:\n\n"); + + printf(" Number of orient3d tests: %ld\n", orient3dcount); + printf(" Number of insphere tests: %ld\n", inspherecount); + printf(" Number of symbolic insphere tests: %ld\n", insphere_sos_count); + printf(" Number of visited tets in point location: %ld\n", ptloc_count); + printf(" Maximal number of tets per point location: %ld\n",ptloc_max_count); + printf(" Number of 1-to-4 flips: %ld\n", flip14count); + printf(" Number of 2-to-6 flips: %ld\n", flip26count); + printf(" Number of n-t-2n flips: %ld\n", flipn2ncount); + + if (!b->plc) { + if (b->bowyerwatson) { + printf(" Number of deleted tets: %ld\n", totaldeadtets); + printf(" Number of created tets: %ld\n", totalbowatcavsize); + printf(" Maximum number of tets per new point: %ld\n", maxbowatcavsize); + printf(" Number of 3-to-2 flips: %ld\n", flip32count); + } else { + printf(" Number of 3-to-2 flips: %ld\n", flip32count); + printf(" Number of 2-to-3 flips: %ld\n", flip23count); + printf(" Number of n-to-m flips: %ld\n", flipnmcount); + printf(" Total number of primitive flips: %ld\n", + flip23count + flip32count); + } + } + + if (b->plc) { + printf(" Number of 2-to-2 flips: %ld\n", flip22count); + printf(" Number of tri-edge inter (coplanar) tests: %ld (%ld)\n", + triedgcount, triedgcopcount); + printf(" Number of crossed faces (edges) in scout segs: %ld (%ld)\n", + across_face_count, across_edge_count); + printf(" Maximal number of crossed faces per segment: %ld\n", + across_max_count); + printf(" Number of rule-1 points: %ld\n", r1count); + printf(" Number of rule-2 points: %ld\n", r2count); + printf(" Number of rule-3 points: %ld\n", r3count); + printf(" Maximal size of a missing region: %ld\n", maxregionsize); + printf(" Maximal size of a recovered cavity: %ld\n", maxcavsize); + printf(" Number of non-Delaunay edges: %ld\n", ndelaunayedgecount); + printf(" Number of cavity expansions: %ld\n", cavityexpcount); + } + + // printf(" Total point location time (millisec): %g\n", tloctime * 1e+3); + // printf(" Total point insertion time (millisec): %g\n",tinserttime*1e+3); + // if (b->bowyerwatson == 0) { + // printf(" Total flip time (millisec): %g\n", tfliptime * 1e+3); + // } + + printf("\n"); +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// statistics() Print all sorts of cool facts. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::statistics() +{ + long tetnumber, facenumber; + + printf("\nStatistics:\n\n"); + printf(" Input points: %d\n", in->numberofpoints); + if (b->refine) { + printf(" Input tetrahedra: %d\n", in->numberoftetrahedra); + } + if (b->plc) { + printf(" Input facets: %d\n", in->numberoffacets); + printf(" Input segments: %ld\n", insegments); + printf(" Input holes: %d\n", in->numberofholes); + printf(" Input regions: %d\n", in->numberofregions); + } + + tetnumber = tetrahedronpool->items - hullsize; + facenumber = (tetnumber * 4l + hullsize) / 2l; + + printf("\n Mesh points: %ld\n", pointpool->items); + printf(" Mesh tetrahedra: %ld\n", tetnumber); + printf(" Mesh faces: %ld\n", facenumber); + printf(" Mesh edges: %ld\n", meshedges); + + if (b->plc || b->refine) { + printf(" Mesh boundary faces: %ld\n", subfacepool->items); + printf(" Mesh boundary edges: %ld\n", meshsubedges); + printf(" Mesh subsegments: %ld\n", subsegpool->items); + } else { + printf(" Convex hull faces: %ld\n", hullsize); + } + printf("\n"); + + if (b->verbose > 0) { + printf(" Euler characteristic of mesh domain: %ld\n", pointpool->items + - meshedges + facenumber - tetnumber); + //printf(" Euler characteristic of boundary: %ld\n", pointpool->items + // - meshsubedges + subfacepool->items); + printf("\n"); + } + + if (b->verbose > 0) { + // qualitystatistics(); + algorithmstatistics(); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// terminatetetgen() Terminate TetGen with a given exit code. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void terminatetetgen(int x) +{ +#ifdef TETLIBRARY + throw x; +#else + exit(x); +#endif // #ifdef TETLIBRARY +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// Debug functions. // +// // +/////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +// Print the detail informations of a tetrahedron. + +void tetgenmesh::ptet(triface* t) +{ + triface tmpface, prtface; + shellface *shells; + face checksh; + point *pts, tmppt; + REAL ori; + int facecount; + + printf("Tetra x%lx with loc(%i) ver(%i):", + (unsigned long)(t->tet), t->loc, t->ver); + if (t->tet == NULL) { + printf(" !! NOT A VALID HANDLE\n"); + return; + } + pts = (point *) t->tet; + if (pts[4] == NULL) { + printf(" !! A DEAD TET\n"); + return; + } + if (pts[7] != dummypoint) { + ori = orient3d(pts[4], pts[5], pts[6], pts[7]); + printf(" ori = %g.\n", ori); + } else { + printf(" (hull tet).\n"); + } + // Report the status of this tet. + printf(" "); + if (infected(*t)) { + printf("(infected) "); + } + if (marktested(*t)) { + printf("(marktested) "); + } + if (edgemarked(*t)) { + printf("(edgemarked) "); + } + if (b->regionattrib) { + printf("attr(%d)", elemattribute(t->tet, 0)); + } + printf("\n"); + + tmpface = *t; + facecount = 0; + while(facecount < 4) { + tmpface.loc = facecount; + sym(tmpface, prtface); + if (prtface.tet == NULL) { + printf(" [%i] Open !!\n", facecount); + } else { + printf(" [%i] x%lx loc(%i) ver(%i)", facecount, + (unsigned long)(prtface.tet), prtface.loc, prtface.ver); + if ((point) prtface.tet[7] == dummypoint) { + printf(" (hull tet)"); + } + if (infected(prtface)) { + printf(" (infected)"); + } + printf("\n"); + } + facecount++; + } + + tmppt = org(*t); + if(tmppt == (point) NULL) { + printf(" Org [%i] NULL\n", locver2org[t->loc][t->ver]); + } else { + printf(" Org [%i] x%lx (%.12g,%.12g,%.12g) %d\n", + locver2org[t->loc][t->ver], (unsigned long)(tmppt), + tmppt[0], tmppt[1], tmppt[2], pointmark(tmppt)); + } + tmppt = dest(*t); + if(tmppt == (point) NULL) { + printf(" Dest[%i] NULL\n", locver2dest[t->loc][t->ver]); + } else { + printf(" Dest[%i] x%lx (%.12g,%.12g,%.12g) %d\n", + locver2dest[t->loc][t->ver], (unsigned long)(tmppt), + tmppt[0], tmppt[1], tmppt[2], pointmark(tmppt)); + } + tmppt = apex(*t); + if(tmppt == (point) NULL) { + printf(" Apex[%i] NULL\n", locver2apex[t->loc][t->ver]); + } else { + printf(" Apex[%i] x%lx (%.12g,%.12g,%.12g) %d\n", + locver2apex[t->loc][t->ver], (unsigned long)(tmppt), + tmppt[0], tmppt[1], tmppt[2], pointmark(tmppt)); + } + tmppt = oppo(*t); + if(tmppt == (point) NULL) { + printf(" Oppo[%i] NULL\n", loc2oppo[t->loc]); + } else { + printf(" Oppo[%i] x%lx (%.12g,%.12g,%.12g) %d\n", + loc2oppo[t->loc], (unsigned long)(tmppt), + tmppt[0], tmppt[1], tmppt[2], pointmark(tmppt)); + } + + if (checksubsegs) { + if (t->tet[8] != NULL) { + shells = (shellface *) t->tet[8]; + for (facecount = 0; facecount < 6; facecount++) { + sdecode(shells[facecount], checksh); + if (checksh.sh != NULL) { + printf(" [%d] x%lx %d.", facecount, (unsigned long) checksh.sh, + checksh.shver); + } else { + printf(" [%d] NULL.", facecount); + } + if (locver2edge[t->loc][t->ver] == facecount) { + printf(" (*)"); // It is the current edge. + } + printf("\n"); + } + } + } + + if (checksubfaces) { + if (t->tet[9] != NULL) { + shells = (shellface *) t->tet[9]; + for (facecount = 0; facecount < 4; facecount++) { + sdecode(shells[facecount], checksh); + if (checksh.sh != NULL) { + printf(" [%d] x%lx %d.", facecount, (unsigned long) checksh.sh, + checksh.shver); + } else { + printf(" [%d] NULL.", facecount); + } + if (t->loc == facecount) { + printf(" (*)"); // It is the current face. + } + printf("\n"); + } + } + } +} + +/////////////////////////////////////////////////////////////////////////////// +// Print the detail informations of a shellface. + +void tetgenmesh::psh(face *s) +{ + face prtsh; + triface prttet; + point *pt, printpoint; + REAL n[3]; + + if (s->sh == NULL) { + printf("Not a handle.\n"); + return; + } + + if (s->sh[3] == NULL) { + printf("A dead subface x%lx.\n", (unsigned long)(s->sh)); + return; + } + + pt = (point *) s->sh; + if (s->sh[5] != NULL) { + printf("subface x%lx, ver %d, mark %d:\n",(unsigned long)(s->sh),s->shver, + getshellmark(*s)); + facenormal(pt[3], pt[4], pt[5], n, 1); + printf(" area %g, edge lengths %g %g %g\n", 0.5 * sqrt(DOT(n, n)), + DIST(pt[3], pt[4]), DIST(pt[4], pt[5]), DIST(pt[5], pt[3])); + } else { + printf("Subsegment x%lx, ver %d, mark %d:\n", (unsigned long)(s->sh), + s->shver, getshellmark(*s)); + printf(" length %g", DIST(pt[3], pt[4])); + } + if (sinfected(*s)) { + printf(" (infected)"); + } + if (smarktested(*s)) { + printf(" (marked)"); + } + // if (shell2badface(*sface)) { + // printf(" (queued)"); + // } + // if (checkpbcs) { + // if (shellpbcgroup(*sface) >= 0) { + // printf(" (pbc %d)", shellpbcgroup(*sface)); + // } + // } + printf("\n"); + + sdecode(s->sh[0], prtsh); + if (prtsh.sh == NULL) { + printf(" [0] = No shell\n"); + } else { + printf(" [0] = x%lx %d\n", (unsigned long)(prtsh.sh), prtsh.shver); + } + sdecode(s->sh[1], prtsh); + if (prtsh.sh == NULL) { + printf(" [1] = No shell\n"); + } else { + printf(" [1] = x%lx %d\n", (unsigned long)(prtsh.sh), prtsh.shver); + } + sdecode(s->sh[2], prtsh); + if (prtsh.sh == NULL) { + printf(" [2] = No shell\n"); + } else { + printf(" [2] = x%lx %d\n", (unsigned long)(prtsh.sh), prtsh.shver); + } + + printpoint = sorg(*s); + if (printpoint == (point) NULL) + printf(" Org [%d] = NULL\n", vo[s->shver]); + else + printf(" Org [%d] = x%lx (%.12g,%.12g,%.12g) %d\n", + vo[s->shver], (unsigned long)(printpoint), printpoint[0], + printpoint[1], printpoint[2], pointmark(printpoint)); + printpoint = sdest(*s); + if (printpoint == (point) NULL) + printf(" Dest[%d] = NULL\n", vd[s->shver]); + else + printf(" Dest[%d] = x%lx (%.12g,%.12g,%.12g) %d\n", + vd[s->shver], (unsigned long)(printpoint), printpoint[0], + printpoint[1], printpoint[2], pointmark(printpoint)); + + printpoint = sapex(*s); + if (printpoint == (point) NULL) + printf(" Apex[%d] = NULL\n", va[s->shver]); + else + printf(" Apex[%d] = x%lx (%.12g,%.12g,%.12g) %d\n", + va[s->shver], (unsigned long)(printpoint), printpoint[0], + printpoint[1], printpoint[2], pointmark(printpoint)); + + if (s->sh[5] != NULL) { + sdecode(s->sh[6], prtsh); + if (prtsh.sh == NULL) { + printf(" [6] = No subsegment\n"); + } else { + printf(" [6] = x%lx %d\n", (unsigned long) prtsh.sh, prtsh.shver); + } + sdecode(s->sh[7], prtsh); + if (prtsh.sh == NULL) { + printf(" [7] = No subsegment\n"); + } else { + printf(" [7] = x%lx %d\n", (unsigned long) prtsh.sh, prtsh.shver); + } + sdecode(s->sh[8], prtsh); + if (prtsh.sh == NULL) { + printf(" [8] = No subsegment\n"); + } else { + printf(" [8] = x%lx %d\n", (unsigned long) prtsh.sh, prtsh.shver); + } + } + + // Print the adjacent tet of this subface or segment. + decode(s->sh[9], prttet); + if (prttet.tet == NULL) { + printf(" [9] = Outer space\n"); + } else { + printf(" [9] = x%lx (%d, %d, %d, %d)\n",(unsigned long) prttet.tet, + pointmark(org(prttet)), pointmark(dest(prttet)), + pointmark(apex(prttet)), pointmark(oppo(prttet))); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// Find and print the tetrahedron (or face or edge) with the given indices. +// Do not handle 'dummypoint' (-1). + +void tetgenmesh::pteti(int i, int j, int k, int l) +{ + triface t; + point *pts; + int *marklist; + int ii; + + marklist = new int[pointpool->items + 1]; + for (ii = 0; ii < pointpool->items + 1; ii++) marklist[ii] = 0; + // Marke the given indices. + marklist[i] = marklist[j] = marklist[k] = marklist[l] = 1; + + t.loc = t.ver = 0; + tetrahedronpool->traversalinit(); + t.tet = tetrahedrontraverse(); + while (t.tet != NULL) { + pts = (point *) t.tet; + if (pts[7] != dummypoint) { + if ((marklist[pointmark(pts[4])] + marklist[pointmark(pts[5])] + + marklist[pointmark(pts[6])] + marklist[pointmark(pts[7])]) == 4) { + ptet(&t); // Find! + break; + } + } + t.tet = tetrahedrontraverse(); + } + + if (t.tet == NULL) { + printf(" !! Not exist.\n"); + } + delete [] marklist; +} + +void tetgenmesh::pface(int i, int j, int k) +{ + triface t, t1; + point *pts; + REAL sign; + int *marklist; + int ii; + + marklist = new int[pointpool->items + 1]; + for (ii = 0; ii < pointpool->items + 1; ii++) marklist[ii] = 0; + // Marke the given indices. + marklist[i] = marklist[j] = marklist[k] = 1; + + t.ver = t1.ver = 0; + tetrahedronpool->traversalinit(); + t.tet = tetrahedrontraverse(); + while (t.tet != NULL) { + pts = (point *) t.tet; + if (pts[7] != dummypoint) { + if ((marklist[pointmark(pts[4])] + marklist[pointmark(pts[5])] + + marklist[pointmark(pts[6])] + marklist[pointmark(pts[7])]) == 3) { + // Find a tet containing the search face. + for (t.loc = 0; t.loc < 4; t.loc++) { + sym(t, t1); + pts = (point *) t1.tet; + if ((marklist[pointmark(pts[4])] + marklist[pointmark(pts[5])] + + marklist[pointmark(pts[6])] + + (pts[7] != dummypoint ? marklist[pointmark(pts[7])] : 0)) == 3) + break; + } + assert(t.loc < 4); + // Now t and t1 share the face. + printf(" tet x%lx (%d, %d, %d, %d) %d\n", (unsigned long) t.tet, + pointmark(org(t)), pointmark(dest(t)), pointmark(apex(t)), + pointmark(oppo(t)), t.loc); + printf(" tet x%lx (%d, %d, %d, %d) %d\n", (unsigned long) t1.tet, + pointmark(org(t1)), pointmark(dest(t1)), pointmark(apex(t1)), + pointmark(oppo(t1)), t1.loc); + if ((point) t1.tet[7] != dummypoint) { + pts = (point *) t.tet; + sign = insphere(pts[4], pts[5], pts[6], pts[7], oppo(t1)); + printf(" %s (sign = %.g).\n", sign > 0 ? "Delaunay" : + (sign < 0 ? "Non-Delaunay" : "Cosphere"), sign); + if (sign == 0) { + sign = insphere_sos(pts[4], pts[5], pts[6], pts[7], oppo(t1)); + printf(" %s (symbolic).\n", sign > 0 ? "Delaunay":"Non-Delaunay"); + } + } + break; + } + } + t.tet = tetrahedrontraverse(); + } + + if (t.tet == NULL) { + printf(" !! Not exist.\n"); + } + delete [] marklist; +} + +bool tetgenmesh::pedge(int i, int j) +{ + triface t, t1; + face ssub, sseg; + int ii; + + t.ver = t1.ver = 0; + tetrahedronpool->traversalinit(); + t.tet = tetrahedrontraverse(); + while (t.tet != NULL) { + for (ii = 0; ii < 6; ii++) { + t.loc = edge2locver[ii][0]; + t.ver = edge2locver[ii][1]; + if ((pointmark(org(t)) == i && pointmark(dest(t)) == j) || + (pointmark(org(t)) == j && pointmark(dest(t)) == i)) break; + } + if (ii < 6) { + // Now t is the edge (i, j). Find all tets at (i, j). + t1 = t; + do { + printf(" tet x%lx (%d, %d, %d, %d)", (unsigned long) t1.tet, + pointmark(org(t1)), pointmark(dest(t1)), pointmark(apex(t1)), + pointmark(oppo(t1))); + if (checksubsegs) { + tsspivot(t1, sseg); + if (sseg.sh != NULL) { + printf(" (seg)"); + } + } + if (checksubfaces) { + tspivot(t1, ssub); + if (ssub.sh != NULL) { + printf(" (sub)"); + } + } + if (edgemarked(t1)) { + printf(" (marked)"); + } + printf("\n"); + // Go to the next tet. + fnextself(t1); + } while (t1.tet != t.tet); + break; + } + t.tet = tetrahedrontraverse(); + } + + if (t.tet == NULL) { + printf(" !! Not exist.\n"); + } + return t.tet != NULL; +} + +/////////////////////////////////////////////////////////////////////////////// +// Find the subface with indices (i, j, k) + +void tetgenmesh::psubface(int i, int j, int k) +{ + triface t, t1; + face s, s1; + point *pts; + REAL n[3], sign; + int *marklist; + int ii; + + void **bakpathblock = subfacepool->pathblock; + void *bakpathitem = subfacepool->pathitem; + int bakpathitemsleft = subfacepool->pathitemsleft; + int bakalignbytes = subfacepool->alignbytes; + + marklist = new int[pointpool->items + 1]; + for (ii = 0; ii < pointpool->items + 1; ii++) marklist[ii] = 0; + // Marke the given indices. + marklist[i] = marklist[j] = marklist[k] = 1; + + s.shver = 0; + subfacepool->traversalinit(); + s.sh = shellfacetraverse(subfacepool); + while (s.sh != NULL) { + pts = (point *) s.sh; + if (pts[3] != NULL) { + if ((marklist[pointmark(pts[3])] + marklist[pointmark(pts[4])] + + marklist[pointmark(pts[5])]) == 3) { + // Found. + printf(" sub x%lx (%d, %d, %d) mark=%d\n", (unsigned long) s.sh, + pointmark(pts[3]), pointmark(pts[4]), pointmark(pts[5]), + getshellmark(s)); + facenormal(pts[3], pts[4], pts[5], n, 1); + printf(" area=%g, lengths: %g, %g, %g\n", 0.5 * sqrt(DOT(n, n)), + DIST(pts[3], pts[4]), DIST(pts[4], pts[5]), DIST(pts[5], pts[3])); + // Print coplanar adjacent subfaces. + s.shver = 0; + for (ii = 0; ii < 3; ii++) { + sspivot(s, s1); + if (s1.sh != NULL) { + printf(" seg x%lx (%d, %d)\n", (unsigned long) s1.sh, + pointmark(sorg(s1)), pointmark(sdest(s1))); + } else { + spivot(s, s1); + if (s1.sh != NULL) { + printf(" sub x%lx (%d, %d, %d)\n", (unsigned long) s1.sh, + pointmark(sorg(s1)), pointmark(sdest(s1)), pointmark(sapex(s1))); + } else { + printf(" No seg and sub at (%d, %d)!\n", pointmark(sorg(s)), + pointmark(sdest(s))); + } + } + senextself(s); + } + stpivot(s, t); + if (t.tet != NULL) { + // Print two adjacent tets. + symedge(t, t1); + // Now t and t1 share the face. + printf(" tet x%lx (%d, %d, %d, %d) %d\n", (unsigned long) t.tet, + pointmark(org(t)), pointmark(dest(t)), pointmark(apex(t)), + pointmark(oppo(t)), t.loc); + printf(" tet x%lx (%d, %d, %d, %d) %d\n", (unsigned long) t1.tet, + pointmark(org(t1)), pointmark(dest(t1)), pointmark(apex(t1)), + pointmark(oppo(t1)), t1.loc); + if (((point) t1.tet[7] != dummypoint) && + ((point) t.tet[7] != dummypoint)) { + pts = (point *) t.tet; + sign = insphere(pts[4], pts[5], pts[6], pts[7], oppo(t1)); + printf(" %s (sign = %.g).\n", sign > 0 ? "Delaunay" : + (sign < 0 ? "Non-Delaunay" : "Cosphere"), sign); + if (sign == 0) { + sign = insphere_sos(pts[4], pts[5], pts[6], pts[7], oppo(t1)); + printf(" %s (symbolic).\n", sign > 0 ? "Delaunay":"Non-Delaunay"); + } + } + } + break; + } + } + s.sh = shellfacetraverse(subfacepool); + } + + if (s.sh == NULL) { + printf(" !! Not exist.\n"); + } + delete [] marklist; + + subfacepool->pathblock = bakpathblock; + subfacepool->pathitem = bakpathitem; + subfacepool->pathitemsleft = bakpathitemsleft; + subfacepool->alignbytes = bakalignbytes; +} + +/////////////////////////////////////////////////////////////////////////////// +// Print the information of the subsegment (i, j). +// Return 1 if seg-to-tet pointer is broken. + +int tetgenmesh::psubseg(int i, int j) +{ + triface t; + face s; //, s1; + point forg, fdest; + bool bflag; + + bflag = false; + s.shver = 0; + subsegpool->traversalinit(); + s.sh = shellfacetraverse(subsegpool); + while (s.sh != NULL) { + if (pointmark(sorg(s)) == i) { + if (pointmark(sdest(s)) == j) { + bflag = true; + } + } else if (pointmark(sorg(s)) == j) { + if (pointmark(sdest(s)) == i) { + sesymself(s); + bflag = true; + } + } + if (bflag) { + // Print the original segment containing [i, j] + forg = farsorg(s); + fdest = farsdest(s); + printf(" seg x%lx (%d, %d) < (%d, %d)\n", (unsigned long) s.sh, i, j, + pointmark(forg), pointmark(fdest)); + // Chekc seg-to-tet pointer + stpivot(s, t); + if (t.tet != NULL) { + if (t.tet[4] != NULL) { + forg = org(t); + fdest = dest(t); + if (((pointmark(forg) == i) && (pointmark(fdest) == j)) || + ((pointmark(forg) == j) && (pointmark(fdest) == i))) { + printf(" adj tet x%lx (%d, %d, %d, %d)\n", (unsigned long) t.tet, + pointmark(forg), pointmark(fdest), pointmark(apex(t)), + pointmark(oppo(t))); + } else { + printf(" !! Wrong seg-to-tet pointer.\n"); + return 1; + } + } else { + printf(" !! A DEAD seg-to-tet pointer.\n"); + return 1; + } + } + /*// Print the adjacent subsegments at i and j. + senext2(s, s1); + spivotself(s1); + if (s1.sh != NULL) { + if (sdest(s1) != i) sesymself(s1); + printf(" [%d] seg x%lx (%d, %d)\n", i, (unsigned long) s1.sh, + pointmark(sorg(s1)), pointmark(sdest(s1))); + } else { + printf(" [%d] NULL", i); + } + senext(s, s1); + spivotself(s1); + if (s1.sh != NULL) { + if (sorg(s1) != j) sesymself(s1); + printf(" [%d] seg x%lx (%d, %d)\n", j, (unsigned long) s1.sh, + pointmark(sorg(s1)), pointmark(sdest(s1))); + } else { + printf(" [%d] NULL", j); + }*/ + break; + } + s.sh = shellfacetraverse(subsegpool); + } + + if (!bflag) { + printf(" !! Not exist.\n"); + } + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// +// Print the index of the point. + +int tetgenmesh::pmark(point p) +{ + return pointmark(p); +} + +void tetgenmesh::pvert(point pt) +{ + triface adjtet; + int idx; + + idx = ((int *) (pt))[pointmarkindex]; + printf(" vertex %d: x%lx\n", idx, (unsigned long) pt); + idx = ((int *) (pt))[pointmarkindex + 1]; + printf(" type: %d (%s infected).\n", idx >> 1, idx & 1 ? " " : "not"); + + decode(point2tet(pt), adjtet); + if (adjtet.tet != NULL) { + printf(" adjtet: x%lx (%d, %d, %d, %d).\n", (unsigned long) adjtet.tet, + pointmark(adjtet.tet[4]), pointmark(adjtet.tet[5]), + pointmark(adjtet.tet[6]), pointmark(adjtet.tet[7])); + } else { + printf(" No adjacent tet.\n"); + } +} + +int tetgenmesh::pverti(int idx) +{ + triface adjtet; + point pt; + + // Search the vertex. + pointpool->traversalinit(); + pt = pointtraverse(); + while (pt != NULL) { + if (idx == ((int *) (pt))[pointmarkindex]) break; + pt = pointtraverse(); + } + + if (pt == NULL) { + printf(" Not exist.\n"); + return 0; + } + + printf(" vertex %d: x%lx\n", idx, (unsigned long) pt); + idx = ((int *) (pt))[pointmarkindex + 1]; + printf(" type: %d (%s infected).\n", idx >> 1, idx & 1 ? " " : "not"); + + decode(point2tet(pt), adjtet); + if (adjtet.tet == NULL) { + printf(" No adjacent tet.\n"); + return 0; + } + if (adjtet.tet[4] == NULL) { + printf(" !! A DEAD adjacent tet.\n"); + return 0; + } + printf(" adjtet: x%lx (%d, %d, %d, %d).\n", (unsigned long) adjtet.tet, + pointmark(adjtet.tet[4]), pointmark(adjtet.tet[5]), + pointmark(adjtet.tet[6]), pointmark(adjtet.tet[7])); + + if (((point) adjtet.tet[4] == pt) || ((point) adjtet.tet[5] == pt) || + ((point) adjtet.tet[6] == pt) || ((point) adjtet.tet[7] == pt)) { + return 0; + } else { + return 1; // Bad point-to-tet map. + } +} + +/////////////////////////////////////////////////////////////////////////////// +// Geometrical tests. + +REAL tetgenmesh::test_orient3d(int i, int j, int k, int l) +{ + point *idx2ptmap; + REAL ori; + int idx; + + idx = (int) pointpool->items; + if ((i > idx) || (j > idx) || (k > idx) || (l > idx)) { + printf("Input indices are invalid.\n"); + return 0; + } + + makeindex2pointmap(idx2ptmap); + ori = orient3d(idx2ptmap[i], idx2ptmap[j], idx2ptmap[k], idx2ptmap[l]); + delete [] idx2ptmap; + + return ori; +} + +REAL tetgenmesh::test_insphere(int i, int j, int k, int l, int m) +{ + point *idx2ptmap; + REAL sign; + int idx; + + idx = (int) pointpool->items; + if ((i > idx) || (j > idx) || (k > idx) || (l > idx) || (m > idx)) { + printf("Input indices are invalid.\n"); + return 0; + } + + makeindex2pointmap(idx2ptmap); + sign = insphere(idx2ptmap[i], idx2ptmap[j], idx2ptmap[k], idx2ptmap[l], + idx2ptmap[m]); + if (sign == 0) { + printf(" sign == 0.0! (symbolic perturbed) \n"); + sign = insphere_sos(idx2ptmap[i], idx2ptmap[j], idx2ptmap[k], idx2ptmap[l], + idx2ptmap[m]); + } + delete [] idx2ptmap; + + return sign; +} + +/////////////////////////////////////////////////////////////////////////////// +// test_tritri() Test if two triangles are intersecting. + +int tetgenmesh::test_tritri(int a, int b, int c, int p, int q, int r) +{ + point *idx2ptmap; + point A, B, C, P, Q, R; + enum intersection dir; + int ret, types[2], pos[4]; + int idx, i; + + idx = (int) pointpool->items; + if ((a > idx) || (b > idx) || (c > idx) || + (p > idx) || (q > idx) || (r > idx) || + (a<in->firstnumber) || (b<in->firstnumber) || (c<in->firstnumber) || + (p<in->firstnumber) || (q<in->firstnumber) || (r<in->firstnumber)) { + printf("Input indices are invalid.\n"); + return 0; + } + + makeindex2pointmap(idx2ptmap); + A = idx2ptmap[a]; + B = idx2ptmap[b]; + C = idx2ptmap[c]; + P = idx2ptmap[p]; + Q = idx2ptmap[q]; + R = idx2ptmap[r]; + + ret = tri_tri_test(A, B, C, P, Q, R, NULL, 1, types, pos); + + // Report the intersection types and positions. + for (i = 0; i < 2; i++) { + dir = (enum tetgenmesh::intersection) types[i]; + switch (dir) { + case tetgenmesh::DISJOINT: + printf(" DISJOINT\n"); break; + case tetgenmesh::SHAREVERT: + printf(" SHAREVERT %d %d\n", pos[i*2], pos[i*2+1]); break; + case tetgenmesh::SHAREEDGE: + printf(" SHAREEDGE %d %d\n", pos[i*2], pos[i*2+1]); break; + case tetgenmesh::SHAREFACE: + printf(" SHAREFACE\n"); break; + case tetgenmesh::TOUCHEDGE: + printf(" TOUCHEDGE %d %d\n", pos[i*2], pos[i*2+1]); break; + case tetgenmesh::TOUCHFACE: + printf(" TOUCHFACE %d %d\n", pos[i*2], pos[i*2+1]); break; + case tetgenmesh::ACROSSVERT: + printf(" ACROSSVERT %d %d\n", pos[i*2], pos[i*2+1]); break; + case tetgenmesh::ACROSSEDGE: + printf(" ACROSSEDGE %d %d\n", pos[i*2], pos[i*2+1]); break; + case tetgenmesh::ACROSSFACE: + printf(" ACROSSFACE %d %d\n", pos[i*2], pos[i*2+1]); break; + case tetgenmesh::ACROSSTET: + printf(" ACROSSTET\n"); break; + case tetgenmesh::TRIEDGEINT: + printf(" TRIEDGEINT %d %d\n", pos[i*2], pos[i*2+1]); break; + case tetgenmesh::EDGETRIINT: + printf(" EDGETRIINT %d %d\n", pos[i*2], pos[i*2+1]); break; + } + } + + delete [] idx2ptmap; + return ret; +} + +/////////////////////////////////////////////////////////////////////////////// +// Print an array of tetrahedra (in draw command) + +void tetgenmesh::print_cavebdrylist() +{ + FILE *fout; + triface *cavetet; + int i; + + printf(" Dump %ld faces to dump_cavebdry.lua.\n", cavebdrylist->objects); + + fout = fopen("dump_cavebdry.lua", "w"); + for (i = 0; i < cavebdrylist->objects; i++) { + cavetet = (triface *) fastlookup(cavebdrylist, i); + fprintf(fout, "p:draw_subface(%d, %d, %d) -- %d\n", + pointmark(org(*cavetet)), pointmark(dest(*cavetet)), + pointmark(apex(*cavetet)), i); + } + fclose(fout); +} + +/////////////////////////////////////////////////////////////////////////////// +// Print current faces in flipstack (in draw command) + +void tetgenmesh::print_flipstack() +{ + badface *traveface; + int i; + + traveface = futureflip; + i = 0; + while (traveface != NULL) { + if (traveface->tt.tet[4] != NULL) { + printf("%2d (%d, %d, %d, %d) - %d\n",i+1,pointmark(org(traveface->tt)), + pointmark(dest(traveface->tt)), pointmark(apex(traveface->tt)), + pointmark(oppo(traveface->tt)), pointmark(traveface->foppo)); + } + traveface = traveface->nextitem; + i++; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// Print an array of tetrahedra, faces, subfaces (in draw command) +// If 'nohulltet' is TRUE, ignore hull tets. + +void tetgenmesh::print_tetarray(arraypool *tetarray, bool nohulltet) +{ + triface *parytet; + int i; + + for (i = 0; i < tetarray->objects; i++) { + parytet = (triface *) fastlookup(tetarray, i); + if (parytet->tet == NULL) { + printf("-- NOT A TET -- %d\n", i + 1); continue; + } + if (nohulltet) { + if ((point) parytet->tet[7] == dummypoint) continue; + } + if (parytet->tet[4] == NULL) { + printf("-- A DEAD TET -- %d\n", i + 1); continue; + } + if ((point) parytet->tet[7] != dummypoint) { + printf("p:draw_tet(%d, %d, %d, %d) -- %d", + pointmark(org(*parytet)), pointmark(dest(*parytet)), + pointmark(apex(*parytet)), pointmark(oppo(*parytet)), i + 1); + } else { + printf("-- p:draw_tet(%d, %d, %d, %d) -- %d (hulltet)", + pointmark(org(*parytet)), pointmark(dest(*parytet)), + pointmark(apex(*parytet)), pointmark(oppo(*parytet)), i + 1); + } + if (marktested(*parytet)) { + printf(" (marked)"); + } + if (infected(*parytet)) { + printf(" (infect)"); + } + printf("\n"); + } +} + +void tetgenmesh::print_facearray(arraypool *facearray) +{ + triface *parytet; + int i; + + for (i = 0; i < facearray->objects; i++) { + parytet = (triface *) fastlookup(facearray, i); + printf("p:draw_subface(%d, %d, %d) -- %d\n", + pointmark(org(*parytet)), pointmark(dest(*parytet)), + pointmark(apex(*parytet)), i + 1); + } +} + +void tetgenmesh::print_subfacearray(arraypool *subfacearray) +{ + face *parysub; + int i; + + for (i = 0; i < subfacearray->objects; i++) { + parysub = (face *) fastlookup(subfacearray, i); + printf("p:draw_subface(%d, %d, %d) -- %d\n", + pointmark(sorg(*parysub)), pointmark(sdest(*parysub)), + pointmark(sapex(*parysub)), i + 1); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// dump the boundary faces of a cavity into file "cavity.lua" +// 'topfaces' and 'botfaces' are two arrays. +// NOTE: hull tets may be included. + +void tetgenmesh::dump_cavity(arraypool *topfaces, arraypool *botfaces = NULL) +{ + FILE *fout; + arraypool *cavfaces; + triface *paryface; + int i, k; + + printf(" dump %ld topfaces to cavity.lua\n", topfaces->objects); + if (botfaces != NULL) { + printf(" dump %ld botfaces to cavity.lua\n", botfaces->objects); + } + fout = fopen("cavity.lua", "w"); + + for (k = 0; k < 2; k++) { + cavfaces = (k == 0 ? topfaces : botfaces); + if (cavfaces != NULL) { + for (i = 0; i < cavfaces->objects; i++) { + paryface = (triface *) fastlookup(cavfaces, i); + fprintf(fout, "p:draw_subface(%d, %d, %d) -- %d\n", + pointmark(org(*paryface)), pointmark(dest(*paryface)), + pointmark(apex(*paryface)), i + 1); + } + } + } + + fclose(fout); +} + +/////////////////////////////////////////////////////////////////////////////// +// dump a facet containing a given subface s. + +void tetgenmesh::dump_facetof(face *pssub, char *filename) +{ + FILE *fout; + char outfilename[256]; + arraypool *tmpfaces; + face *parysh, *parysh2, s; + face checkseg; + int ii, jj; + + tmpfaces = new arraypool(sizeof(face), 8); + + smarktest(*pssub); + tmpfaces->newindex((void **) &parysh); + *parysh = *pssub; + + for (ii = 0; ii < tmpfaces->objects; ii++) { + parysh = (face *) fastlookup(tmpfaces, ii); + for (jj = 0; jj < 3; jj++) { + sspivot(*parysh, checkseg); + if (checkseg.sh == NULL) { + spivot(*parysh, s); + if (s.sh != NULL) { + if (!smarktested(s)) { + smarktest(s); + tmpfaces->newindex((void **) &parysh2); + *parysh2 = s; + } + } + } + senextself(*parysh); + } + } + + for (ii = 0; ii < tmpfaces->objects; ii++) { + parysh = (face *) fastlookup(tmpfaces, ii); + sunmarktest(*parysh); + } + + if (filename != NULL) { + sprintf(outfilename, filename); + } else { + sprintf(outfilename, "facet.lua"); + } + + printf(" dump %ld subfaces to %s\n", tmpfaces->objects, outfilename); + fout = fopen(outfilename, "w"); + + for (ii = 0; ii < tmpfaces->objects; ii++) { + parysh = (face *) fastlookup(tmpfaces, ii); + fprintf(fout, "p:draw_subface(%d, %d, %d) -- %d\n", + pointmark(sorg(*parysh)), pointmark(sdest(*parysh)), + pointmark(sapex(*parysh)), ii + 1); + } + + fclose(fout); + + delete tmpfaces; +} + +/////////////////////////////////////////////////////////////////////////////// +// Dump the new tets of a cavity (in draw_tet() lua commands) + +void tetgenmesh::dump_cavitynewtets() +{ + arraypool *newtets; + triface searchtet, neightet, *parytet; + point *ppt; + int i; + + newtets = new arraypool(sizeof(triface), 8); + + // Collect all tets of the DT. + marktest(recenttet); + newtets->newindex((void **) &parytet); + *parytet = recenttet; + for (i = 0; i < newtets->objects; i++) { + searchtet = * (triface *) fastlookup(newtets, i); + for (searchtet.loc = 0; searchtet.loc < 4; searchtet.loc++) { + sym(searchtet, neightet); + if (!marktested(neightet)) { + marktest(neightet); + newtets->newindex((void **) &parytet); + *parytet = neightet; + } + } + } + // Comment: All new tets are marktested. + + // Output the new tets. + for (i = 0; i < newtets->objects; i++) { + parytet = (triface *) fastlookup(newtets, i); + ppt = (point *) parytet->tet; + if (ppt[7] != dummypoint) { + printf("p:draw_tet(%d, %d, %d, %d) -- %i\n", pointmark(ppt[4]), + pointmark(ppt[5]), pointmark(ppt[6]), pointmark(ppt[7]), i); + } else { + printf("-- p:draw_tet(%d, %d, %d, -1) -- %i\n", pointmark(ppt[4]), + pointmark(ppt[5]), pointmark(ppt[6]), i); + } + unmarktest(*parytet); // Unmarktest it. + } + + delete newtets; +} + +#endif // #ifndef meshstatCXX \ No newline at end of file diff --git a/contrib/TetgenNew/predicates.cxx b/contrib/TetgenNew/predicates.cxx new file mode 100644 index 0000000000..bc0bd39f9e --- /dev/null +++ b/contrib/TetgenNew/predicates.cxx @@ -0,0 +1,4187 @@ +/*****************************************************************************/ +/* */ +/* Routines for Arbitrary Precision Floating-point Arithmetic */ +/* and Fast Robust Geometric Predicates */ +/* (predicates.c) */ +/* */ +/* May 18, 1996 */ +/* */ +/* Placed in the public domain by */ +/* Jonathan Richard Shewchuk */ +/* School of Computer Science */ +/* Carnegie Mellon University */ +/* 5000 Forbes Avenue */ +/* Pittsburgh, Pennsylvania 15213-3891 */ +/* jrs@cs.cmu.edu */ +/* */ +/* This file contains C implementation of algorithms for exact addition */ +/* and multiplication of floating-point numbers, and predicates for */ +/* robustly performing the orientation and incircle tests used in */ +/* computational geometry. The algorithms and underlying theory are */ +/* described in Jonathan Richard Shewchuk. "Adaptive Precision Floating- */ +/* Point Arithmetic and Fast Robust Geometric Predicates." Technical */ +/* Report CMU-CS-96-140, School of Computer Science, Carnegie Mellon */ +/* University, Pittsburgh, Pennsylvania, May 1996. (Submitted to */ +/* Discrete & Computational Geometry.) */ +/* */ +/* This file, the paper listed above, and other information are available */ +/* from the Web page http://www.cs.cmu.edu/~quake/robust.html . */ +/* */ +/*****************************************************************************/ + +/*****************************************************************************/ +/* */ +/* Using this code: */ +/* */ +/* First, read the short or long version of the paper (from the Web page */ +/* above). */ +/* */ +/* Be sure to call exactinit() once, before calling any of the arithmetic */ +/* functions or geometric predicates. Also be sure to turn on the */ +/* optimizer when compiling this file. */ +/* */ +/* */ +/* Several geometric predicates are defined. Their parameters are all */ +/* points. Each point is an array of two or three floating-point */ +/* numbers. The geometric predicates, described in the papers, are */ +/* */ +/* orient2d(pa, pb, pc) */ +/* orient2dfast(pa, pb, pc) */ +/* orient3d(pa, pb, pc, pd) */ +/* orient3dfast(pa, pb, pc, pd) */ +/* incircle(pa, pb, pc, pd) */ +/* incirclefast(pa, pb, pc, pd) */ +/* insphere(pa, pb, pc, pd, pe) */ +/* inspherefast(pa, pb, pc, pd, pe) */ +/* */ +/* Those with suffix "fast" are approximate, non-robust versions. Those */ +/* without the suffix are adaptive precision, robust versions. There */ +/* are also versions with the suffices "exact" and "slow", which are */ +/* non-adaptive, exact arithmetic versions, which I use only for timings */ +/* in my arithmetic papers. */ +/* */ +/* */ +/* An expansion is represented by an array of floating-point numbers, */ +/* sorted from smallest to largest magnitude (possibly with interspersed */ +/* zeros). The length of each expansion is stored as a separate integer, */ +/* and each arithmetic function returns an integer which is the length */ +/* of the expansion it created. */ +/* */ +/* Several arithmetic functions are defined. Their parameters are */ +/* */ +/* e, f Input expansions */ +/* elen, flen Lengths of input expansions (must be >= 1) */ +/* h Output expansion */ +/* b Input scalar */ +/* */ +/* The arithmetic functions are */ +/* */ +/* grow_expansion(elen, e, b, h) */ +/* grow_expansion_zeroelim(elen, e, b, h) */ +/* expansion_sum(elen, e, flen, f, h) */ +/* expansion_sum_zeroelim1(elen, e, flen, f, h) */ +/* expansion_sum_zeroelim2(elen, e, flen, f, h) */ +/* fast_expansion_sum(elen, e, flen, f, h) */ +/* fast_expansion_sum_zeroelim(elen, e, flen, f, h) */ +/* linear_expansion_sum(elen, e, flen, f, h) */ +/* linear_expansion_sum_zeroelim(elen, e, flen, f, h) */ +/* scale_expansion(elen, e, b, h) */ +/* scale_expansion_zeroelim(elen, e, b, h) */ +/* compress(elen, e, h) */ +/* */ +/* All of these are described in the long version of the paper; some are */ +/* described in the short version. All return an integer that is the */ +/* length of h. Those with suffix _zeroelim perform zero elimination, */ +/* and are recommended over their counterparts. The procedure */ +/* fast_expansion_sum_zeroelim() (or linear_expansion_sum_zeroelim() on */ +/* processors that do not use the round-to-even tiebreaking rule) is */ +/* recommended over expansion_sum_zeroelim(). Each procedure has a */ +/* little note next to it (in the code below) that tells you whether or */ +/* not the output expansion may be the same array as one of the input */ +/* expansions. */ +/* */ +/* */ +/* If you look around below, you'll also find macros for a bunch of */ +/* simple unrolled arithmetic operations, and procedures for printing */ +/* expansions (commented out because they don't work with all C */ +/* compilers) and for generating random floating-point numbers whose */ +/* significand bits are all random. Most of the macros have undocumented */ +/* requirements that certain of their parameters should not be the same */ +/* variable; for safety, better to make sure all the parameters are */ +/* distinct variables. Feel free to send email to jrs@cs.cmu.edu if you */ +/* have questions. */ +/* */ +/*****************************************************************************/ + +#include <stdio.h> +#include <stdlib.h> +#include <math.h> +#ifdef CPU86 +#include <float.h> +#endif /* CPU86 */ +#ifdef LINUX +#include <fpu_control.h> +#endif /* LINUX */ + +#include "tetgen.h" // Defines the symbol REAL (float or double). + +/* On some machines, the exact arithmetic routines might be defeated by the */ +/* use of internal extended precision floating-point registers. Sometimes */ +/* this problem can be fixed by defining certain values to be volatile, */ +/* thus forcing them to be stored to memory and rounded off. This isn't */ +/* a great solution, though, as it slows the arithmetic down. */ +/* */ +/* To try this out, write "#define INEXACT volatile" below. Normally, */ +/* however, INEXACT should be defined to be nothing. ("#define INEXACT".) */ + +#define INEXACT /* Nothing */ +/* #define INEXACT volatile */ + +/* #define REAL double */ /* float or double */ +#define REALPRINT doubleprint +#define REALRAND doublerand +#define NARROWRAND narrowdoublerand +#define UNIFORMRAND uniformdoublerand + +/* Which of the following two methods of finding the absolute values is */ +/* fastest is compiler-dependent. A few compilers can inline and optimize */ +/* the fabs() call; but most will incur the overhead of a function call, */ +/* which is disastrously slow. A faster way on IEEE machines might be to */ +/* mask the appropriate bit, but that's difficult to do in C. */ + +#define Absolute(a) ((a) >= 0.0 ? (a) : -(a)) +/* #define Absolute(a) fabs(a) */ + +/* Many of the operations are broken up into two pieces, a main part that */ +/* performs an approximate operation, and a "tail" that computes the */ +/* roundoff error of that operation. */ +/* */ +/* The operations Fast_Two_Sum(), Fast_Two_Diff(), Two_Sum(), Two_Diff(), */ +/* Split(), and Two_Product() are all implemented as described in the */ +/* reference. Each of these macros requires certain variables to be */ +/* defined in the calling routine. The variables `bvirt', `c', `abig', */ +/* `_i', `_j', `_k', `_l', `_m', and `_n' are declared `INEXACT' because */ +/* they store the result of an operation that may incur roundoff error. */ +/* The input parameter `x' (or the highest numbered `x_' parameter) must */ +/* also be declared `INEXACT'. */ + +#define Fast_Two_Sum_Tail(a, b, x, y) \ + bvirt = x - a; \ + y = b - bvirt + +#define Fast_Two_Sum(a, b, x, y) \ + x = (REAL) (a + b); \ + Fast_Two_Sum_Tail(a, b, x, y) + +#define Fast_Two_Diff_Tail(a, b, x, y) \ + bvirt = a - x; \ + y = bvirt - b + +#define Fast_Two_Diff(a, b, x, y) \ + x = (REAL) (a - b); \ + Fast_Two_Diff_Tail(a, b, x, y) + +#define Two_Sum_Tail(a, b, x, y) \ + bvirt = (REAL) (x - a); \ + avirt = x - bvirt; \ + bround = b - bvirt; \ + around = a - avirt; \ + y = around + bround + +#define Two_Sum(a, b, x, y) \ + x = (REAL) (a + b); \ + Two_Sum_Tail(a, b, x, y) + +#define Two_Diff_Tail(a, b, x, y) \ + bvirt = (REAL) (a - x); \ + avirt = x + bvirt; \ + bround = bvirt - b; \ + around = a - avirt; \ + y = around + bround + +#define Two_Diff(a, b, x, y) \ + x = (REAL) (a - b); \ + Two_Diff_Tail(a, b, x, y) + +#define Split(a, ahi, alo) \ + c = (REAL) (splitter * a); \ + abig = (REAL) (c - a); \ + ahi = c - abig; \ + alo = a - ahi + +#define Two_Product_Tail(a, b, x, y) \ + Split(a, ahi, alo); \ + Split(b, bhi, blo); \ + err1 = x - (ahi * bhi); \ + err2 = err1 - (alo * bhi); \ + err3 = err2 - (ahi * blo); \ + y = (alo * blo) - err3 + +#define Two_Product(a, b, x, y) \ + x = (REAL) (a * b); \ + Two_Product_Tail(a, b, x, y) + +/* Two_Product_Presplit() is Two_Product() where one of the inputs has */ +/* already been split. Avoids redundant splitting. */ + +#define Two_Product_Presplit(a, b, bhi, blo, x, y) \ + x = (REAL) (a * b); \ + Split(a, ahi, alo); \ + err1 = x - (ahi * bhi); \ + err2 = err1 - (alo * bhi); \ + err3 = err2 - (ahi * blo); \ + y = (alo * blo) - err3 + +/* Two_Product_2Presplit() is Two_Product() where both of the inputs have */ +/* already been split. Avoids redundant splitting. */ + +#define Two_Product_2Presplit(a, ahi, alo, b, bhi, blo, x, y) \ + x = (REAL) (a * b); \ + err1 = x - (ahi * bhi); \ + err2 = err1 - (alo * bhi); \ + err3 = err2 - (ahi * blo); \ + y = (alo * blo) - err3 + +/* Square() can be done more quickly than Two_Product(). */ + +#define Square_Tail(a, x, y) \ + Split(a, ahi, alo); \ + err1 = x - (ahi * ahi); \ + err3 = err1 - ((ahi + ahi) * alo); \ + y = (alo * alo) - err3 + +#define Square(a, x, y) \ + x = (REAL) (a * a); \ + Square_Tail(a, x, y) + +/* Macros for summing expansions of various fixed lengths. These are all */ +/* unrolled versions of Expansion_Sum(). */ + +#define Two_One_Sum(a1, a0, b, x2, x1, x0) \ + Two_Sum(a0, b , _i, x0); \ + Two_Sum(a1, _i, x2, x1) + +#define Two_One_Diff(a1, a0, b, x2, x1, x0) \ + Two_Diff(a0, b , _i, x0); \ + Two_Sum( a1, _i, x2, x1) + +#define Two_Two_Sum(a1, a0, b1, b0, x3, x2, x1, x0) \ + Two_One_Sum(a1, a0, b0, _j, _0, x0); \ + Two_One_Sum(_j, _0, b1, x3, x2, x1) + +#define Two_Two_Diff(a1, a0, b1, b0, x3, x2, x1, x0) \ + Two_One_Diff(a1, a0, b0, _j, _0, x0); \ + Two_One_Diff(_j, _0, b1, x3, x2, x1) + +#define Four_One_Sum(a3, a2, a1, a0, b, x4, x3, x2, x1, x0) \ + Two_One_Sum(a1, a0, b , _j, x1, x0); \ + Two_One_Sum(a3, a2, _j, x4, x3, x2) + +#define Four_Two_Sum(a3, a2, a1, a0, b1, b0, x5, x4, x3, x2, x1, x0) \ + Four_One_Sum(a3, a2, a1, a0, b0, _k, _2, _1, _0, x0); \ + Four_One_Sum(_k, _2, _1, _0, b1, x5, x4, x3, x2, x1) + +#define Four_Four_Sum(a3, a2, a1, a0, b4, b3, b1, b0, x7, x6, x5, x4, x3, x2, \ + x1, x0) \ + Four_Two_Sum(a3, a2, a1, a0, b1, b0, _l, _2, _1, _0, x1, x0); \ + Four_Two_Sum(_l, _2, _1, _0, b4, b3, x7, x6, x5, x4, x3, x2) + +#define Eight_One_Sum(a7, a6, a5, a4, a3, a2, a1, a0, b, x8, x7, x6, x5, x4, \ + x3, x2, x1, x0) \ + Four_One_Sum(a3, a2, a1, a0, b , _j, x3, x2, x1, x0); \ + Four_One_Sum(a7, a6, a5, a4, _j, x8, x7, x6, x5, x4) + +#define Eight_Two_Sum(a7, a6, a5, a4, a3, a2, a1, a0, b1, b0, x9, x8, x7, \ + x6, x5, x4, x3, x2, x1, x0) \ + Eight_One_Sum(a7, a6, a5, a4, a3, a2, a1, a0, b0, _k, _6, _5, _4, _3, _2, \ + _1, _0, x0); \ + Eight_One_Sum(_k, _6, _5, _4, _3, _2, _1, _0, b1, x9, x8, x7, x6, x5, x4, \ + x3, x2, x1) + +#define Eight_Four_Sum(a7, a6, a5, a4, a3, a2, a1, a0, b4, b3, b1, b0, x11, \ + x10, x9, x8, x7, x6, x5, x4, x3, x2, x1, x0) \ + Eight_Two_Sum(a7, a6, a5, a4, a3, a2, a1, a0, b1, b0, _l, _6, _5, _4, _3, \ + _2, _1, _0, x1, x0); \ + Eight_Two_Sum(_l, _6, _5, _4, _3, _2, _1, _0, b4, b3, x11, x10, x9, x8, \ + x7, x6, x5, x4, x3, x2) + +/* Macros for multiplying expansions of various fixed lengths. */ + +#define Two_One_Product(a1, a0, b, x3, x2, x1, x0) \ + Split(b, bhi, blo); \ + Two_Product_Presplit(a0, b, bhi, blo, _i, x0); \ + Two_Product_Presplit(a1, b, bhi, blo, _j, _0); \ + Two_Sum(_i, _0, _k, x1); \ + Fast_Two_Sum(_j, _k, x3, x2) + +#define Four_One_Product(a3, a2, a1, a0, b, x7, x6, x5, x4, x3, x2, x1, x0) \ + Split(b, bhi, blo); \ + Two_Product_Presplit(a0, b, bhi, blo, _i, x0); \ + Two_Product_Presplit(a1, b, bhi, blo, _j, _0); \ + Two_Sum(_i, _0, _k, x1); \ + Fast_Two_Sum(_j, _k, _i, x2); \ + Two_Product_Presplit(a2, b, bhi, blo, _j, _0); \ + Two_Sum(_i, _0, _k, x3); \ + Fast_Two_Sum(_j, _k, _i, x4); \ + Two_Product_Presplit(a3, b, bhi, blo, _j, _0); \ + Two_Sum(_i, _0, _k, x5); \ + Fast_Two_Sum(_j, _k, x7, x6) + +#define Two_Two_Product(a1, a0, b1, b0, x7, x6, x5, x4, x3, x2, x1, x0) \ + Split(a0, a0hi, a0lo); \ + Split(b0, bhi, blo); \ + Two_Product_2Presplit(a0, a0hi, a0lo, b0, bhi, blo, _i, x0); \ + Split(a1, a1hi, a1lo); \ + Two_Product_2Presplit(a1, a1hi, a1lo, b0, bhi, blo, _j, _0); \ + Two_Sum(_i, _0, _k, _1); \ + Fast_Two_Sum(_j, _k, _l, _2); \ + Split(b1, bhi, blo); \ + Two_Product_2Presplit(a0, a0hi, a0lo, b1, bhi, blo, _i, _0); \ + Two_Sum(_1, _0, _k, x1); \ + Two_Sum(_2, _k, _j, _1); \ + Two_Sum(_l, _j, _m, _2); \ + Two_Product_2Presplit(a1, a1hi, a1lo, b1, bhi, blo, _j, _0); \ + Two_Sum(_i, _0, _n, _0); \ + Two_Sum(_1, _0, _i, x2); \ + Two_Sum(_2, _i, _k, _1); \ + Two_Sum(_m, _k, _l, _2); \ + Two_Sum(_j, _n, _k, _0); \ + Two_Sum(_1, _0, _j, x3); \ + Two_Sum(_2, _j, _i, _1); \ + Two_Sum(_l, _i, _m, _2); \ + Two_Sum(_1, _k, _i, x4); \ + Two_Sum(_2, _i, _k, x5); \ + Two_Sum(_m, _k, x7, x6) + +/* An expansion of length two can be squared more quickly than finding the */ +/* product of two different expansions of length two, and the result is */ +/* guaranteed to have no more than six (rather than eight) components. */ + +#define Two_Square(a1, a0, x5, x4, x3, x2, x1, x0) \ + Square(a0, _j, x0); \ + _0 = a0 + a0; \ + Two_Product(a1, _0, _k, _1); \ + Two_One_Sum(_k, _1, _j, _l, _2, x1); \ + Square(a1, _j, _1); \ + Two_Two_Sum(_j, _1, _l, _2, x5, x4, x3, x2) + +/* splitter = 2^ceiling(p / 2) + 1. Used to split floats in half. */ +static REAL splitter; +static REAL epsilon; /* = 2^(-p). Used to estimate roundoff errors. */ +/* A set of coefficients used to calculate maximum roundoff errors. */ +static REAL resulterrbound; +static REAL ccwerrboundA, ccwerrboundB, ccwerrboundC; +static REAL o3derrboundA, o3derrboundB, o3derrboundC; +static REAL iccerrboundA, iccerrboundB, iccerrboundC; +static REAL isperrboundA, isperrboundB, isperrboundC; + +/*****************************************************************************/ +/* */ +/* doubleprint() Print the bit representation of a double. */ +/* */ +/* Useful for debugging exact arithmetic routines. */ +/* */ +/*****************************************************************************/ + +/* +void doubleprint(number) +double number; +{ + unsigned long long no; + unsigned long long sign, expo; + int exponent; + int i, bottomi; + + no = *(unsigned long long *) &number; + sign = no & 0x8000000000000000ll; + expo = (no >> 52) & 0x7ffll; + exponent = (int) expo; + exponent = exponent - 1023; + if (sign) { + printf("-"); + } else { + printf(" "); + } + if (exponent == -1023) { + printf( + "0.0000000000000000000000000000000000000000000000000000_ ( )"); + } else { + printf("1."); + bottomi = -1; + for (i = 0; i < 52; i++) { + if (no & 0x0008000000000000ll) { + printf("1"); + bottomi = i; + } else { + printf("0"); + } + no <<= 1; + } + printf("_%d (%d)", exponent, exponent - 1 - bottomi); + } +} +*/ + +/*****************************************************************************/ +/* */ +/* floatprint() Print the bit representation of a float. */ +/* */ +/* Useful for debugging exact arithmetic routines. */ +/* */ +/*****************************************************************************/ + +/* +void floatprint(number) +float number; +{ + unsigned no; + unsigned sign, expo; + int exponent; + int i, bottomi; + + no = *(unsigned *) &number; + sign = no & 0x80000000; + expo = (no >> 23) & 0xff; + exponent = (int) expo; + exponent = exponent - 127; + if (sign) { + printf("-"); + } else { + printf(" "); + } + if (exponent == -127) { + printf("0.00000000000000000000000_ ( )"); + } else { + printf("1."); + bottomi = -1; + for (i = 0; i < 23; i++) { + if (no & 0x00400000) { + printf("1"); + bottomi = i; + } else { + printf("0"); + } + no <<= 1; + } + printf("_%3d (%3d)", exponent, exponent - 1 - bottomi); + } +} +*/ + +/*****************************************************************************/ +/* */ +/* expansion_print() Print the bit representation of an expansion. */ +/* */ +/* Useful for debugging exact arithmetic routines. */ +/* */ +/*****************************************************************************/ + +/* +void expansion_print(elen, e) +int elen; +REAL *e; +{ + int i; + + for (i = elen - 1; i >= 0; i--) { + REALPRINT(e[i]); + if (i > 0) { + printf(" +\n"); + } else { + printf("\n"); + } + } +} +*/ + +/*****************************************************************************/ +/* */ +/* doublerand() Generate a double with random 53-bit significand and a */ +/* random exponent in [0, 511]. */ +/* */ +/*****************************************************************************/ + +/* +double doublerand() +{ + double result; + double expo; + long a, b, c; + long i; + + a = random(); + b = random(); + c = random(); + result = (double) (a - 1073741824) * 8388608.0 + (double) (b >> 8); + for (i = 512, expo = 2; i <= 131072; i *= 2, expo = expo * expo) { + if (c & i) { + result *= expo; + } + } + return result; +} +*/ + +/*****************************************************************************/ +/* */ +/* narrowdoublerand() Generate a double with random 53-bit significand */ +/* and a random exponent in [0, 7]. */ +/* */ +/*****************************************************************************/ + +/* +double narrowdoublerand() +{ + double result; + double expo; + long a, b, c; + long i; + + a = random(); + b = random(); + c = random(); + result = (double) (a - 1073741824) * 8388608.0 + (double) (b >> 8); + for (i = 512, expo = 2; i <= 2048; i *= 2, expo = expo * expo) { + if (c & i) { + result *= expo; + } + } + return result; +} +*/ + +/*****************************************************************************/ +/* */ +/* uniformdoublerand() Generate a double with random 53-bit significand. */ +/* */ +/*****************************************************************************/ + +/* +double uniformdoublerand() +{ + double result; + long a, b; + + a = random(); + b = random(); + result = (double) (a - 1073741824) * 8388608.0 + (double) (b >> 8); + return result; +} +*/ + +/*****************************************************************************/ +/* */ +/* floatrand() Generate a float with random 24-bit significand and a */ +/* random exponent in [0, 63]. */ +/* */ +/*****************************************************************************/ + +/* +float floatrand() +{ + float result; + float expo; + long a, c; + long i; + + a = random(); + c = random(); + result = (float) ((a - 1073741824) >> 6); + for (i = 512, expo = 2; i <= 16384; i *= 2, expo = expo * expo) { + if (c & i) { + result *= expo; + } + } + return result; +} +*/ + +/*****************************************************************************/ +/* */ +/* narrowfloatrand() Generate a float with random 24-bit significand and */ +/* a random exponent in [0, 7]. */ +/* */ +/*****************************************************************************/ + +/* +float narrowfloatrand() +{ + float result; + float expo; + long a, c; + long i; + + a = random(); + c = random(); + result = (float) ((a - 1073741824) >> 6); + for (i = 512, expo = 2; i <= 2048; i *= 2, expo = expo * expo) { + if (c & i) { + result *= expo; + } + } + return result; +} +*/ + +/*****************************************************************************/ +/* */ +/* uniformfloatrand() Generate a float with random 24-bit significand. */ +/* */ +/*****************************************************************************/ + +/* +float uniformfloatrand() +{ + float result; + long a; + + a = random(); + result = (float) ((a - 1073741824) >> 6); + return result; +} +*/ + +/*****************************************************************************/ +/* */ +/* exactinit() Initialize the variables used for exact arithmetic. */ +/* */ +/* `epsilon' is the largest power of two such that 1.0 + epsilon = 1.0 in */ +/* floating-point arithmetic. `epsilon' bounds the relative roundoff */ +/* error. It is used for floating-point error analysis. */ +/* */ +/* `splitter' is used to split floating-point numbers into two half- */ +/* length significands for exact multiplication. */ +/* */ +/* I imagine that a highly optimizing compiler might be too smart for its */ +/* own good, and somehow cause this routine to fail, if it pretends that */ +/* floating-point arithmetic is too much like real arithmetic. */ +/* */ +/* Don't change this routine unless you fully understand it. */ +/* */ +/*****************************************************************************/ + +REAL exactinit() +{ + REAL half; + REAL check, lastcheck; + int every_other; +#ifdef LINUX + int cword; +#endif /* LINUX */ + +#ifdef CPU86 +#ifdef SINGLE + _control87(_PC_24, _MCW_PC); /* Set FPU control word for single precision. */ +#else /* not SINGLE */ + _control87(_PC_53, _MCW_PC); /* Set FPU control word for double precision. */ +#endif /* not SINGLE */ +#endif /* CPU86 */ +#ifdef LINUX +#ifdef SINGLE + /* cword = 4223; */ + cword = 4210; /* set FPU control word for single precision */ +#else /* not SINGLE */ + /* cword = 4735; */ + cword = 4722; /* set FPU control word for double precision */ +#endif /* not SINGLE */ + _FPU_SETCW(cword); +#endif /* LINUX */ + + every_other = 1; + half = 0.5; + epsilon = 1.0; + splitter = 1.0; + check = 1.0; + /* Repeatedly divide `epsilon' by two until it is too small to add to */ + /* one without causing roundoff. (Also check if the sum is equal to */ + /* the previous sum, for machines that round up instead of using exact */ + /* rounding. Not that this library will work on such machines anyway. */ + do { + lastcheck = check; + epsilon *= half; + if (every_other) { + splitter *= 2.0; + } + every_other = !every_other; + check = 1.0 + epsilon; + } while ((check != 1.0) && (check != lastcheck)); + splitter += 1.0; + + /* Error bounds for orientation and incircle tests. */ + resulterrbound = (3.0 + 8.0 * epsilon) * epsilon; + ccwerrboundA = (3.0 + 16.0 * epsilon) * epsilon; + ccwerrboundB = (2.0 + 12.0 * epsilon) * epsilon; + ccwerrboundC = (9.0 + 64.0 * epsilon) * epsilon * epsilon; + o3derrboundA = (7.0 + 56.0 * epsilon) * epsilon; + o3derrboundB = (3.0 + 28.0 * epsilon) * epsilon; + o3derrboundC = (26.0 + 288.0 * epsilon) * epsilon * epsilon; + iccerrboundA = (10.0 + 96.0 * epsilon) * epsilon; + iccerrboundB = (4.0 + 48.0 * epsilon) * epsilon; + iccerrboundC = (44.0 + 576.0 * epsilon) * epsilon * epsilon; + isperrboundA = (16.0 + 224.0 * epsilon) * epsilon; + isperrboundB = (5.0 + 72.0 * epsilon) * epsilon; + isperrboundC = (71.0 + 1408.0 * epsilon) * epsilon * epsilon; + + return epsilon; /* Added by H. Si 30 Juli, 2004. */ +} + +/*****************************************************************************/ +/* */ +/* grow_expansion() Add a scalar to an expansion. */ +/* */ +/* Sets h = e + b. See the long version of my paper for details. */ +/* */ +/* Maintains the nonoverlapping property. If round-to-even is used (as */ +/* with IEEE 754), maintains the strongly nonoverlapping and nonadjacent */ +/* properties as well. (That is, if e has one of these properties, so */ +/* will h.) */ +/* */ +/*****************************************************************************/ + +int grow_expansion(int elen, REAL *e, REAL b, REAL *h) +/* e and h can be the same. */ +{ + REAL Q; + INEXACT REAL Qnew; + int eindex; + REAL enow; + INEXACT REAL bvirt; + REAL avirt, bround, around; + + Q = b; + for (eindex = 0; eindex < elen; eindex++) { + enow = e[eindex]; + Two_Sum(Q, enow, Qnew, h[eindex]); + Q = Qnew; + } + h[eindex] = Q; + return eindex + 1; +} + +/*****************************************************************************/ +/* */ +/* grow_expansion_zeroelim() Add a scalar to an expansion, eliminating */ +/* zero components from the output expansion. */ +/* */ +/* Sets h = e + b. See the long version of my paper for details. */ +/* */ +/* Maintains the nonoverlapping property. If round-to-even is used (as */ +/* with IEEE 754), maintains the strongly nonoverlapping and nonadjacent */ +/* properties as well. (That is, if e has one of these properties, so */ +/* will h.) */ +/* */ +/*****************************************************************************/ + +int grow_expansion_zeroelim(int elen, REAL *e, REAL b, REAL *h) +/* e and h can be the same. */ +{ + REAL Q, hh; + INEXACT REAL Qnew; + int eindex, hindex; + REAL enow; + INEXACT REAL bvirt; + REAL avirt, bround, around; + + hindex = 0; + Q = b; + for (eindex = 0; eindex < elen; eindex++) { + enow = e[eindex]; + Two_Sum(Q, enow, Qnew, hh); + Q = Qnew; + if (hh != 0.0) { + h[hindex++] = hh; + } + } + if ((Q != 0.0) || (hindex == 0)) { + h[hindex++] = Q; + } + return hindex; +} + +/*****************************************************************************/ +/* */ +/* expansion_sum() Sum two expansions. */ +/* */ +/* Sets h = e + f. See the long version of my paper for details. */ +/* */ +/* Maintains the nonoverlapping property. If round-to-even is used (as */ +/* with IEEE 754), maintains the nonadjacent property as well. (That is, */ +/* if e has one of these properties, so will h.) Does NOT maintain the */ +/* strongly nonoverlapping property. */ +/* */ +/*****************************************************************************/ + +int expansion_sum(int elen, REAL *e, int flen, REAL *f, REAL *h) +/* e and h can be the same, but f and h cannot. */ +{ + REAL Q; + INEXACT REAL Qnew; + int findex, hindex, hlast; + REAL hnow; + INEXACT REAL bvirt; + REAL avirt, bround, around; + + Q = f[0]; + for (hindex = 0; hindex < elen; hindex++) { + hnow = e[hindex]; + Two_Sum(Q, hnow, Qnew, h[hindex]); + Q = Qnew; + } + h[hindex] = Q; + hlast = hindex; + for (findex = 1; findex < flen; findex++) { + Q = f[findex]; + for (hindex = findex; hindex <= hlast; hindex++) { + hnow = h[hindex]; + Two_Sum(Q, hnow, Qnew, h[hindex]); + Q = Qnew; + } + h[++hlast] = Q; + } + return hlast + 1; +} + +/*****************************************************************************/ +/* */ +/* expansion_sum_zeroelim1() Sum two expansions, eliminating zero */ +/* components from the output expansion. */ +/* */ +/* Sets h = e + f. See the long version of my paper for details. */ +/* */ +/* Maintains the nonoverlapping property. If round-to-even is used (as */ +/* with IEEE 754), maintains the nonadjacent property as well. (That is, */ +/* if e has one of these properties, so will h.) Does NOT maintain the */ +/* strongly nonoverlapping property. */ +/* */ +/*****************************************************************************/ + +int expansion_sum_zeroelim1(int elen, REAL *e, int flen, REAL *f, REAL *h) +/* e and h can be the same, but f and h cannot. */ +{ + REAL Q; + INEXACT REAL Qnew; + int index, findex, hindex, hlast; + REAL hnow; + INEXACT REAL bvirt; + REAL avirt, bround, around; + + Q = f[0]; + for (hindex = 0; hindex < elen; hindex++) { + hnow = e[hindex]; + Two_Sum(Q, hnow, Qnew, h[hindex]); + Q = Qnew; + } + h[hindex] = Q; + hlast = hindex; + for (findex = 1; findex < flen; findex++) { + Q = f[findex]; + for (hindex = findex; hindex <= hlast; hindex++) { + hnow = h[hindex]; + Two_Sum(Q, hnow, Qnew, h[hindex]); + Q = Qnew; + } + h[++hlast] = Q; + } + hindex = -1; + for (index = 0; index <= hlast; index++) { + hnow = h[index]; + if (hnow != 0.0) { + h[++hindex] = hnow; + } + } + if (hindex == -1) { + return 1; + } else { + return hindex + 1; + } +} + +/*****************************************************************************/ +/* */ +/* expansion_sum_zeroelim2() Sum two expansions, eliminating zero */ +/* components from the output expansion. */ +/* */ +/* Sets h = e + f. See the long version of my paper for details. */ +/* */ +/* Maintains the nonoverlapping property. If round-to-even is used (as */ +/* with IEEE 754), maintains the nonadjacent property as well. (That is, */ +/* if e has one of these properties, so will h.) Does NOT maintain the */ +/* strongly nonoverlapping property. */ +/* */ +/*****************************************************************************/ + +int expansion_sum_zeroelim2(int elen, REAL *e, int flen, REAL *f, REAL *h) +/* e and h can be the same, but f and h cannot. */ +{ + REAL Q, hh; + INEXACT REAL Qnew; + int eindex, findex, hindex, hlast; + REAL enow; + INEXACT REAL bvirt; + REAL avirt, bround, around; + + hindex = 0; + Q = f[0]; + for (eindex = 0; eindex < elen; eindex++) { + enow = e[eindex]; + Two_Sum(Q, enow, Qnew, hh); + Q = Qnew; + if (hh != 0.0) { + h[hindex++] = hh; + } + } + h[hindex] = Q; + hlast = hindex; + for (findex = 1; findex < flen; findex++) { + hindex = 0; + Q = f[findex]; + for (eindex = 0; eindex <= hlast; eindex++) { + enow = h[eindex]; + Two_Sum(Q, enow, Qnew, hh); + Q = Qnew; + if (hh != 0) { + h[hindex++] = hh; + } + } + h[hindex] = Q; + hlast = hindex; + } + return hlast + 1; +} + +/*****************************************************************************/ +/* */ +/* fast_expansion_sum() Sum two expansions. */ +/* */ +/* Sets h = e + f. See the long version of my paper for details. */ +/* */ +/* If round-to-even is used (as with IEEE 754), maintains the strongly */ +/* nonoverlapping property. (That is, if e is strongly nonoverlapping, h */ +/* will be also.) Does NOT maintain the nonoverlapping or nonadjacent */ +/* properties. */ +/* */ +/*****************************************************************************/ + +int fast_expansion_sum(int elen, REAL *e, int flen, REAL *f, REAL *h) +/* h cannot be e or f. */ +{ + REAL Q; + INEXACT REAL Qnew; + INEXACT REAL bvirt; + REAL avirt, bround, around; + int eindex, findex, hindex; + REAL enow, fnow; + + enow = e[0]; + fnow = f[0]; + eindex = findex = 0; + if ((fnow > enow) == (fnow > -enow)) { + Q = enow; + enow = e[++eindex]; + } else { + Q = fnow; + fnow = f[++findex]; + } + hindex = 0; + if ((eindex < elen) && (findex < flen)) { + if ((fnow > enow) == (fnow > -enow)) { + Fast_Two_Sum(enow, Q, Qnew, h[0]); + enow = e[++eindex]; + } else { + Fast_Two_Sum(fnow, Q, Qnew, h[0]); + fnow = f[++findex]; + } + Q = Qnew; + hindex = 1; + while ((eindex < elen) && (findex < flen)) { + if ((fnow > enow) == (fnow > -enow)) { + Two_Sum(Q, enow, Qnew, h[hindex]); + enow = e[++eindex]; + } else { + Two_Sum(Q, fnow, Qnew, h[hindex]); + fnow = f[++findex]; + } + Q = Qnew; + hindex++; + } + } + while (eindex < elen) { + Two_Sum(Q, enow, Qnew, h[hindex]); + enow = e[++eindex]; + Q = Qnew; + hindex++; + } + while (findex < flen) { + Two_Sum(Q, fnow, Qnew, h[hindex]); + fnow = f[++findex]; + Q = Qnew; + hindex++; + } + h[hindex] = Q; + return hindex + 1; +} + +/*****************************************************************************/ +/* */ +/* fast_expansion_sum_zeroelim() Sum two expansions, eliminating zero */ +/* components from the output expansion. */ +/* */ +/* Sets h = e + f. See the long version of my paper for details. */ +/* */ +/* If round-to-even is used (as with IEEE 754), maintains the strongly */ +/* nonoverlapping property. (That is, if e is strongly nonoverlapping, h */ +/* will be also.) Does NOT maintain the nonoverlapping or nonadjacent */ +/* properties. */ +/* */ +/*****************************************************************************/ + +int fast_expansion_sum_zeroelim(int elen, REAL *e, int flen, REAL *f, REAL *h) +/* h cannot be e or f. */ +{ + REAL Q; + INEXACT REAL Qnew; + INEXACT REAL hh; + INEXACT REAL bvirt; + REAL avirt, bround, around; + int eindex, findex, hindex; + REAL enow, fnow; + + enow = e[0]; + fnow = f[0]; + eindex = findex = 0; + if ((fnow > enow) == (fnow > -enow)) { + Q = enow; + enow = e[++eindex]; + } else { + Q = fnow; + fnow = f[++findex]; + } + hindex = 0; + if ((eindex < elen) && (findex < flen)) { + if ((fnow > enow) == (fnow > -enow)) { + Fast_Two_Sum(enow, Q, Qnew, hh); + enow = e[++eindex]; + } else { + Fast_Two_Sum(fnow, Q, Qnew, hh); + fnow = f[++findex]; + } + Q = Qnew; + if (hh != 0.0) { + h[hindex++] = hh; + } + while ((eindex < elen) && (findex < flen)) { + if ((fnow > enow) == (fnow > -enow)) { + Two_Sum(Q, enow, Qnew, hh); + enow = e[++eindex]; + } else { + Two_Sum(Q, fnow, Qnew, hh); + fnow = f[++findex]; + } + Q = Qnew; + if (hh != 0.0) { + h[hindex++] = hh; + } + } + } + while (eindex < elen) { + Two_Sum(Q, enow, Qnew, hh); + enow = e[++eindex]; + Q = Qnew; + if (hh != 0.0) { + h[hindex++] = hh; + } + } + while (findex < flen) { + Two_Sum(Q, fnow, Qnew, hh); + fnow = f[++findex]; + Q = Qnew; + if (hh != 0.0) { + h[hindex++] = hh; + } + } + if ((Q != 0.0) || (hindex == 0)) { + h[hindex++] = Q; + } + return hindex; +} + +/*****************************************************************************/ +/* */ +/* linear_expansion_sum() Sum two expansions. */ +/* */ +/* Sets h = e + f. See either version of my paper for details. */ +/* */ +/* Maintains the nonoverlapping property. (That is, if e is */ +/* nonoverlapping, h will be also.) */ +/* */ +/*****************************************************************************/ + +int linear_expansion_sum(int elen, REAL *e, int flen, REAL *f, REAL *h) +/* h cannot be e or f. */ +{ + REAL Q, q; + INEXACT REAL Qnew; + INEXACT REAL R; + INEXACT REAL bvirt; + REAL avirt, bround, around; + int eindex, findex, hindex; + REAL enow, fnow; + REAL g0; + + enow = e[0]; + fnow = f[0]; + eindex = findex = 0; + if ((fnow > enow) == (fnow > -enow)) { + g0 = enow; + enow = e[++eindex]; + } else { + g0 = fnow; + fnow = f[++findex]; + } + if ((eindex < elen) && ((findex >= flen) + || ((fnow > enow) == (fnow > -enow)))) { + Fast_Two_Sum(enow, g0, Qnew, q); + enow = e[++eindex]; + } else { + Fast_Two_Sum(fnow, g0, Qnew, q); + fnow = f[++findex]; + } + Q = Qnew; + for (hindex = 0; hindex < elen + flen - 2; hindex++) { + if ((eindex < elen) && ((findex >= flen) + || ((fnow > enow) == (fnow > -enow)))) { + Fast_Two_Sum(enow, q, R, h[hindex]); + enow = e[++eindex]; + } else { + Fast_Two_Sum(fnow, q, R, h[hindex]); + fnow = f[++findex]; + } + Two_Sum(Q, R, Qnew, q); + Q = Qnew; + } + h[hindex] = q; + h[hindex + 1] = Q; + return hindex + 2; +} + +/*****************************************************************************/ +/* */ +/* linear_expansion_sum_zeroelim() Sum two expansions, eliminating zero */ +/* components from the output expansion. */ +/* */ +/* Sets h = e + f. See either version of my paper for details. */ +/* */ +/* Maintains the nonoverlapping property. (That is, if e is */ +/* nonoverlapping, h will be also.) */ +/* */ +/*****************************************************************************/ + +int linear_expansion_sum_zeroelim(int elen, REAL *e, int flen, REAL *f, + REAL *h) +/* h cannot be e or f. */ +{ + REAL Q, q, hh; + INEXACT REAL Qnew; + INEXACT REAL R; + INEXACT REAL bvirt; + REAL avirt, bround, around; + int eindex, findex, hindex; + int count; + REAL enow, fnow; + REAL g0; + + enow = e[0]; + fnow = f[0]; + eindex = findex = 0; + hindex = 0; + if ((fnow > enow) == (fnow > -enow)) { + g0 = enow; + enow = e[++eindex]; + } else { + g0 = fnow; + fnow = f[++findex]; + } + if ((eindex < elen) && ((findex >= flen) + || ((fnow > enow) == (fnow > -enow)))) { + Fast_Two_Sum(enow, g0, Qnew, q); + enow = e[++eindex]; + } else { + Fast_Two_Sum(fnow, g0, Qnew, q); + fnow = f[++findex]; + } + Q = Qnew; + for (count = 2; count < elen + flen; count++) { + if ((eindex < elen) && ((findex >= flen) + || ((fnow > enow) == (fnow > -enow)))) { + Fast_Two_Sum(enow, q, R, hh); + enow = e[++eindex]; + } else { + Fast_Two_Sum(fnow, q, R, hh); + fnow = f[++findex]; + } + Two_Sum(Q, R, Qnew, q); + Q = Qnew; + if (hh != 0) { + h[hindex++] = hh; + } + } + if (q != 0) { + h[hindex++] = q; + } + if ((Q != 0.0) || (hindex == 0)) { + h[hindex++] = Q; + } + return hindex; +} + +/*****************************************************************************/ +/* */ +/* scale_expansion() Multiply an expansion by a scalar. */ +/* */ +/* Sets h = be. See either version of my paper for details. */ +/* */ +/* Maintains the nonoverlapping property. If round-to-even is used (as */ +/* with IEEE 754), maintains the strongly nonoverlapping and nonadjacent */ +/* properties as well. (That is, if e has one of these properties, so */ +/* will h.) */ +/* */ +/*****************************************************************************/ + +int scale_expansion(int elen, REAL *e, REAL b, REAL *h) +/* e and h cannot be the same. */ +{ + INEXACT REAL Q; + INEXACT REAL sum; + INEXACT REAL product1; + REAL product0; + int eindex, hindex; + REAL enow; + INEXACT REAL bvirt; + REAL avirt, bround, around; + INEXACT REAL c; + INEXACT REAL abig; + REAL ahi, alo, bhi, blo; + REAL err1, err2, err3; + + Split(b, bhi, blo); + Two_Product_Presplit(e[0], b, bhi, blo, Q, h[0]); + hindex = 1; + for (eindex = 1; eindex < elen; eindex++) { + enow = e[eindex]; + Two_Product_Presplit(enow, b, bhi, blo, product1, product0); + Two_Sum(Q, product0, sum, h[hindex]); + hindex++; + Two_Sum(product1, sum, Q, h[hindex]); + hindex++; + } + h[hindex] = Q; + return elen + elen; +} + +/*****************************************************************************/ +/* */ +/* scale_expansion_zeroelim() Multiply an expansion by a scalar, */ +/* eliminating zero components from the */ +/* output expansion. */ +/* */ +/* Sets h = be. See either version of my paper for details. */ +/* */ +/* Maintains the nonoverlapping property. If round-to-even is used (as */ +/* with IEEE 754), maintains the strongly nonoverlapping and nonadjacent */ +/* properties as well. (That is, if e has one of these properties, so */ +/* will h.) */ +/* */ +/*****************************************************************************/ + +int scale_expansion_zeroelim(int elen, REAL *e, REAL b, REAL *h) +/* e and h cannot be the same. */ +{ + INEXACT REAL Q, sum; + REAL hh; + INEXACT REAL product1; + REAL product0; + int eindex, hindex; + REAL enow; + INEXACT REAL bvirt; + REAL avirt, bround, around; + INEXACT REAL c; + INEXACT REAL abig; + REAL ahi, alo, bhi, blo; + REAL err1, err2, err3; + + Split(b, bhi, blo); + Two_Product_Presplit(e[0], b, bhi, blo, Q, hh); + hindex = 0; + if (hh != 0) { + h[hindex++] = hh; + } + for (eindex = 1; eindex < elen; eindex++) { + enow = e[eindex]; + Two_Product_Presplit(enow, b, bhi, blo, product1, product0); + Two_Sum(Q, product0, sum, hh); + if (hh != 0) { + h[hindex++] = hh; + } + Fast_Two_Sum(product1, sum, Q, hh); + if (hh != 0) { + h[hindex++] = hh; + } + } + if ((Q != 0.0) || (hindex == 0)) { + h[hindex++] = Q; + } + return hindex; +} + +/*****************************************************************************/ +/* */ +/* compress() Compress an expansion. */ +/* */ +/* See the long version of my paper for details. */ +/* */ +/* Maintains the nonoverlapping property. If round-to-even is used (as */ +/* with IEEE 754), then any nonoverlapping expansion is converted to a */ +/* nonadjacent expansion. */ +/* */ +/*****************************************************************************/ + +int compress(int elen, REAL *e, REAL *h) +/* e and h may be the same. */ +{ + REAL Q, q; + INEXACT REAL Qnew; + int eindex, hindex; + INEXACT REAL bvirt; + REAL enow, hnow; + int top, bottom; + + bottom = elen - 1; + Q = e[bottom]; + for (eindex = elen - 2; eindex >= 0; eindex--) { + enow = e[eindex]; + Fast_Two_Sum(Q, enow, Qnew, q); + if (q != 0) { + h[bottom--] = Qnew; + Q = q; + } else { + Q = Qnew; + } + } + top = 0; + for (hindex = bottom + 1; hindex < elen; hindex++) { + hnow = h[hindex]; + Fast_Two_Sum(hnow, Q, Qnew, q); + if (q != 0) { + h[top++] = q; + } + Q = Qnew; + } + h[top] = Q; + return top + 1; +} + +/*****************************************************************************/ +/* */ +/* estimate() Produce a one-word estimate of an expansion's value. */ +/* */ +/* See either version of my paper for details. */ +/* */ +/*****************************************************************************/ + +REAL estimate(int elen, REAL *e) +{ + REAL Q; + int eindex; + + Q = e[0]; + for (eindex = 1; eindex < elen; eindex++) { + Q += e[eindex]; + } + return Q; +} + +/*****************************************************************************/ +/* */ +/* orient2dfast() Approximate 2D orientation test. Nonrobust. */ +/* orient2dexact() Exact 2D orientation test. Robust. */ +/* orient2dslow() Another exact 2D orientation test. Robust. */ +/* orient2d() Adaptive exact 2D orientation test. Robust. */ +/* */ +/* Return a positive value if the points pa, pb, and pc occur */ +/* in counterclockwise order; a negative value if they occur */ +/* in clockwise order; and zero if they are collinear. The */ +/* result is also a rough approximation of twice the signed */ +/* area of the triangle defined by the three points. */ +/* */ +/* Only the first and last routine should be used; the middle two are for */ +/* timings. */ +/* */ +/* The last three use exact arithmetic to ensure a correct answer. The */ +/* result returned is the determinant of a matrix. In orient2d() only, */ +/* this determinant is computed adaptively, in the sense that exact */ +/* arithmetic is used only to the degree it is needed to ensure that the */ +/* returned value has the correct sign. Hence, orient2d() is usually quite */ +/* fast, but will run more slowly when the input points are collinear or */ +/* nearly so. */ +/* */ +/*****************************************************************************/ + +REAL orient2dfast(REAL *pa, REAL *pb, REAL *pc) +{ + REAL acx, bcx, acy, bcy; + + acx = pa[0] - pc[0]; + bcx = pb[0] - pc[0]; + acy = pa[1] - pc[1]; + bcy = pb[1] - pc[1]; + return acx * bcy - acy * bcx; +} + +REAL orient2dexact(REAL *pa, REAL *pb, REAL *pc) +{ + INEXACT REAL axby1, axcy1, bxcy1, bxay1, cxay1, cxby1; + REAL axby0, axcy0, bxcy0, bxay0, cxay0, cxby0; + REAL aterms[4], bterms[4], cterms[4]; + INEXACT REAL aterms3, bterms3, cterms3; + REAL v[8], w[12]; + int vlength, wlength; + + INEXACT REAL bvirt; + REAL avirt, bround, around; + INEXACT REAL c; + INEXACT REAL abig; + REAL ahi, alo, bhi, blo; + REAL err1, err2, err3; + INEXACT REAL _i, _j; + REAL _0; + + Two_Product(pa[0], pb[1], axby1, axby0); + Two_Product(pa[0], pc[1], axcy1, axcy0); + Two_Two_Diff(axby1, axby0, axcy1, axcy0, + aterms3, aterms[2], aterms[1], aterms[0]); + aterms[3] = aterms3; + + Two_Product(pb[0], pc[1], bxcy1, bxcy0); + Two_Product(pb[0], pa[1], bxay1, bxay0); + Two_Two_Diff(bxcy1, bxcy0, bxay1, bxay0, + bterms3, bterms[2], bterms[1], bterms[0]); + bterms[3] = bterms3; + + Two_Product(pc[0], pa[1], cxay1, cxay0); + Two_Product(pc[0], pb[1], cxby1, cxby0); + Two_Two_Diff(cxay1, cxay0, cxby1, cxby0, + cterms3, cterms[2], cterms[1], cterms[0]); + cterms[3] = cterms3; + + vlength = fast_expansion_sum_zeroelim(4, aterms, 4, bterms, v); + wlength = fast_expansion_sum_zeroelim(vlength, v, 4, cterms, w); + + return w[wlength - 1]; +} + +REAL orient2dslow(REAL *pa, REAL *pb, REAL *pc) +{ + INEXACT REAL acx, acy, bcx, bcy; + REAL acxtail, acytail; + REAL bcxtail, bcytail; + REAL negate, negatetail; + REAL axby[8], bxay[8]; + INEXACT REAL axby7, bxay7; + REAL deter[16]; + int deterlen; + + INEXACT REAL bvirt; + REAL avirt, bround, around; + INEXACT REAL c; + INEXACT REAL abig; + REAL a0hi, a0lo, a1hi, a1lo, bhi, blo; + REAL err1, err2, err3; + INEXACT REAL _i, _j, _k, _l, _m, _n; + REAL _0, _1, _2; + + Two_Diff(pa[0], pc[0], acx, acxtail); + Two_Diff(pa[1], pc[1], acy, acytail); + Two_Diff(pb[0], pc[0], bcx, bcxtail); + Two_Diff(pb[1], pc[1], bcy, bcytail); + + Two_Two_Product(acx, acxtail, bcy, bcytail, + axby7, axby[6], axby[5], axby[4], + axby[3], axby[2], axby[1], axby[0]); + axby[7] = axby7; + negate = -acy; + negatetail = -acytail; + Two_Two_Product(bcx, bcxtail, negate, negatetail, + bxay7, bxay[6], bxay[5], bxay[4], + bxay[3], bxay[2], bxay[1], bxay[0]); + bxay[7] = bxay7; + + deterlen = fast_expansion_sum_zeroelim(8, axby, 8, bxay, deter); + + return deter[deterlen - 1]; +} + +REAL orient2dadapt(REAL *pa, REAL *pb, REAL *pc, REAL detsum) +{ + INEXACT REAL acx, acy, bcx, bcy; + REAL acxtail, acytail, bcxtail, bcytail; + INEXACT REAL detleft, detright; + REAL detlefttail, detrighttail; + REAL det, errbound; + REAL B[4], C1[8], C2[12], D[16]; + INEXACT REAL B3; + int C1length, C2length, Dlength; + REAL u[4]; + INEXACT REAL u3; + INEXACT REAL s1, t1; + REAL s0, t0; + + INEXACT REAL bvirt; + REAL avirt, bround, around; + INEXACT REAL c; + INEXACT REAL abig; + REAL ahi, alo, bhi, blo; + REAL err1, err2, err3; + INEXACT REAL _i, _j; + REAL _0; + + acx = (REAL) (pa[0] - pc[0]); + bcx = (REAL) (pb[0] - pc[0]); + acy = (REAL) (pa[1] - pc[1]); + bcy = (REAL) (pb[1] - pc[1]); + + Two_Product(acx, bcy, detleft, detlefttail); + Two_Product(acy, bcx, detright, detrighttail); + + Two_Two_Diff(detleft, detlefttail, detright, detrighttail, + B3, B[2], B[1], B[0]); + B[3] = B3; + + det = estimate(4, B); + errbound = ccwerrboundB * detsum; + if ((det >= errbound) || (-det >= errbound)) { + return det; + } + + Two_Diff_Tail(pa[0], pc[0], acx, acxtail); + Two_Diff_Tail(pb[0], pc[0], bcx, bcxtail); + Two_Diff_Tail(pa[1], pc[1], acy, acytail); + Two_Diff_Tail(pb[1], pc[1], bcy, bcytail); + + if ((acxtail == 0.0) && (acytail == 0.0) + && (bcxtail == 0.0) && (bcytail == 0.0)) { + return det; + } + + errbound = ccwerrboundC * detsum + resulterrbound * Absolute(det); + det += (acx * bcytail + bcy * acxtail) + - (acy * bcxtail + bcx * acytail); + if ((det >= errbound) || (-det >= errbound)) { + return det; + } + + Two_Product(acxtail, bcy, s1, s0); + Two_Product(acytail, bcx, t1, t0); + Two_Two_Diff(s1, s0, t1, t0, u3, u[2], u[1], u[0]); + u[3] = u3; + C1length = fast_expansion_sum_zeroelim(4, B, 4, u, C1); + + Two_Product(acx, bcytail, s1, s0); + Two_Product(acy, bcxtail, t1, t0); + Two_Two_Diff(s1, s0, t1, t0, u3, u[2], u[1], u[0]); + u[3] = u3; + C2length = fast_expansion_sum_zeroelim(C1length, C1, 4, u, C2); + + Two_Product(acxtail, bcytail, s1, s0); + Two_Product(acytail, bcxtail, t1, t0); + Two_Two_Diff(s1, s0, t1, t0, u3, u[2], u[1], u[0]); + u[3] = u3; + Dlength = fast_expansion_sum_zeroelim(C2length, C2, 4, u, D); + + return(D[Dlength - 1]); +} + +REAL orient2d(REAL *pa, REAL *pb, REAL *pc) +{ + REAL detleft, detright, det; + REAL detsum, errbound; + + detleft = (pa[0] - pc[0]) * (pb[1] - pc[1]); + detright = (pa[1] - pc[1]) * (pb[0] - pc[0]); + det = detleft - detright; + + if (detleft > 0.0) { + if (detright <= 0.0) { + return det; + } else { + detsum = detleft + detright; + } + } else if (detleft < 0.0) { + if (detright >= 0.0) { + return det; + } else { + detsum = -detleft - detright; + } + } else { + return det; + } + + errbound = ccwerrboundA * detsum; + if ((det >= errbound) || (-det >= errbound)) { + return det; + } + + return orient2dadapt(pa, pb, pc, detsum); +} + +/*****************************************************************************/ +/* */ +/* orient3dfast() Approximate 3D orientation test. Nonrobust. */ +/* orient3dexact() Exact 3D orientation test. Robust. */ +/* orient3dslow() Another exact 3D orientation test. Robust. */ +/* orient3d() Adaptive exact 3D orientation test. Robust. */ +/* */ +/* Return a positive value if the point pd lies below the */ +/* plane passing through pa, pb, and pc; "below" is defined so */ +/* that pa, pb, and pc appear in counterclockwise order when */ +/* viewed from above the plane. Returns a negative value if */ +/* pd lies above the plane. Returns zero if the points are */ +/* coplanar. The result is also a rough approximation of six */ +/* times the signed volume of the tetrahedron defined by the */ +/* four points. */ +/* */ +/* Only the first and last routine should be used; the middle two are for */ +/* timings. */ +/* */ +/* The last three use exact arithmetic to ensure a correct answer. The */ +/* result returned is the determinant of a matrix. In orient3d() only, */ +/* this determinant is computed adaptively, in the sense that exact */ +/* arithmetic is used only to the degree it is needed to ensure that the */ +/* returned value has the correct sign. Hence, orient3d() is usually quite */ +/* fast, but will run more slowly when the input points are coplanar or */ +/* nearly so. */ +/* */ +/*****************************************************************************/ + +REAL orient3dfast(REAL *pa, REAL *pb, REAL *pc, REAL *pd) +{ + REAL adx, bdx, cdx; + REAL ady, bdy, cdy; + REAL adz, bdz, cdz; + + adx = pa[0] - pd[0]; + bdx = pb[0] - pd[0]; + cdx = pc[0] - pd[0]; + ady = pa[1] - pd[1]; + bdy = pb[1] - pd[1]; + cdy = pc[1] - pd[1]; + adz = pa[2] - pd[2]; + bdz = pb[2] - pd[2]; + cdz = pc[2] - pd[2]; + + return adx * (bdy * cdz - bdz * cdy) + + bdx * (cdy * adz - cdz * ady) + + cdx * (ady * bdz - adz * bdy); +} + +REAL orient3dexact(REAL *pa, REAL *pb, REAL *pc, REAL *pd) +{ + INEXACT REAL axby1, bxcy1, cxdy1, dxay1, axcy1, bxdy1; + INEXACT REAL bxay1, cxby1, dxcy1, axdy1, cxay1, dxby1; + REAL axby0, bxcy0, cxdy0, dxay0, axcy0, bxdy0; + REAL bxay0, cxby0, dxcy0, axdy0, cxay0, dxby0; + REAL ab[4], bc[4], cd[4], da[4], ac[4], bd[4]; + REAL temp8[8]; + int templen; + REAL abc[12], bcd[12], cda[12], dab[12]; + int abclen, bcdlen, cdalen, dablen; + REAL adet[24], bdet[24], cdet[24], ddet[24]; + int alen, blen, clen, dlen; + REAL abdet[48], cddet[48]; + int ablen, cdlen; + REAL deter[96]; + int deterlen; + int i; + + INEXACT REAL bvirt; + REAL avirt, bround, around; + INEXACT REAL c; + INEXACT REAL abig; + REAL ahi, alo, bhi, blo; + REAL err1, err2, err3; + INEXACT REAL _i, _j; + REAL _0; + + Two_Product(pa[0], pb[1], axby1, axby0); + Two_Product(pb[0], pa[1], bxay1, bxay0); + Two_Two_Diff(axby1, axby0, bxay1, bxay0, ab[3], ab[2], ab[1], ab[0]); + + Two_Product(pb[0], pc[1], bxcy1, bxcy0); + Two_Product(pc[0], pb[1], cxby1, cxby0); + Two_Two_Diff(bxcy1, bxcy0, cxby1, cxby0, bc[3], bc[2], bc[1], bc[0]); + + Two_Product(pc[0], pd[1], cxdy1, cxdy0); + Two_Product(pd[0], pc[1], dxcy1, dxcy0); + Two_Two_Diff(cxdy1, cxdy0, dxcy1, dxcy0, cd[3], cd[2], cd[1], cd[0]); + + Two_Product(pd[0], pa[1], dxay1, dxay0); + Two_Product(pa[0], pd[1], axdy1, axdy0); + Two_Two_Diff(dxay1, dxay0, axdy1, axdy0, da[3], da[2], da[1], da[0]); + + Two_Product(pa[0], pc[1], axcy1, axcy0); + Two_Product(pc[0], pa[1], cxay1, cxay0); + Two_Two_Diff(axcy1, axcy0, cxay1, cxay0, ac[3], ac[2], ac[1], ac[0]); + + Two_Product(pb[0], pd[1], bxdy1, bxdy0); + Two_Product(pd[0], pb[1], dxby1, dxby0); + Two_Two_Diff(bxdy1, bxdy0, dxby1, dxby0, bd[3], bd[2], bd[1], bd[0]); + + templen = fast_expansion_sum_zeroelim(4, cd, 4, da, temp8); + cdalen = fast_expansion_sum_zeroelim(templen, temp8, 4, ac, cda); + templen = fast_expansion_sum_zeroelim(4, da, 4, ab, temp8); + dablen = fast_expansion_sum_zeroelim(templen, temp8, 4, bd, dab); + for (i = 0; i < 4; i++) { + bd[i] = -bd[i]; + ac[i] = -ac[i]; + } + templen = fast_expansion_sum_zeroelim(4, ab, 4, bc, temp8); + abclen = fast_expansion_sum_zeroelim(templen, temp8, 4, ac, abc); + templen = fast_expansion_sum_zeroelim(4, bc, 4, cd, temp8); + bcdlen = fast_expansion_sum_zeroelim(templen, temp8, 4, bd, bcd); + + alen = scale_expansion_zeroelim(bcdlen, bcd, pa[2], adet); + blen = scale_expansion_zeroelim(cdalen, cda, -pb[2], bdet); + clen = scale_expansion_zeroelim(dablen, dab, pc[2], cdet); + dlen = scale_expansion_zeroelim(abclen, abc, -pd[2], ddet); + + ablen = fast_expansion_sum_zeroelim(alen, adet, blen, bdet, abdet); + cdlen = fast_expansion_sum_zeroelim(clen, cdet, dlen, ddet, cddet); + deterlen = fast_expansion_sum_zeroelim(ablen, abdet, cdlen, cddet, deter); + + return deter[deterlen - 1]; +} + +REAL orient3dslow(REAL *pa, REAL *pb, REAL *pc, REAL *pd) +{ + INEXACT REAL adx, ady, adz, bdx, bdy, bdz, cdx, cdy, cdz; + REAL adxtail, adytail, adztail; + REAL bdxtail, bdytail, bdztail; + REAL cdxtail, cdytail, cdztail; + REAL negate, negatetail; + INEXACT REAL axby7, bxcy7, axcy7, bxay7, cxby7, cxay7; + REAL axby[8], bxcy[8], axcy[8], bxay[8], cxby[8], cxay[8]; + REAL temp16[16], temp32[32], temp32t[32]; + int temp16len, temp32len, temp32tlen; + REAL adet[64], bdet[64], cdet[64]; + int alen, blen, clen; + REAL abdet[128]; + int ablen; + REAL deter[192]; + int deterlen; + + INEXACT REAL bvirt; + REAL avirt, bround, around; + INEXACT REAL c; + INEXACT REAL abig; + REAL a0hi, a0lo, a1hi, a1lo, bhi, blo; + REAL err1, err2, err3; + INEXACT REAL _i, _j, _k, _l, _m, _n; + REAL _0, _1, _2; + + Two_Diff(pa[0], pd[0], adx, adxtail); + Two_Diff(pa[1], pd[1], ady, adytail); + Two_Diff(pa[2], pd[2], adz, adztail); + Two_Diff(pb[0], pd[0], bdx, bdxtail); + Two_Diff(pb[1], pd[1], bdy, bdytail); + Two_Diff(pb[2], pd[2], bdz, bdztail); + Two_Diff(pc[0], pd[0], cdx, cdxtail); + Two_Diff(pc[1], pd[1], cdy, cdytail); + Two_Diff(pc[2], pd[2], cdz, cdztail); + + Two_Two_Product(adx, adxtail, bdy, bdytail, + axby7, axby[6], axby[5], axby[4], + axby[3], axby[2], axby[1], axby[0]); + axby[7] = axby7; + negate = -ady; + negatetail = -adytail; + Two_Two_Product(bdx, bdxtail, negate, negatetail, + bxay7, bxay[6], bxay[5], bxay[4], + bxay[3], bxay[2], bxay[1], bxay[0]); + bxay[7] = bxay7; + Two_Two_Product(bdx, bdxtail, cdy, cdytail, + bxcy7, bxcy[6], bxcy[5], bxcy[4], + bxcy[3], bxcy[2], bxcy[1], bxcy[0]); + bxcy[7] = bxcy7; + negate = -bdy; + negatetail = -bdytail; + Two_Two_Product(cdx, cdxtail, negate, negatetail, + cxby7, cxby[6], cxby[5], cxby[4], + cxby[3], cxby[2], cxby[1], cxby[0]); + cxby[7] = cxby7; + Two_Two_Product(cdx, cdxtail, ady, adytail, + cxay7, cxay[6], cxay[5], cxay[4], + cxay[3], cxay[2], cxay[1], cxay[0]); + cxay[7] = cxay7; + negate = -cdy; + negatetail = -cdytail; + Two_Two_Product(adx, adxtail, negate, negatetail, + axcy7, axcy[6], axcy[5], axcy[4], + axcy[3], axcy[2], axcy[1], axcy[0]); + axcy[7] = axcy7; + + temp16len = fast_expansion_sum_zeroelim(8, bxcy, 8, cxby, temp16); + temp32len = scale_expansion_zeroelim(temp16len, temp16, adz, temp32); + temp32tlen = scale_expansion_zeroelim(temp16len, temp16, adztail, temp32t); + alen = fast_expansion_sum_zeroelim(temp32len, temp32, temp32tlen, temp32t, + adet); + + temp16len = fast_expansion_sum_zeroelim(8, cxay, 8, axcy, temp16); + temp32len = scale_expansion_zeroelim(temp16len, temp16, bdz, temp32); + temp32tlen = scale_expansion_zeroelim(temp16len, temp16, bdztail, temp32t); + blen = fast_expansion_sum_zeroelim(temp32len, temp32, temp32tlen, temp32t, + bdet); + + temp16len = fast_expansion_sum_zeroelim(8, axby, 8, bxay, temp16); + temp32len = scale_expansion_zeroelim(temp16len, temp16, cdz, temp32); + temp32tlen = scale_expansion_zeroelim(temp16len, temp16, cdztail, temp32t); + clen = fast_expansion_sum_zeroelim(temp32len, temp32, temp32tlen, temp32t, + cdet); + + ablen = fast_expansion_sum_zeroelim(alen, adet, blen, bdet, abdet); + deterlen = fast_expansion_sum_zeroelim(ablen, abdet, clen, cdet, deter); + + return deter[deterlen - 1]; +} + +REAL orient3dadapt(REAL *pa, REAL *pb, REAL *pc, REAL *pd, REAL permanent) +{ + INEXACT REAL adx, bdx, cdx, ady, bdy, cdy, adz, bdz, cdz; + REAL det, errbound; + + INEXACT REAL bdxcdy1, cdxbdy1, cdxady1, adxcdy1, adxbdy1, bdxady1; + REAL bdxcdy0, cdxbdy0, cdxady0, adxcdy0, adxbdy0, bdxady0; + REAL bc[4], ca[4], ab[4]; + INEXACT REAL bc3, ca3, ab3; + REAL adet[8], bdet[8], cdet[8]; + int alen, blen, clen; + REAL abdet[16]; + int ablen; + REAL *finnow, *finother, *finswap; + REAL fin1[192], fin2[192]; + int finlength; + + //////////////////////////////////////////////////////// + // To avoid uninitialized warnings reported by valgrind. + int i; + for (i = 0; i < 8; i++) { + adet[i] = bdet[i] = cdet[i] = 0.0; + } + for (i = 0; i < 16; i++) { + abdet[i] = 0.0; + } + //////////////////////////////////////////////////////// + + REAL adxtail, bdxtail, cdxtail; + REAL adytail, bdytail, cdytail; + REAL adztail, bdztail, cdztail; + INEXACT REAL at_blarge, at_clarge; + INEXACT REAL bt_clarge, bt_alarge; + INEXACT REAL ct_alarge, ct_blarge; + REAL at_b[4], at_c[4], bt_c[4], bt_a[4], ct_a[4], ct_b[4]; + int at_blen, at_clen, bt_clen, bt_alen, ct_alen, ct_blen; + INEXACT REAL bdxt_cdy1, cdxt_bdy1, cdxt_ady1; + INEXACT REAL adxt_cdy1, adxt_bdy1, bdxt_ady1; + REAL bdxt_cdy0, cdxt_bdy0, cdxt_ady0; + REAL adxt_cdy0, adxt_bdy0, bdxt_ady0; + INEXACT REAL bdyt_cdx1, cdyt_bdx1, cdyt_adx1; + INEXACT REAL adyt_cdx1, adyt_bdx1, bdyt_adx1; + REAL bdyt_cdx0, cdyt_bdx0, cdyt_adx0; + REAL adyt_cdx0, adyt_bdx0, bdyt_adx0; + REAL bct[8], cat[8], abt[8]; + int bctlen, catlen, abtlen; + INEXACT REAL bdxt_cdyt1, cdxt_bdyt1, cdxt_adyt1; + INEXACT REAL adxt_cdyt1, adxt_bdyt1, bdxt_adyt1; + REAL bdxt_cdyt0, cdxt_bdyt0, cdxt_adyt0; + REAL adxt_cdyt0, adxt_bdyt0, bdxt_adyt0; + REAL u[4], v[12], w[16]; + INEXACT REAL u3; + int vlength, wlength; + REAL negate; + + INEXACT REAL bvirt; + REAL avirt, bround, around; + INEXACT REAL c; + INEXACT REAL abig; + REAL ahi, alo, bhi, blo; + REAL err1, err2, err3; + INEXACT REAL _i, _j, _k; + REAL _0; + + adx = (REAL) (pa[0] - pd[0]); + bdx = (REAL) (pb[0] - pd[0]); + cdx = (REAL) (pc[0] - pd[0]); + ady = (REAL) (pa[1] - pd[1]); + bdy = (REAL) (pb[1] - pd[1]); + cdy = (REAL) (pc[1] - pd[1]); + adz = (REAL) (pa[2] - pd[2]); + bdz = (REAL) (pb[2] - pd[2]); + cdz = (REAL) (pc[2] - pd[2]); + + Two_Product(bdx, cdy, bdxcdy1, bdxcdy0); + Two_Product(cdx, bdy, cdxbdy1, cdxbdy0); + Two_Two_Diff(bdxcdy1, bdxcdy0, cdxbdy1, cdxbdy0, bc3, bc[2], bc[1], bc[0]); + bc[3] = bc3; + alen = scale_expansion_zeroelim(4, bc, adz, adet); + + Two_Product(cdx, ady, cdxady1, cdxady0); + Two_Product(adx, cdy, adxcdy1, adxcdy0); + Two_Two_Diff(cdxady1, cdxady0, adxcdy1, adxcdy0, ca3, ca[2], ca[1], ca[0]); + ca[3] = ca3; + blen = scale_expansion_zeroelim(4, ca, bdz, bdet); + + Two_Product(adx, bdy, adxbdy1, adxbdy0); + Two_Product(bdx, ady, bdxady1, bdxady0); + Two_Two_Diff(adxbdy1, adxbdy0, bdxady1, bdxady0, ab3, ab[2], ab[1], ab[0]); + ab[3] = ab3; + clen = scale_expansion_zeroelim(4, ab, cdz, cdet); + + ablen = fast_expansion_sum_zeroelim(alen, adet, blen, bdet, abdet); + finlength = fast_expansion_sum_zeroelim(ablen, abdet, clen, cdet, fin1); + + det = estimate(finlength, fin1); + errbound = o3derrboundB * permanent; + if ((det >= errbound) || (-det >= errbound)) { + return det; + } + + Two_Diff_Tail(pa[0], pd[0], adx, adxtail); + Two_Diff_Tail(pb[0], pd[0], bdx, bdxtail); + Two_Diff_Tail(pc[0], pd[0], cdx, cdxtail); + Two_Diff_Tail(pa[1], pd[1], ady, adytail); + Two_Diff_Tail(pb[1], pd[1], bdy, bdytail); + Two_Diff_Tail(pc[1], pd[1], cdy, cdytail); + Two_Diff_Tail(pa[2], pd[2], adz, adztail); + Two_Diff_Tail(pb[2], pd[2], bdz, bdztail); + Two_Diff_Tail(pc[2], pd[2], cdz, cdztail); + + if ((adxtail == 0.0) && (bdxtail == 0.0) && (cdxtail == 0.0) + && (adytail == 0.0) && (bdytail == 0.0) && (cdytail == 0.0) + && (adztail == 0.0) && (bdztail == 0.0) && (cdztail == 0.0)) { + return det; + } + + errbound = o3derrboundC * permanent + resulterrbound * Absolute(det); + det += (adz * ((bdx * cdytail + cdy * bdxtail) + - (bdy * cdxtail + cdx * bdytail)) + + adztail * (bdx * cdy - bdy * cdx)) + + (bdz * ((cdx * adytail + ady * cdxtail) + - (cdy * adxtail + adx * cdytail)) + + bdztail * (cdx * ady - cdy * adx)) + + (cdz * ((adx * bdytail + bdy * adxtail) + - (ady * bdxtail + bdx * adytail)) + + cdztail * (adx * bdy - ady * bdx)); + if ((det >= errbound) || (-det >= errbound)) { + return det; + } + + finnow = fin1; + finother = fin2; + + if (adxtail == 0.0) { + if (adytail == 0.0) { + at_b[0] = 0.0; + at_blen = 1; + at_c[0] = 0.0; + at_clen = 1; + } else { + negate = -adytail; + Two_Product(negate, bdx, at_blarge, at_b[0]); + at_b[1] = at_blarge; + at_blen = 2; + Two_Product(adytail, cdx, at_clarge, at_c[0]); + at_c[1] = at_clarge; + at_clen = 2; + } + } else { + if (adytail == 0.0) { + Two_Product(adxtail, bdy, at_blarge, at_b[0]); + at_b[1] = at_blarge; + at_blen = 2; + negate = -adxtail; + Two_Product(negate, cdy, at_clarge, at_c[0]); + at_c[1] = at_clarge; + at_clen = 2; + } else { + Two_Product(adxtail, bdy, adxt_bdy1, adxt_bdy0); + Two_Product(adytail, bdx, adyt_bdx1, adyt_bdx0); + Two_Two_Diff(adxt_bdy1, adxt_bdy0, adyt_bdx1, adyt_bdx0, + at_blarge, at_b[2], at_b[1], at_b[0]); + at_b[3] = at_blarge; + at_blen = 4; + Two_Product(adytail, cdx, adyt_cdx1, adyt_cdx0); + Two_Product(adxtail, cdy, adxt_cdy1, adxt_cdy0); + Two_Two_Diff(adyt_cdx1, adyt_cdx0, adxt_cdy1, adxt_cdy0, + at_clarge, at_c[2], at_c[1], at_c[0]); + at_c[3] = at_clarge; + at_clen = 4; + } + } + if (bdxtail == 0.0) { + if (bdytail == 0.0) { + bt_c[0] = 0.0; + bt_clen = 1; + bt_a[0] = 0.0; + bt_alen = 1; + } else { + negate = -bdytail; + Two_Product(negate, cdx, bt_clarge, bt_c[0]); + bt_c[1] = bt_clarge; + bt_clen = 2; + Two_Product(bdytail, adx, bt_alarge, bt_a[0]); + bt_a[1] = bt_alarge; + bt_alen = 2; + } + } else { + if (bdytail == 0.0) { + Two_Product(bdxtail, cdy, bt_clarge, bt_c[0]); + bt_c[1] = bt_clarge; + bt_clen = 2; + negate = -bdxtail; + Two_Product(negate, ady, bt_alarge, bt_a[0]); + bt_a[1] = bt_alarge; + bt_alen = 2; + } else { + Two_Product(bdxtail, cdy, bdxt_cdy1, bdxt_cdy0); + Two_Product(bdytail, cdx, bdyt_cdx1, bdyt_cdx0); + Two_Two_Diff(bdxt_cdy1, bdxt_cdy0, bdyt_cdx1, bdyt_cdx0, + bt_clarge, bt_c[2], bt_c[1], bt_c[0]); + bt_c[3] = bt_clarge; + bt_clen = 4; + Two_Product(bdytail, adx, bdyt_adx1, bdyt_adx0); + Two_Product(bdxtail, ady, bdxt_ady1, bdxt_ady0); + Two_Two_Diff(bdyt_adx1, bdyt_adx0, bdxt_ady1, bdxt_ady0, + bt_alarge, bt_a[2], bt_a[1], bt_a[0]); + bt_a[3] = bt_alarge; + bt_alen = 4; + } + } + if (cdxtail == 0.0) { + if (cdytail == 0.0) { + ct_a[0] = 0.0; + ct_alen = 1; + ct_b[0] = 0.0; + ct_blen = 1; + } else { + negate = -cdytail; + Two_Product(negate, adx, ct_alarge, ct_a[0]); + ct_a[1] = ct_alarge; + ct_alen = 2; + Two_Product(cdytail, bdx, ct_blarge, ct_b[0]); + ct_b[1] = ct_blarge; + ct_blen = 2; + } + } else { + if (cdytail == 0.0) { + Two_Product(cdxtail, ady, ct_alarge, ct_a[0]); + ct_a[1] = ct_alarge; + ct_alen = 2; + negate = -cdxtail; + Two_Product(negate, bdy, ct_blarge, ct_b[0]); + ct_b[1] = ct_blarge; + ct_blen = 2; + } else { + Two_Product(cdxtail, ady, cdxt_ady1, cdxt_ady0); + Two_Product(cdytail, adx, cdyt_adx1, cdyt_adx0); + Two_Two_Diff(cdxt_ady1, cdxt_ady0, cdyt_adx1, cdyt_adx0, + ct_alarge, ct_a[2], ct_a[1], ct_a[0]); + ct_a[3] = ct_alarge; + ct_alen = 4; + Two_Product(cdytail, bdx, cdyt_bdx1, cdyt_bdx0); + Two_Product(cdxtail, bdy, cdxt_bdy1, cdxt_bdy0); + Two_Two_Diff(cdyt_bdx1, cdyt_bdx0, cdxt_bdy1, cdxt_bdy0, + ct_blarge, ct_b[2], ct_b[1], ct_b[0]); + ct_b[3] = ct_blarge; + ct_blen = 4; + } + } + + bctlen = fast_expansion_sum_zeroelim(bt_clen, bt_c, ct_blen, ct_b, bct); + wlength = scale_expansion_zeroelim(bctlen, bct, adz, w); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, wlength, w, + finother); + finswap = finnow; finnow = finother; finother = finswap; + + catlen = fast_expansion_sum_zeroelim(ct_alen, ct_a, at_clen, at_c, cat); + wlength = scale_expansion_zeroelim(catlen, cat, bdz, w); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, wlength, w, + finother); + finswap = finnow; finnow = finother; finother = finswap; + + abtlen = fast_expansion_sum_zeroelim(at_blen, at_b, bt_alen, bt_a, abt); + wlength = scale_expansion_zeroelim(abtlen, abt, cdz, w); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, wlength, w, + finother); + finswap = finnow; finnow = finother; finother = finswap; + + if (adztail != 0.0) { + vlength = scale_expansion_zeroelim(4, bc, adztail, v); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, vlength, v, + finother); + finswap = finnow; finnow = finother; finother = finswap; + } + if (bdztail != 0.0) { + vlength = scale_expansion_zeroelim(4, ca, bdztail, v); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, vlength, v, + finother); + finswap = finnow; finnow = finother; finother = finswap; + } + if (cdztail != 0.0) { + vlength = scale_expansion_zeroelim(4, ab, cdztail, v); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, vlength, v, + finother); + finswap = finnow; finnow = finother; finother = finswap; + } + + if (adxtail != 0.0) { + if (bdytail != 0.0) { + Two_Product(adxtail, bdytail, adxt_bdyt1, adxt_bdyt0); + Two_One_Product(adxt_bdyt1, adxt_bdyt0, cdz, u3, u[2], u[1], u[0]); + u[3] = u3; + finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, + finother); + finswap = finnow; finnow = finother; finother = finswap; + if (cdztail != 0.0) { + Two_One_Product(adxt_bdyt1, adxt_bdyt0, cdztail, u3, u[2], u[1], u[0]); + u[3] = u3; + finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, + finother); + finswap = finnow; finnow = finother; finother = finswap; + } + } + if (cdytail != 0.0) { + negate = -adxtail; + Two_Product(negate, cdytail, adxt_cdyt1, adxt_cdyt0); + Two_One_Product(adxt_cdyt1, adxt_cdyt0, bdz, u3, u[2], u[1], u[0]); + u[3] = u3; + finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, + finother); + finswap = finnow; finnow = finother; finother = finswap; + if (bdztail != 0.0) { + Two_One_Product(adxt_cdyt1, adxt_cdyt0, bdztail, u3, u[2], u[1], u[0]); + u[3] = u3; + finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, + finother); + finswap = finnow; finnow = finother; finother = finswap; + } + } + } + if (bdxtail != 0.0) { + if (cdytail != 0.0) { + Two_Product(bdxtail, cdytail, bdxt_cdyt1, bdxt_cdyt0); + Two_One_Product(bdxt_cdyt1, bdxt_cdyt0, adz, u3, u[2], u[1], u[0]); + u[3] = u3; + finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, + finother); + finswap = finnow; finnow = finother; finother = finswap; + if (adztail != 0.0) { + Two_One_Product(bdxt_cdyt1, bdxt_cdyt0, adztail, u3, u[2], u[1], u[0]); + u[3] = u3; + finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, + finother); + finswap = finnow; finnow = finother; finother = finswap; + } + } + if (adytail != 0.0) { + negate = -bdxtail; + Two_Product(negate, adytail, bdxt_adyt1, bdxt_adyt0); + Two_One_Product(bdxt_adyt1, bdxt_adyt0, cdz, u3, u[2], u[1], u[0]); + u[3] = u3; + finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, + finother); + finswap = finnow; finnow = finother; finother = finswap; + if (cdztail != 0.0) { + Two_One_Product(bdxt_adyt1, bdxt_adyt0, cdztail, u3, u[2], u[1], u[0]); + u[3] = u3; + finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, + finother); + finswap = finnow; finnow = finother; finother = finswap; + } + } + } + if (cdxtail != 0.0) { + if (adytail != 0.0) { + Two_Product(cdxtail, adytail, cdxt_adyt1, cdxt_adyt0); + Two_One_Product(cdxt_adyt1, cdxt_adyt0, bdz, u3, u[2], u[1], u[0]); + u[3] = u3; + finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, + finother); + finswap = finnow; finnow = finother; finother = finswap; + if (bdztail != 0.0) { + Two_One_Product(cdxt_adyt1, cdxt_adyt0, bdztail, u3, u[2], u[1], u[0]); + u[3] = u3; + finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, + finother); + finswap = finnow; finnow = finother; finother = finswap; + } + } + if (bdytail != 0.0) { + negate = -cdxtail; + Two_Product(negate, bdytail, cdxt_bdyt1, cdxt_bdyt0); + Two_One_Product(cdxt_bdyt1, cdxt_bdyt0, adz, u3, u[2], u[1], u[0]); + u[3] = u3; + finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, + finother); + finswap = finnow; finnow = finother; finother = finswap; + if (adztail != 0.0) { + Two_One_Product(cdxt_bdyt1, cdxt_bdyt0, adztail, u3, u[2], u[1], u[0]); + u[3] = u3; + finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, + finother); + finswap = finnow; finnow = finother; finother = finswap; + } + } + } + + if (adztail != 0.0) { + wlength = scale_expansion_zeroelim(bctlen, bct, adztail, w); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, wlength, w, + finother); + finswap = finnow; finnow = finother; finother = finswap; + } + if (bdztail != 0.0) { + wlength = scale_expansion_zeroelim(catlen, cat, bdztail, w); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, wlength, w, + finother); + finswap = finnow; finnow = finother; finother = finswap; + } + if (cdztail != 0.0) { + wlength = scale_expansion_zeroelim(abtlen, abt, cdztail, w); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, wlength, w, + finother); + finswap = finnow; finnow = finother; finother = finswap; + } + + return finnow[finlength - 1]; +} + +REAL orient3d(REAL *pa, REAL *pb, REAL *pc, REAL *pd) +{ + REAL adx, bdx, cdx, ady, bdy, cdy, adz, bdz, cdz; + REAL bdxcdy, cdxbdy, cdxady, adxcdy, adxbdy, bdxady; + REAL det; + REAL permanent, errbound; + + adx = pa[0] - pd[0]; + bdx = pb[0] - pd[0]; + cdx = pc[0] - pd[0]; + ady = pa[1] - pd[1]; + bdy = pb[1] - pd[1]; + cdy = pc[1] - pd[1]; + adz = pa[2] - pd[2]; + bdz = pb[2] - pd[2]; + cdz = pc[2] - pd[2]; + + bdxcdy = bdx * cdy; + cdxbdy = cdx * bdy; + + cdxady = cdx * ady; + adxcdy = adx * cdy; + + adxbdy = adx * bdy; + bdxady = bdx * ady; + + det = adz * (bdxcdy - cdxbdy) + + bdz * (cdxady - adxcdy) + + cdz * (adxbdy - bdxady); + + permanent = (Absolute(bdxcdy) + Absolute(cdxbdy)) * Absolute(adz) + + (Absolute(cdxady) + Absolute(adxcdy)) * Absolute(bdz) + + (Absolute(adxbdy) + Absolute(bdxady)) * Absolute(cdz); + errbound = o3derrboundA * permanent; + if ((det > errbound) || (-det > errbound)) { + return det; + } + + return orient3dadapt(pa, pb, pc, pd, permanent); +} + +/*****************************************************************************/ +/* */ +/* incirclefast() Approximate 2D incircle test. Nonrobust. */ +/* incircleexact() Exact 2D incircle test. Robust. */ +/* incircleslow() Another exact 2D incircle test. Robust. */ +/* incircle() Adaptive exact 2D incircle test. Robust. */ +/* */ +/* Return a positive value if the point pd lies inside the */ +/* circle passing through pa, pb, and pc; a negative value if */ +/* it lies outside; and zero if the four points are cocircular.*/ +/* The points pa, pb, and pc must be in counterclockwise */ +/* order, or the sign of the result will be reversed. */ +/* */ +/* Only the first and last routine should be used; the middle two are for */ +/* timings. */ +/* */ +/* The last three use exact arithmetic to ensure a correct answer. The */ +/* result returned is the determinant of a matrix. In incircle() only, */ +/* this determinant is computed adaptively, in the sense that exact */ +/* arithmetic is used only to the degree it is needed to ensure that the */ +/* returned value has the correct sign. Hence, incircle() is usually quite */ +/* fast, but will run more slowly when the input points are cocircular or */ +/* nearly so. */ +/* */ +/*****************************************************************************/ + +REAL incirclefast(REAL *pa, REAL *pb, REAL *pc, REAL *pd) +{ + REAL adx, ady, bdx, bdy, cdx, cdy; + REAL abdet, bcdet, cadet; + REAL alift, blift, clift; + + adx = pa[0] - pd[0]; + ady = pa[1] - pd[1]; + bdx = pb[0] - pd[0]; + bdy = pb[1] - pd[1]; + cdx = pc[0] - pd[0]; + cdy = pc[1] - pd[1]; + + abdet = adx * bdy - bdx * ady; + bcdet = bdx * cdy - cdx * bdy; + cadet = cdx * ady - adx * cdy; + alift = adx * adx + ady * ady; + blift = bdx * bdx + bdy * bdy; + clift = cdx * cdx + cdy * cdy; + + return alift * bcdet + blift * cadet + clift * abdet; +} + +REAL incircleexact(REAL *pa, REAL *pb, REAL *pc, REAL *pd) +{ + INEXACT REAL axby1, bxcy1, cxdy1, dxay1, axcy1, bxdy1; + INEXACT REAL bxay1, cxby1, dxcy1, axdy1, cxay1, dxby1; + REAL axby0, bxcy0, cxdy0, dxay0, axcy0, bxdy0; + REAL bxay0, cxby0, dxcy0, axdy0, cxay0, dxby0; + REAL ab[4], bc[4], cd[4], da[4], ac[4], bd[4]; + REAL temp8[8]; + int templen; + REAL abc[12], bcd[12], cda[12], dab[12]; + int abclen, bcdlen, cdalen, dablen; + REAL det24x[24], det24y[24], det48x[48], det48y[48]; + int xlen, ylen; + REAL adet[96], bdet[96], cdet[96], ddet[96]; + int alen, blen, clen, dlen; + REAL abdet[192], cddet[192]; + int ablen, cdlen; + REAL deter[384]; + int deterlen; + int i; + + INEXACT REAL bvirt; + REAL avirt, bround, around; + INEXACT REAL c; + INEXACT REAL abig; + REAL ahi, alo, bhi, blo; + REAL err1, err2, err3; + INEXACT REAL _i, _j; + REAL _0; + + Two_Product(pa[0], pb[1], axby1, axby0); + Two_Product(pb[0], pa[1], bxay1, bxay0); + Two_Two_Diff(axby1, axby0, bxay1, bxay0, ab[3], ab[2], ab[1], ab[0]); + + Two_Product(pb[0], pc[1], bxcy1, bxcy0); + Two_Product(pc[0], pb[1], cxby1, cxby0); + Two_Two_Diff(bxcy1, bxcy0, cxby1, cxby0, bc[3], bc[2], bc[1], bc[0]); + + Two_Product(pc[0], pd[1], cxdy1, cxdy0); + Two_Product(pd[0], pc[1], dxcy1, dxcy0); + Two_Two_Diff(cxdy1, cxdy0, dxcy1, dxcy0, cd[3], cd[2], cd[1], cd[0]); + + Two_Product(pd[0], pa[1], dxay1, dxay0); + Two_Product(pa[0], pd[1], axdy1, axdy0); + Two_Two_Diff(dxay1, dxay0, axdy1, axdy0, da[3], da[2], da[1], da[0]); + + Two_Product(pa[0], pc[1], axcy1, axcy0); + Two_Product(pc[0], pa[1], cxay1, cxay0); + Two_Two_Diff(axcy1, axcy0, cxay1, cxay0, ac[3], ac[2], ac[1], ac[0]); + + Two_Product(pb[0], pd[1], bxdy1, bxdy0); + Two_Product(pd[0], pb[1], dxby1, dxby0); + Two_Two_Diff(bxdy1, bxdy0, dxby1, dxby0, bd[3], bd[2], bd[1], bd[0]); + + templen = fast_expansion_sum_zeroelim(4, cd, 4, da, temp8); + cdalen = fast_expansion_sum_zeroelim(templen, temp8, 4, ac, cda); + templen = fast_expansion_sum_zeroelim(4, da, 4, ab, temp8); + dablen = fast_expansion_sum_zeroelim(templen, temp8, 4, bd, dab); + for (i = 0; i < 4; i++) { + bd[i] = -bd[i]; + ac[i] = -ac[i]; + } + templen = fast_expansion_sum_zeroelim(4, ab, 4, bc, temp8); + abclen = fast_expansion_sum_zeroelim(templen, temp8, 4, ac, abc); + templen = fast_expansion_sum_zeroelim(4, bc, 4, cd, temp8); + bcdlen = fast_expansion_sum_zeroelim(templen, temp8, 4, bd, bcd); + + xlen = scale_expansion_zeroelim(bcdlen, bcd, pa[0], det24x); + xlen = scale_expansion_zeroelim(xlen, det24x, pa[0], det48x); + ylen = scale_expansion_zeroelim(bcdlen, bcd, pa[1], det24y); + ylen = scale_expansion_zeroelim(ylen, det24y, pa[1], det48y); + alen = fast_expansion_sum_zeroelim(xlen, det48x, ylen, det48y, adet); + + xlen = scale_expansion_zeroelim(cdalen, cda, pb[0], det24x); + xlen = scale_expansion_zeroelim(xlen, det24x, -pb[0], det48x); + ylen = scale_expansion_zeroelim(cdalen, cda, pb[1], det24y); + ylen = scale_expansion_zeroelim(ylen, det24y, -pb[1], det48y); + blen = fast_expansion_sum_zeroelim(xlen, det48x, ylen, det48y, bdet); + + xlen = scale_expansion_zeroelim(dablen, dab, pc[0], det24x); + xlen = scale_expansion_zeroelim(xlen, det24x, pc[0], det48x); + ylen = scale_expansion_zeroelim(dablen, dab, pc[1], det24y); + ylen = scale_expansion_zeroelim(ylen, det24y, pc[1], det48y); + clen = fast_expansion_sum_zeroelim(xlen, det48x, ylen, det48y, cdet); + + xlen = scale_expansion_zeroelim(abclen, abc, pd[0], det24x); + xlen = scale_expansion_zeroelim(xlen, det24x, -pd[0], det48x); + ylen = scale_expansion_zeroelim(abclen, abc, pd[1], det24y); + ylen = scale_expansion_zeroelim(ylen, det24y, -pd[1], det48y); + dlen = fast_expansion_sum_zeroelim(xlen, det48x, ylen, det48y, ddet); + + ablen = fast_expansion_sum_zeroelim(alen, adet, blen, bdet, abdet); + cdlen = fast_expansion_sum_zeroelim(clen, cdet, dlen, ddet, cddet); + deterlen = fast_expansion_sum_zeroelim(ablen, abdet, cdlen, cddet, deter); + + return deter[deterlen - 1]; +} + +REAL incircleslow(REAL *pa, REAL *pb, REAL *pc, REAL *pd) +{ + INEXACT REAL adx, bdx, cdx, ady, bdy, cdy; + REAL adxtail, bdxtail, cdxtail; + REAL adytail, bdytail, cdytail; + REAL negate, negatetail; + INEXACT REAL axby7, bxcy7, axcy7, bxay7, cxby7, cxay7; + REAL axby[8], bxcy[8], axcy[8], bxay[8], cxby[8], cxay[8]; + REAL temp16[16]; + int temp16len; + REAL detx[32], detxx[64], detxt[32], detxxt[64], detxtxt[64]; + int xlen, xxlen, xtlen, xxtlen, xtxtlen; + REAL x1[128], x2[192]; + int x1len, x2len; + REAL dety[32], detyy[64], detyt[32], detyyt[64], detytyt[64]; + int ylen, yylen, ytlen, yytlen, ytytlen; + REAL y1[128], y2[192]; + int y1len, y2len; + REAL adet[384], bdet[384], cdet[384], abdet[768], deter[1152]; + int alen, blen, clen, ablen, deterlen; + int i; + + INEXACT REAL bvirt; + REAL avirt, bround, around; + INEXACT REAL c; + INEXACT REAL abig; + REAL a0hi, a0lo, a1hi, a1lo, bhi, blo; + REAL err1, err2, err3; + INEXACT REAL _i, _j, _k, _l, _m, _n; + REAL _0, _1, _2; + + Two_Diff(pa[0], pd[0], adx, adxtail); + Two_Diff(pa[1], pd[1], ady, adytail); + Two_Diff(pb[0], pd[0], bdx, bdxtail); + Two_Diff(pb[1], pd[1], bdy, bdytail); + Two_Diff(pc[0], pd[0], cdx, cdxtail); + Two_Diff(pc[1], pd[1], cdy, cdytail); + + Two_Two_Product(adx, adxtail, bdy, bdytail, + axby7, axby[6], axby[5], axby[4], + axby[3], axby[2], axby[1], axby[0]); + axby[7] = axby7; + negate = -ady; + negatetail = -adytail; + Two_Two_Product(bdx, bdxtail, negate, negatetail, + bxay7, bxay[6], bxay[5], bxay[4], + bxay[3], bxay[2], bxay[1], bxay[0]); + bxay[7] = bxay7; + Two_Two_Product(bdx, bdxtail, cdy, cdytail, + bxcy7, bxcy[6], bxcy[5], bxcy[4], + bxcy[3], bxcy[2], bxcy[1], bxcy[0]); + bxcy[7] = bxcy7; + negate = -bdy; + negatetail = -bdytail; + Two_Two_Product(cdx, cdxtail, negate, negatetail, + cxby7, cxby[6], cxby[5], cxby[4], + cxby[3], cxby[2], cxby[1], cxby[0]); + cxby[7] = cxby7; + Two_Two_Product(cdx, cdxtail, ady, adytail, + cxay7, cxay[6], cxay[5], cxay[4], + cxay[3], cxay[2], cxay[1], cxay[0]); + cxay[7] = cxay7; + negate = -cdy; + negatetail = -cdytail; + Two_Two_Product(adx, adxtail, negate, negatetail, + axcy7, axcy[6], axcy[5], axcy[4], + axcy[3], axcy[2], axcy[1], axcy[0]); + axcy[7] = axcy7; + + + temp16len = fast_expansion_sum_zeroelim(8, bxcy, 8, cxby, temp16); + + xlen = scale_expansion_zeroelim(temp16len, temp16, adx, detx); + xxlen = scale_expansion_zeroelim(xlen, detx, adx, detxx); + xtlen = scale_expansion_zeroelim(temp16len, temp16, adxtail, detxt); + xxtlen = scale_expansion_zeroelim(xtlen, detxt, adx, detxxt); + for (i = 0; i < xxtlen; i++) { + detxxt[i] *= 2.0; + } + xtxtlen = scale_expansion_zeroelim(xtlen, detxt, adxtail, detxtxt); + x1len = fast_expansion_sum_zeroelim(xxlen, detxx, xxtlen, detxxt, x1); + x2len = fast_expansion_sum_zeroelim(x1len, x1, xtxtlen, detxtxt, x2); + + ylen = scale_expansion_zeroelim(temp16len, temp16, ady, dety); + yylen = scale_expansion_zeroelim(ylen, dety, ady, detyy); + ytlen = scale_expansion_zeroelim(temp16len, temp16, adytail, detyt); + yytlen = scale_expansion_zeroelim(ytlen, detyt, ady, detyyt); + for (i = 0; i < yytlen; i++) { + detyyt[i] *= 2.0; + } + ytytlen = scale_expansion_zeroelim(ytlen, detyt, adytail, detytyt); + y1len = fast_expansion_sum_zeroelim(yylen, detyy, yytlen, detyyt, y1); + y2len = fast_expansion_sum_zeroelim(y1len, y1, ytytlen, detytyt, y2); + + alen = fast_expansion_sum_zeroelim(x2len, x2, y2len, y2, adet); + + + temp16len = fast_expansion_sum_zeroelim(8, cxay, 8, axcy, temp16); + + xlen = scale_expansion_zeroelim(temp16len, temp16, bdx, detx); + xxlen = scale_expansion_zeroelim(xlen, detx, bdx, detxx); + xtlen = scale_expansion_zeroelim(temp16len, temp16, bdxtail, detxt); + xxtlen = scale_expansion_zeroelim(xtlen, detxt, bdx, detxxt); + for (i = 0; i < xxtlen; i++) { + detxxt[i] *= 2.0; + } + xtxtlen = scale_expansion_zeroelim(xtlen, detxt, bdxtail, detxtxt); + x1len = fast_expansion_sum_zeroelim(xxlen, detxx, xxtlen, detxxt, x1); + x2len = fast_expansion_sum_zeroelim(x1len, x1, xtxtlen, detxtxt, x2); + + ylen = scale_expansion_zeroelim(temp16len, temp16, bdy, dety); + yylen = scale_expansion_zeroelim(ylen, dety, bdy, detyy); + ytlen = scale_expansion_zeroelim(temp16len, temp16, bdytail, detyt); + yytlen = scale_expansion_zeroelim(ytlen, detyt, bdy, detyyt); + for (i = 0; i < yytlen; i++) { + detyyt[i] *= 2.0; + } + ytytlen = scale_expansion_zeroelim(ytlen, detyt, bdytail, detytyt); + y1len = fast_expansion_sum_zeroelim(yylen, detyy, yytlen, detyyt, y1); + y2len = fast_expansion_sum_zeroelim(y1len, y1, ytytlen, detytyt, y2); + + blen = fast_expansion_sum_zeroelim(x2len, x2, y2len, y2, bdet); + + + temp16len = fast_expansion_sum_zeroelim(8, axby, 8, bxay, temp16); + + xlen = scale_expansion_zeroelim(temp16len, temp16, cdx, detx); + xxlen = scale_expansion_zeroelim(xlen, detx, cdx, detxx); + xtlen = scale_expansion_zeroelim(temp16len, temp16, cdxtail, detxt); + xxtlen = scale_expansion_zeroelim(xtlen, detxt, cdx, detxxt); + for (i = 0; i < xxtlen; i++) { + detxxt[i] *= 2.0; + } + xtxtlen = scale_expansion_zeroelim(xtlen, detxt, cdxtail, detxtxt); + x1len = fast_expansion_sum_zeroelim(xxlen, detxx, xxtlen, detxxt, x1); + x2len = fast_expansion_sum_zeroelim(x1len, x1, xtxtlen, detxtxt, x2); + + ylen = scale_expansion_zeroelim(temp16len, temp16, cdy, dety); + yylen = scale_expansion_zeroelim(ylen, dety, cdy, detyy); + ytlen = scale_expansion_zeroelim(temp16len, temp16, cdytail, detyt); + yytlen = scale_expansion_zeroelim(ytlen, detyt, cdy, detyyt); + for (i = 0; i < yytlen; i++) { + detyyt[i] *= 2.0; + } + ytytlen = scale_expansion_zeroelim(ytlen, detyt, cdytail, detytyt); + y1len = fast_expansion_sum_zeroelim(yylen, detyy, yytlen, detyyt, y1); + y2len = fast_expansion_sum_zeroelim(y1len, y1, ytytlen, detytyt, y2); + + clen = fast_expansion_sum_zeroelim(x2len, x2, y2len, y2, cdet); + + ablen = fast_expansion_sum_zeroelim(alen, adet, blen, bdet, abdet); + deterlen = fast_expansion_sum_zeroelim(ablen, abdet, clen, cdet, deter); + + return deter[deterlen - 1]; +} + +REAL incircleadapt(REAL *pa, REAL *pb, REAL *pc, REAL *pd, REAL permanent) +{ + INEXACT REAL adx, bdx, cdx, ady, bdy, cdy; + REAL det, errbound; + + INEXACT REAL bdxcdy1, cdxbdy1, cdxady1, adxcdy1, adxbdy1, bdxady1; + REAL bdxcdy0, cdxbdy0, cdxady0, adxcdy0, adxbdy0, bdxady0; + REAL bc[4], ca[4], ab[4]; + INEXACT REAL bc3, ca3, ab3; + REAL axbc[8], axxbc[16], aybc[8], ayybc[16], adet[32]; + int axbclen, axxbclen, aybclen, ayybclen, alen; + REAL bxca[8], bxxca[16], byca[8], byyca[16], bdet[32]; + int bxcalen, bxxcalen, bycalen, byycalen, blen; + REAL cxab[8], cxxab[16], cyab[8], cyyab[16], cdet[32]; + int cxablen, cxxablen, cyablen, cyyablen, clen; + REAL abdet[64]; + int ablen; + REAL fin1[1152], fin2[1152]; + REAL *finnow, *finother, *finswap; + int finlength; + + REAL adxtail, bdxtail, cdxtail, adytail, bdytail, cdytail; + INEXACT REAL adxadx1, adyady1, bdxbdx1, bdybdy1, cdxcdx1, cdycdy1; + REAL adxadx0, adyady0, bdxbdx0, bdybdy0, cdxcdx0, cdycdy0; + REAL aa[4], bb[4], cc[4]; + INEXACT REAL aa3, bb3, cc3; + INEXACT REAL ti1, tj1; + REAL ti0, tj0; + REAL u[4], v[4]; + INEXACT REAL u3, v3; + REAL temp8[8], temp16a[16], temp16b[16], temp16c[16]; + REAL temp32a[32], temp32b[32], temp48[48], temp64[64]; + int temp8len, temp16alen, temp16blen, temp16clen; + int temp32alen, temp32blen, temp48len, temp64len; + REAL axtbb[8], axtcc[8], aytbb[8], aytcc[8]; + int axtbblen, axtcclen, aytbblen, aytcclen; + REAL bxtaa[8], bxtcc[8], bytaa[8], bytcc[8]; + int bxtaalen, bxtcclen, bytaalen, bytcclen; + REAL cxtaa[8], cxtbb[8], cytaa[8], cytbb[8]; + int cxtaalen, cxtbblen, cytaalen, cytbblen; + REAL axtbc[8], aytbc[8], bxtca[8], bytca[8], cxtab[8], cytab[8]; + int axtbclen, aytbclen, bxtcalen, bytcalen, cxtablen, cytablen; + REAL axtbct[16], aytbct[16], bxtcat[16], bytcat[16], cxtabt[16], cytabt[16]; + int axtbctlen, aytbctlen, bxtcatlen, bytcatlen, cxtabtlen, cytabtlen; + REAL axtbctt[8], aytbctt[8], bxtcatt[8]; + REAL bytcatt[8], cxtabtt[8], cytabtt[8]; + int axtbcttlen, aytbcttlen, bxtcattlen, bytcattlen, cxtabttlen, cytabttlen; + REAL abt[8], bct[8], cat[8]; + int abtlen, bctlen, catlen; + REAL abtt[4], bctt[4], catt[4]; + int abttlen, bcttlen, cattlen; + INEXACT REAL abtt3, bctt3, catt3; + REAL negate; + + INEXACT REAL bvirt; + REAL avirt, bround, around; + INEXACT REAL c; + INEXACT REAL abig; + REAL ahi, alo, bhi, blo; + REAL err1, err2, err3; + INEXACT REAL _i, _j; + REAL _0; + + adx = (REAL) (pa[0] - pd[0]); + bdx = (REAL) (pb[0] - pd[0]); + cdx = (REAL) (pc[0] - pd[0]); + ady = (REAL) (pa[1] - pd[1]); + bdy = (REAL) (pb[1] - pd[1]); + cdy = (REAL) (pc[1] - pd[1]); + + Two_Product(bdx, cdy, bdxcdy1, bdxcdy0); + Two_Product(cdx, bdy, cdxbdy1, cdxbdy0); + Two_Two_Diff(bdxcdy1, bdxcdy0, cdxbdy1, cdxbdy0, bc3, bc[2], bc[1], bc[0]); + bc[3] = bc3; + axbclen = scale_expansion_zeroelim(4, bc, adx, axbc); + axxbclen = scale_expansion_zeroelim(axbclen, axbc, adx, axxbc); + aybclen = scale_expansion_zeroelim(4, bc, ady, aybc); + ayybclen = scale_expansion_zeroelim(aybclen, aybc, ady, ayybc); + alen = fast_expansion_sum_zeroelim(axxbclen, axxbc, ayybclen, ayybc, adet); + + Two_Product(cdx, ady, cdxady1, cdxady0); + Two_Product(adx, cdy, adxcdy1, adxcdy0); + Two_Two_Diff(cdxady1, cdxady0, adxcdy1, adxcdy0, ca3, ca[2], ca[1], ca[0]); + ca[3] = ca3; + bxcalen = scale_expansion_zeroelim(4, ca, bdx, bxca); + bxxcalen = scale_expansion_zeroelim(bxcalen, bxca, bdx, bxxca); + bycalen = scale_expansion_zeroelim(4, ca, bdy, byca); + byycalen = scale_expansion_zeroelim(bycalen, byca, bdy, byyca); + blen = fast_expansion_sum_zeroelim(bxxcalen, bxxca, byycalen, byyca, bdet); + + Two_Product(adx, bdy, adxbdy1, adxbdy0); + Two_Product(bdx, ady, bdxady1, bdxady0); + Two_Two_Diff(adxbdy1, adxbdy0, bdxady1, bdxady0, ab3, ab[2], ab[1], ab[0]); + ab[3] = ab3; + cxablen = scale_expansion_zeroelim(4, ab, cdx, cxab); + cxxablen = scale_expansion_zeroelim(cxablen, cxab, cdx, cxxab); + cyablen = scale_expansion_zeroelim(4, ab, cdy, cyab); + cyyablen = scale_expansion_zeroelim(cyablen, cyab, cdy, cyyab); + clen = fast_expansion_sum_zeroelim(cxxablen, cxxab, cyyablen, cyyab, cdet); + + ablen = fast_expansion_sum_zeroelim(alen, adet, blen, bdet, abdet); + finlength = fast_expansion_sum_zeroelim(ablen, abdet, clen, cdet, fin1); + + det = estimate(finlength, fin1); + errbound = iccerrboundB * permanent; + if ((det >= errbound) || (-det >= errbound)) { + return det; + } + + Two_Diff_Tail(pa[0], pd[0], adx, adxtail); + Two_Diff_Tail(pa[1], pd[1], ady, adytail); + Two_Diff_Tail(pb[0], pd[0], bdx, bdxtail); + Two_Diff_Tail(pb[1], pd[1], bdy, bdytail); + Two_Diff_Tail(pc[0], pd[0], cdx, cdxtail); + Two_Diff_Tail(pc[1], pd[1], cdy, cdytail); + if ((adxtail == 0.0) && (bdxtail == 0.0) && (cdxtail == 0.0) + && (adytail == 0.0) && (bdytail == 0.0) && (cdytail == 0.0)) { + return det; + } + + errbound = iccerrboundC * permanent + resulterrbound * Absolute(det); + det += ((adx * adx + ady * ady) * ((bdx * cdytail + cdy * bdxtail) + - (bdy * cdxtail + cdx * bdytail)) + + 2.0 * (adx * adxtail + ady * adytail) * (bdx * cdy - bdy * cdx)) + + ((bdx * bdx + bdy * bdy) * ((cdx * adytail + ady * cdxtail) + - (cdy * adxtail + adx * cdytail)) + + 2.0 * (bdx * bdxtail + bdy * bdytail) * (cdx * ady - cdy * adx)) + + ((cdx * cdx + cdy * cdy) * ((adx * bdytail + bdy * adxtail) + - (ady * bdxtail + bdx * adytail)) + + 2.0 * (cdx * cdxtail + cdy * cdytail) * (adx * bdy - ady * bdx)); + if ((det >= errbound) || (-det >= errbound)) { + return det; + } + + finnow = fin1; + finother = fin2; + + if ((bdxtail != 0.0) || (bdytail != 0.0) + || (cdxtail != 0.0) || (cdytail != 0.0)) { + Square(adx, adxadx1, adxadx0); + Square(ady, adyady1, adyady0); + Two_Two_Sum(adxadx1, adxadx0, adyady1, adyady0, aa3, aa[2], aa[1], aa[0]); + aa[3] = aa3; + } + if ((cdxtail != 0.0) || (cdytail != 0.0) + || (adxtail != 0.0) || (adytail != 0.0)) { + Square(bdx, bdxbdx1, bdxbdx0); + Square(bdy, bdybdy1, bdybdy0); + Two_Two_Sum(bdxbdx1, bdxbdx0, bdybdy1, bdybdy0, bb3, bb[2], bb[1], bb[0]); + bb[3] = bb3; + } + if ((adxtail != 0.0) || (adytail != 0.0) + || (bdxtail != 0.0) || (bdytail != 0.0)) { + Square(cdx, cdxcdx1, cdxcdx0); + Square(cdy, cdycdy1, cdycdy0); + Two_Two_Sum(cdxcdx1, cdxcdx0, cdycdy1, cdycdy0, cc3, cc[2], cc[1], cc[0]); + cc[3] = cc3; + } + + if (adxtail != 0.0) { + axtbclen = scale_expansion_zeroelim(4, bc, adxtail, axtbc); + temp16alen = scale_expansion_zeroelim(axtbclen, axtbc, 2.0 * adx, + temp16a); + + axtcclen = scale_expansion_zeroelim(4, cc, adxtail, axtcc); + temp16blen = scale_expansion_zeroelim(axtcclen, axtcc, bdy, temp16b); + + axtbblen = scale_expansion_zeroelim(4, bb, adxtail, axtbb); + temp16clen = scale_expansion_zeroelim(axtbblen, axtbb, -cdy, temp16c); + + temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a, + temp16blen, temp16b, temp32a); + temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c, + temp32alen, temp32a, temp48); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, + temp48, finother); + finswap = finnow; finnow = finother; finother = finswap; + } + if (adytail != 0.0) { + aytbclen = scale_expansion_zeroelim(4, bc, adytail, aytbc); + temp16alen = scale_expansion_zeroelim(aytbclen, aytbc, 2.0 * ady, + temp16a); + + aytbblen = scale_expansion_zeroelim(4, bb, adytail, aytbb); + temp16blen = scale_expansion_zeroelim(aytbblen, aytbb, cdx, temp16b); + + aytcclen = scale_expansion_zeroelim(4, cc, adytail, aytcc); + temp16clen = scale_expansion_zeroelim(aytcclen, aytcc, -bdx, temp16c); + + temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a, + temp16blen, temp16b, temp32a); + temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c, + temp32alen, temp32a, temp48); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, + temp48, finother); + finswap = finnow; finnow = finother; finother = finswap; + } + if (bdxtail != 0.0) { + bxtcalen = scale_expansion_zeroelim(4, ca, bdxtail, bxtca); + temp16alen = scale_expansion_zeroelim(bxtcalen, bxtca, 2.0 * bdx, + temp16a); + + bxtaalen = scale_expansion_zeroelim(4, aa, bdxtail, bxtaa); + temp16blen = scale_expansion_zeroelim(bxtaalen, bxtaa, cdy, temp16b); + + bxtcclen = scale_expansion_zeroelim(4, cc, bdxtail, bxtcc); + temp16clen = scale_expansion_zeroelim(bxtcclen, bxtcc, -ady, temp16c); + + temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a, + temp16blen, temp16b, temp32a); + temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c, + temp32alen, temp32a, temp48); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, + temp48, finother); + finswap = finnow; finnow = finother; finother = finswap; + } + if (bdytail != 0.0) { + bytcalen = scale_expansion_zeroelim(4, ca, bdytail, bytca); + temp16alen = scale_expansion_zeroelim(bytcalen, bytca, 2.0 * bdy, + temp16a); + + bytcclen = scale_expansion_zeroelim(4, cc, bdytail, bytcc); + temp16blen = scale_expansion_zeroelim(bytcclen, bytcc, adx, temp16b); + + bytaalen = scale_expansion_zeroelim(4, aa, bdytail, bytaa); + temp16clen = scale_expansion_zeroelim(bytaalen, bytaa, -cdx, temp16c); + + temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a, + temp16blen, temp16b, temp32a); + temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c, + temp32alen, temp32a, temp48); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, + temp48, finother); + finswap = finnow; finnow = finother; finother = finswap; + } + if (cdxtail != 0.0) { + cxtablen = scale_expansion_zeroelim(4, ab, cdxtail, cxtab); + temp16alen = scale_expansion_zeroelim(cxtablen, cxtab, 2.0 * cdx, + temp16a); + + cxtbblen = scale_expansion_zeroelim(4, bb, cdxtail, cxtbb); + temp16blen = scale_expansion_zeroelim(cxtbblen, cxtbb, ady, temp16b); + + cxtaalen = scale_expansion_zeroelim(4, aa, cdxtail, cxtaa); + temp16clen = scale_expansion_zeroelim(cxtaalen, cxtaa, -bdy, temp16c); + + temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a, + temp16blen, temp16b, temp32a); + temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c, + temp32alen, temp32a, temp48); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, + temp48, finother); + finswap = finnow; finnow = finother; finother = finswap; + } + if (cdytail != 0.0) { + cytablen = scale_expansion_zeroelim(4, ab, cdytail, cytab); + temp16alen = scale_expansion_zeroelim(cytablen, cytab, 2.0 * cdy, + temp16a); + + cytaalen = scale_expansion_zeroelim(4, aa, cdytail, cytaa); + temp16blen = scale_expansion_zeroelim(cytaalen, cytaa, bdx, temp16b); + + cytbblen = scale_expansion_zeroelim(4, bb, cdytail, cytbb); + temp16clen = scale_expansion_zeroelim(cytbblen, cytbb, -adx, temp16c); + + temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a, + temp16blen, temp16b, temp32a); + temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c, + temp32alen, temp32a, temp48); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, + temp48, finother); + finswap = finnow; finnow = finother; finother = finswap; + } + + if ((adxtail != 0.0) || (adytail != 0.0)) { + if ((bdxtail != 0.0) || (bdytail != 0.0) + || (cdxtail != 0.0) || (cdytail != 0.0)) { + Two_Product(bdxtail, cdy, ti1, ti0); + Two_Product(bdx, cdytail, tj1, tj0); + Two_Two_Sum(ti1, ti0, tj1, tj0, u3, u[2], u[1], u[0]); + u[3] = u3; + negate = -bdy; + Two_Product(cdxtail, negate, ti1, ti0); + negate = -bdytail; + Two_Product(cdx, negate, tj1, tj0); + Two_Two_Sum(ti1, ti0, tj1, tj0, v3, v[2], v[1], v[0]); + v[3] = v3; + bctlen = fast_expansion_sum_zeroelim(4, u, 4, v, bct); + + Two_Product(bdxtail, cdytail, ti1, ti0); + Two_Product(cdxtail, bdytail, tj1, tj0); + Two_Two_Diff(ti1, ti0, tj1, tj0, bctt3, bctt[2], bctt[1], bctt[0]); + bctt[3] = bctt3; + bcttlen = 4; + } else { + bct[0] = 0.0; + bctlen = 1; + bctt[0] = 0.0; + bcttlen = 1; + } + + if (adxtail != 0.0) { + temp16alen = scale_expansion_zeroelim(axtbclen, axtbc, adxtail, temp16a); + axtbctlen = scale_expansion_zeroelim(bctlen, bct, adxtail, axtbct); + temp32alen = scale_expansion_zeroelim(axtbctlen, axtbct, 2.0 * adx, + temp32a); + temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a, + temp32alen, temp32a, temp48); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, + temp48, finother); + finswap = finnow; finnow = finother; finother = finswap; + if (bdytail != 0.0) { + temp8len = scale_expansion_zeroelim(4, cc, adxtail, temp8); + temp16alen = scale_expansion_zeroelim(temp8len, temp8, bdytail, + temp16a); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen, + temp16a, finother); + finswap = finnow; finnow = finother; finother = finswap; + } + if (cdytail != 0.0) { + temp8len = scale_expansion_zeroelim(4, bb, -adxtail, temp8); + temp16alen = scale_expansion_zeroelim(temp8len, temp8, cdytail, + temp16a); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen, + temp16a, finother); + finswap = finnow; finnow = finother; finother = finswap; + } + + temp32alen = scale_expansion_zeroelim(axtbctlen, axtbct, adxtail, + temp32a); + axtbcttlen = scale_expansion_zeroelim(bcttlen, bctt, adxtail, axtbctt); + temp16alen = scale_expansion_zeroelim(axtbcttlen, axtbctt, 2.0 * adx, + temp16a); + temp16blen = scale_expansion_zeroelim(axtbcttlen, axtbctt, adxtail, + temp16b); + temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a, + temp16blen, temp16b, temp32b); + temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a, + temp32blen, temp32b, temp64); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len, + temp64, finother); + finswap = finnow; finnow = finother; finother = finswap; + } + if (adytail != 0.0) { + temp16alen = scale_expansion_zeroelim(aytbclen, aytbc, adytail, temp16a); + aytbctlen = scale_expansion_zeroelim(bctlen, bct, adytail, aytbct); + temp32alen = scale_expansion_zeroelim(aytbctlen, aytbct, 2.0 * ady, + temp32a); + temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a, + temp32alen, temp32a, temp48); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, + temp48, finother); + finswap = finnow; finnow = finother; finother = finswap; + + + temp32alen = scale_expansion_zeroelim(aytbctlen, aytbct, adytail, + temp32a); + aytbcttlen = scale_expansion_zeroelim(bcttlen, bctt, adytail, aytbctt); + temp16alen = scale_expansion_zeroelim(aytbcttlen, aytbctt, 2.0 * ady, + temp16a); + temp16blen = scale_expansion_zeroelim(aytbcttlen, aytbctt, adytail, + temp16b); + temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a, + temp16blen, temp16b, temp32b); + temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a, + temp32blen, temp32b, temp64); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len, + temp64, finother); + finswap = finnow; finnow = finother; finother = finswap; + } + } + if ((bdxtail != 0.0) || (bdytail != 0.0)) { + if ((cdxtail != 0.0) || (cdytail != 0.0) + || (adxtail != 0.0) || (adytail != 0.0)) { + Two_Product(cdxtail, ady, ti1, ti0); + Two_Product(cdx, adytail, tj1, tj0); + Two_Two_Sum(ti1, ti0, tj1, tj0, u3, u[2], u[1], u[0]); + u[3] = u3; + negate = -cdy; + Two_Product(adxtail, negate, ti1, ti0); + negate = -cdytail; + Two_Product(adx, negate, tj1, tj0); + Two_Two_Sum(ti1, ti0, tj1, tj0, v3, v[2], v[1], v[0]); + v[3] = v3; + catlen = fast_expansion_sum_zeroelim(4, u, 4, v, cat); + + Two_Product(cdxtail, adytail, ti1, ti0); + Two_Product(adxtail, cdytail, tj1, tj0); + Two_Two_Diff(ti1, ti0, tj1, tj0, catt3, catt[2], catt[1], catt[0]); + catt[3] = catt3; + cattlen = 4; + } else { + cat[0] = 0.0; + catlen = 1; + catt[0] = 0.0; + cattlen = 1; + } + + if (bdxtail != 0.0) { + temp16alen = scale_expansion_zeroelim(bxtcalen, bxtca, bdxtail, temp16a); + bxtcatlen = scale_expansion_zeroelim(catlen, cat, bdxtail, bxtcat); + temp32alen = scale_expansion_zeroelim(bxtcatlen, bxtcat, 2.0 * bdx, + temp32a); + temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a, + temp32alen, temp32a, temp48); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, + temp48, finother); + finswap = finnow; finnow = finother; finother = finswap; + if (cdytail != 0.0) { + temp8len = scale_expansion_zeroelim(4, aa, bdxtail, temp8); + temp16alen = scale_expansion_zeroelim(temp8len, temp8, cdytail, + temp16a); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen, + temp16a, finother); + finswap = finnow; finnow = finother; finother = finswap; + } + if (adytail != 0.0) { + temp8len = scale_expansion_zeroelim(4, cc, -bdxtail, temp8); + temp16alen = scale_expansion_zeroelim(temp8len, temp8, adytail, + temp16a); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen, + temp16a, finother); + finswap = finnow; finnow = finother; finother = finswap; + } + + temp32alen = scale_expansion_zeroelim(bxtcatlen, bxtcat, bdxtail, + temp32a); + bxtcattlen = scale_expansion_zeroelim(cattlen, catt, bdxtail, bxtcatt); + temp16alen = scale_expansion_zeroelim(bxtcattlen, bxtcatt, 2.0 * bdx, + temp16a); + temp16blen = scale_expansion_zeroelim(bxtcattlen, bxtcatt, bdxtail, + temp16b); + temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a, + temp16blen, temp16b, temp32b); + temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a, + temp32blen, temp32b, temp64); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len, + temp64, finother); + finswap = finnow; finnow = finother; finother = finswap; + } + if (bdytail != 0.0) { + temp16alen = scale_expansion_zeroelim(bytcalen, bytca, bdytail, temp16a); + bytcatlen = scale_expansion_zeroelim(catlen, cat, bdytail, bytcat); + temp32alen = scale_expansion_zeroelim(bytcatlen, bytcat, 2.0 * bdy, + temp32a); + temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a, + temp32alen, temp32a, temp48); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, + temp48, finother); + finswap = finnow; finnow = finother; finother = finswap; + + + temp32alen = scale_expansion_zeroelim(bytcatlen, bytcat, bdytail, + temp32a); + bytcattlen = scale_expansion_zeroelim(cattlen, catt, bdytail, bytcatt); + temp16alen = scale_expansion_zeroelim(bytcattlen, bytcatt, 2.0 * bdy, + temp16a); + temp16blen = scale_expansion_zeroelim(bytcattlen, bytcatt, bdytail, + temp16b); + temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a, + temp16blen, temp16b, temp32b); + temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a, + temp32blen, temp32b, temp64); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len, + temp64, finother); + finswap = finnow; finnow = finother; finother = finswap; + } + } + if ((cdxtail != 0.0) || (cdytail != 0.0)) { + if ((adxtail != 0.0) || (adytail != 0.0) + || (bdxtail != 0.0) || (bdytail != 0.0)) { + Two_Product(adxtail, bdy, ti1, ti0); + Two_Product(adx, bdytail, tj1, tj0); + Two_Two_Sum(ti1, ti0, tj1, tj0, u3, u[2], u[1], u[0]); + u[3] = u3; + negate = -ady; + Two_Product(bdxtail, negate, ti1, ti0); + negate = -adytail; + Two_Product(bdx, negate, tj1, tj0); + Two_Two_Sum(ti1, ti0, tj1, tj0, v3, v[2], v[1], v[0]); + v[3] = v3; + abtlen = fast_expansion_sum_zeroelim(4, u, 4, v, abt); + + Two_Product(adxtail, bdytail, ti1, ti0); + Two_Product(bdxtail, adytail, tj1, tj0); + Two_Two_Diff(ti1, ti0, tj1, tj0, abtt3, abtt[2], abtt[1], abtt[0]); + abtt[3] = abtt3; + abttlen = 4; + } else { + abt[0] = 0.0; + abtlen = 1; + abtt[0] = 0.0; + abttlen = 1; + } + + if (cdxtail != 0.0) { + temp16alen = scale_expansion_zeroelim(cxtablen, cxtab, cdxtail, temp16a); + cxtabtlen = scale_expansion_zeroelim(abtlen, abt, cdxtail, cxtabt); + temp32alen = scale_expansion_zeroelim(cxtabtlen, cxtabt, 2.0 * cdx, + temp32a); + temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a, + temp32alen, temp32a, temp48); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, + temp48, finother); + finswap = finnow; finnow = finother; finother = finswap; + if (adytail != 0.0) { + temp8len = scale_expansion_zeroelim(4, bb, cdxtail, temp8); + temp16alen = scale_expansion_zeroelim(temp8len, temp8, adytail, + temp16a); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen, + temp16a, finother); + finswap = finnow; finnow = finother; finother = finswap; + } + if (bdytail != 0.0) { + temp8len = scale_expansion_zeroelim(4, aa, -cdxtail, temp8); + temp16alen = scale_expansion_zeroelim(temp8len, temp8, bdytail, + temp16a); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen, + temp16a, finother); + finswap = finnow; finnow = finother; finother = finswap; + } + + temp32alen = scale_expansion_zeroelim(cxtabtlen, cxtabt, cdxtail, + temp32a); + cxtabttlen = scale_expansion_zeroelim(abttlen, abtt, cdxtail, cxtabtt); + temp16alen = scale_expansion_zeroelim(cxtabttlen, cxtabtt, 2.0 * cdx, + temp16a); + temp16blen = scale_expansion_zeroelim(cxtabttlen, cxtabtt, cdxtail, + temp16b); + temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a, + temp16blen, temp16b, temp32b); + temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a, + temp32blen, temp32b, temp64); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len, + temp64, finother); + finswap = finnow; finnow = finother; finother = finswap; + } + if (cdytail != 0.0) { + temp16alen = scale_expansion_zeroelim(cytablen, cytab, cdytail, temp16a); + cytabtlen = scale_expansion_zeroelim(abtlen, abt, cdytail, cytabt); + temp32alen = scale_expansion_zeroelim(cytabtlen, cytabt, 2.0 * cdy, + temp32a); + temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a, + temp32alen, temp32a, temp48); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, + temp48, finother); + finswap = finnow; finnow = finother; finother = finswap; + + + temp32alen = scale_expansion_zeroelim(cytabtlen, cytabt, cdytail, + temp32a); + cytabttlen = scale_expansion_zeroelim(abttlen, abtt, cdytail, cytabtt); + temp16alen = scale_expansion_zeroelim(cytabttlen, cytabtt, 2.0 * cdy, + temp16a); + temp16blen = scale_expansion_zeroelim(cytabttlen, cytabtt, cdytail, + temp16b); + temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a, + temp16blen, temp16b, temp32b); + temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a, + temp32blen, temp32b, temp64); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len, + temp64, finother); + finswap = finnow; finnow = finother; finother = finswap; + } + } + + return finnow[finlength - 1]; +} + +REAL incircle(REAL *pa, REAL *pb, REAL *pc, REAL *pd) +{ + REAL adx, bdx, cdx, ady, bdy, cdy; + REAL bdxcdy, cdxbdy, cdxady, adxcdy, adxbdy, bdxady; + REAL alift, blift, clift; + REAL det; + REAL permanent, errbound; + + adx = pa[0] - pd[0]; + bdx = pb[0] - pd[0]; + cdx = pc[0] - pd[0]; + ady = pa[1] - pd[1]; + bdy = pb[1] - pd[1]; + cdy = pc[1] - pd[1]; + + bdxcdy = bdx * cdy; + cdxbdy = cdx * bdy; + alift = adx * adx + ady * ady; + + cdxady = cdx * ady; + adxcdy = adx * cdy; + blift = bdx * bdx + bdy * bdy; + + adxbdy = adx * bdy; + bdxady = bdx * ady; + clift = cdx * cdx + cdy * cdy; + + det = alift * (bdxcdy - cdxbdy) + + blift * (cdxady - adxcdy) + + clift * (adxbdy - bdxady); + + permanent = (Absolute(bdxcdy) + Absolute(cdxbdy)) * alift + + (Absolute(cdxady) + Absolute(adxcdy)) * blift + + (Absolute(adxbdy) + Absolute(bdxady)) * clift; + errbound = iccerrboundA * permanent; + if ((det > errbound) || (-det > errbound)) { + return det; + } + + return incircleadapt(pa, pb, pc, pd, permanent); +} + +/*****************************************************************************/ +/* */ +/* inspherefast() Approximate 3D insphere test. Nonrobust. */ +/* insphereexact() Exact 3D insphere test. Robust. */ +/* insphereslow() Another exact 3D insphere test. Robust. */ +/* insphere() Adaptive exact 3D insphere test. Robust. */ +/* */ +/* Return a positive value if the point pe lies inside the */ +/* sphere passing through pa, pb, pc, and pd; a negative value */ +/* if it lies outside; and zero if the five points are */ +/* cospherical. The points pa, pb, pc, and pd must be ordered */ +/* so that they have a positive orientation (as defined by */ +/* orient3d()), or the sign of the result will be reversed. */ +/* */ +/* Only the first and last routine should be used; the middle two are for */ +/* timings. */ +/* */ +/* The last three use exact arithmetic to ensure a correct answer. The */ +/* result returned is the determinant of a matrix. In insphere() only, */ +/* this determinant is computed adaptively, in the sense that exact */ +/* arithmetic is used only to the degree it is needed to ensure that the */ +/* returned value has the correct sign. Hence, insphere() is usually quite */ +/* fast, but will run more slowly when the input points are cospherical or */ +/* nearly so. */ +/* */ +/*****************************************************************************/ + +REAL inspherefast(REAL *pa, REAL *pb, REAL *pc, REAL *pd, REAL *pe) +{ + REAL aex, bex, cex, dex; + REAL aey, bey, cey, dey; + REAL aez, bez, cez, dez; + REAL alift, blift, clift, dlift; + REAL ab, bc, cd, da, ac, bd; + REAL abc, bcd, cda, dab; + + aex = pa[0] - pe[0]; + bex = pb[0] - pe[0]; + cex = pc[0] - pe[0]; + dex = pd[0] - pe[0]; + aey = pa[1] - pe[1]; + bey = pb[1] - pe[1]; + cey = pc[1] - pe[1]; + dey = pd[1] - pe[1]; + aez = pa[2] - pe[2]; + bez = pb[2] - pe[2]; + cez = pc[2] - pe[2]; + dez = pd[2] - pe[2]; + + ab = aex * bey - bex * aey; + bc = bex * cey - cex * bey; + cd = cex * dey - dex * cey; + da = dex * aey - aex * dey; + + ac = aex * cey - cex * aey; + bd = bex * dey - dex * bey; + + abc = aez * bc - bez * ac + cez * ab; + bcd = bez * cd - cez * bd + dez * bc; + cda = cez * da + dez * ac + aez * cd; + dab = dez * ab + aez * bd + bez * da; + + alift = aex * aex + aey * aey + aez * aez; + blift = bex * bex + bey * bey + bez * bez; + clift = cex * cex + cey * cey + cez * cez; + dlift = dex * dex + dey * dey + dez * dez; + + return (dlift * abc - clift * dab) + (blift * cda - alift * bcd); +} + +REAL insphereexact(REAL *pa, REAL *pb, REAL *pc, REAL *pd, REAL *pe) +{ + INEXACT REAL axby1, bxcy1, cxdy1, dxey1, exay1; + INEXACT REAL bxay1, cxby1, dxcy1, exdy1, axey1; + INEXACT REAL axcy1, bxdy1, cxey1, dxay1, exby1; + INEXACT REAL cxay1, dxby1, excy1, axdy1, bxey1; + REAL axby0, bxcy0, cxdy0, dxey0, exay0; + REAL bxay0, cxby0, dxcy0, exdy0, axey0; + REAL axcy0, bxdy0, cxey0, dxay0, exby0; + REAL cxay0, dxby0, excy0, axdy0, bxey0; + REAL ab[4], bc[4], cd[4], de[4], ea[4]; + REAL ac[4], bd[4], ce[4], da[4], eb[4]; + REAL temp8a[8], temp8b[8], temp16[16]; + int temp8alen, temp8blen, temp16len; + REAL abc[24], bcd[24], cde[24], dea[24], eab[24]; + REAL abd[24], bce[24], cda[24], deb[24], eac[24]; + int abclen, bcdlen, cdelen, dealen, eablen; + int abdlen, bcelen, cdalen, deblen, eaclen; + REAL temp48a[48], temp48b[48]; + int temp48alen, temp48blen; + REAL abcd[96], bcde[96], cdea[96], deab[96], eabc[96]; + int abcdlen, bcdelen, cdealen, deablen, eabclen; + REAL temp192[192]; + REAL det384x[384], det384y[384], det384z[384]; + int xlen, ylen, zlen; + REAL detxy[768]; + int xylen; + REAL adet[1152], bdet[1152], cdet[1152], ddet[1152], edet[1152]; + int alen, blen, clen, dlen, elen; + REAL abdet[2304], cddet[2304], cdedet[3456]; + int ablen, cdlen; + REAL deter[5760]; + int deterlen; + int i; + + INEXACT REAL bvirt; + REAL avirt, bround, around; + INEXACT REAL c; + INEXACT REAL abig; + REAL ahi, alo, bhi, blo; + REAL err1, err2, err3; + INEXACT REAL _i, _j; + REAL _0; + + Two_Product(pa[0], pb[1], axby1, axby0); + Two_Product(pb[0], pa[1], bxay1, bxay0); + Two_Two_Diff(axby1, axby0, bxay1, bxay0, ab[3], ab[2], ab[1], ab[0]); + + Two_Product(pb[0], pc[1], bxcy1, bxcy0); + Two_Product(pc[0], pb[1], cxby1, cxby0); + Two_Two_Diff(bxcy1, bxcy0, cxby1, cxby0, bc[3], bc[2], bc[1], bc[0]); + + Two_Product(pc[0], pd[1], cxdy1, cxdy0); + Two_Product(pd[0], pc[1], dxcy1, dxcy0); + Two_Two_Diff(cxdy1, cxdy0, dxcy1, dxcy0, cd[3], cd[2], cd[1], cd[0]); + + Two_Product(pd[0], pe[1], dxey1, dxey0); + Two_Product(pe[0], pd[1], exdy1, exdy0); + Two_Two_Diff(dxey1, dxey0, exdy1, exdy0, de[3], de[2], de[1], de[0]); + + Two_Product(pe[0], pa[1], exay1, exay0); + Two_Product(pa[0], pe[1], axey1, axey0); + Two_Two_Diff(exay1, exay0, axey1, axey0, ea[3], ea[2], ea[1], ea[0]); + + Two_Product(pa[0], pc[1], axcy1, axcy0); + Two_Product(pc[0], pa[1], cxay1, cxay0); + Two_Two_Diff(axcy1, axcy0, cxay1, cxay0, ac[3], ac[2], ac[1], ac[0]); + + Two_Product(pb[0], pd[1], bxdy1, bxdy0); + Two_Product(pd[0], pb[1], dxby1, dxby0); + Two_Two_Diff(bxdy1, bxdy0, dxby1, dxby0, bd[3], bd[2], bd[1], bd[0]); + + Two_Product(pc[0], pe[1], cxey1, cxey0); + Two_Product(pe[0], pc[1], excy1, excy0); + Two_Two_Diff(cxey1, cxey0, excy1, excy0, ce[3], ce[2], ce[1], ce[0]); + + Two_Product(pd[0], pa[1], dxay1, dxay0); + Two_Product(pa[0], pd[1], axdy1, axdy0); + Two_Two_Diff(dxay1, dxay0, axdy1, axdy0, da[3], da[2], da[1], da[0]); + + Two_Product(pe[0], pb[1], exby1, exby0); + Two_Product(pb[0], pe[1], bxey1, bxey0); + Two_Two_Diff(exby1, exby0, bxey1, bxey0, eb[3], eb[2], eb[1], eb[0]); + + temp8alen = scale_expansion_zeroelim(4, bc, pa[2], temp8a); + temp8blen = scale_expansion_zeroelim(4, ac, -pb[2], temp8b); + temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b, + temp16); + temp8alen = scale_expansion_zeroelim(4, ab, pc[2], temp8a); + abclen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16, + abc); + + temp8alen = scale_expansion_zeroelim(4, cd, pb[2], temp8a); + temp8blen = scale_expansion_zeroelim(4, bd, -pc[2], temp8b); + temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b, + temp16); + temp8alen = scale_expansion_zeroelim(4, bc, pd[2], temp8a); + bcdlen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16, + bcd); + + temp8alen = scale_expansion_zeroelim(4, de, pc[2], temp8a); + temp8blen = scale_expansion_zeroelim(4, ce, -pd[2], temp8b); + temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b, + temp16); + temp8alen = scale_expansion_zeroelim(4, cd, pe[2], temp8a); + cdelen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16, + cde); + + temp8alen = scale_expansion_zeroelim(4, ea, pd[2], temp8a); + temp8blen = scale_expansion_zeroelim(4, da, -pe[2], temp8b); + temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b, + temp16); + temp8alen = scale_expansion_zeroelim(4, de, pa[2], temp8a); + dealen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16, + dea); + + temp8alen = scale_expansion_zeroelim(4, ab, pe[2], temp8a); + temp8blen = scale_expansion_zeroelim(4, eb, -pa[2], temp8b); + temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b, + temp16); + temp8alen = scale_expansion_zeroelim(4, ea, pb[2], temp8a); + eablen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16, + eab); + + temp8alen = scale_expansion_zeroelim(4, bd, pa[2], temp8a); + temp8blen = scale_expansion_zeroelim(4, da, pb[2], temp8b); + temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b, + temp16); + temp8alen = scale_expansion_zeroelim(4, ab, pd[2], temp8a); + abdlen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16, + abd); + + temp8alen = scale_expansion_zeroelim(4, ce, pb[2], temp8a); + temp8blen = scale_expansion_zeroelim(4, eb, pc[2], temp8b); + temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b, + temp16); + temp8alen = scale_expansion_zeroelim(4, bc, pe[2], temp8a); + bcelen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16, + bce); + + temp8alen = scale_expansion_zeroelim(4, da, pc[2], temp8a); + temp8blen = scale_expansion_zeroelim(4, ac, pd[2], temp8b); + temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b, + temp16); + temp8alen = scale_expansion_zeroelim(4, cd, pa[2], temp8a); + cdalen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16, + cda); + + temp8alen = scale_expansion_zeroelim(4, eb, pd[2], temp8a); + temp8blen = scale_expansion_zeroelim(4, bd, pe[2], temp8b); + temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b, + temp16); + temp8alen = scale_expansion_zeroelim(4, de, pb[2], temp8a); + deblen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16, + deb); + + temp8alen = scale_expansion_zeroelim(4, ac, pe[2], temp8a); + temp8blen = scale_expansion_zeroelim(4, ce, pa[2], temp8b); + temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b, + temp16); + temp8alen = scale_expansion_zeroelim(4, ea, pc[2], temp8a); + eaclen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16, + eac); + + temp48alen = fast_expansion_sum_zeroelim(cdelen, cde, bcelen, bce, temp48a); + temp48blen = fast_expansion_sum_zeroelim(deblen, deb, bcdlen, bcd, temp48b); + for (i = 0; i < temp48blen; i++) { + temp48b[i] = -temp48b[i]; + } + bcdelen = fast_expansion_sum_zeroelim(temp48alen, temp48a, + temp48blen, temp48b, bcde); + xlen = scale_expansion_zeroelim(bcdelen, bcde, pa[0], temp192); + xlen = scale_expansion_zeroelim(xlen, temp192, pa[0], det384x); + ylen = scale_expansion_zeroelim(bcdelen, bcde, pa[1], temp192); + ylen = scale_expansion_zeroelim(ylen, temp192, pa[1], det384y); + zlen = scale_expansion_zeroelim(bcdelen, bcde, pa[2], temp192); + zlen = scale_expansion_zeroelim(zlen, temp192, pa[2], det384z); + xylen = fast_expansion_sum_zeroelim(xlen, det384x, ylen, det384y, detxy); + alen = fast_expansion_sum_zeroelim(xylen, detxy, zlen, det384z, adet); + + temp48alen = fast_expansion_sum_zeroelim(dealen, dea, cdalen, cda, temp48a); + temp48blen = fast_expansion_sum_zeroelim(eaclen, eac, cdelen, cde, temp48b); + for (i = 0; i < temp48blen; i++) { + temp48b[i] = -temp48b[i]; + } + cdealen = fast_expansion_sum_zeroelim(temp48alen, temp48a, + temp48blen, temp48b, cdea); + xlen = scale_expansion_zeroelim(cdealen, cdea, pb[0], temp192); + xlen = scale_expansion_zeroelim(xlen, temp192, pb[0], det384x); + ylen = scale_expansion_zeroelim(cdealen, cdea, pb[1], temp192); + ylen = scale_expansion_zeroelim(ylen, temp192, pb[1], det384y); + zlen = scale_expansion_zeroelim(cdealen, cdea, pb[2], temp192); + zlen = scale_expansion_zeroelim(zlen, temp192, pb[2], det384z); + xylen = fast_expansion_sum_zeroelim(xlen, det384x, ylen, det384y, detxy); + blen = fast_expansion_sum_zeroelim(xylen, detxy, zlen, det384z, bdet); + + temp48alen = fast_expansion_sum_zeroelim(eablen, eab, deblen, deb, temp48a); + temp48blen = fast_expansion_sum_zeroelim(abdlen, abd, dealen, dea, temp48b); + for (i = 0; i < temp48blen; i++) { + temp48b[i] = -temp48b[i]; + } + deablen = fast_expansion_sum_zeroelim(temp48alen, temp48a, + temp48blen, temp48b, deab); + xlen = scale_expansion_zeroelim(deablen, deab, pc[0], temp192); + xlen = scale_expansion_zeroelim(xlen, temp192, pc[0], det384x); + ylen = scale_expansion_zeroelim(deablen, deab, pc[1], temp192); + ylen = scale_expansion_zeroelim(ylen, temp192, pc[1], det384y); + zlen = scale_expansion_zeroelim(deablen, deab, pc[2], temp192); + zlen = scale_expansion_zeroelim(zlen, temp192, pc[2], det384z); + xylen = fast_expansion_sum_zeroelim(xlen, det384x, ylen, det384y, detxy); + clen = fast_expansion_sum_zeroelim(xylen, detxy, zlen, det384z, cdet); + + temp48alen = fast_expansion_sum_zeroelim(abclen, abc, eaclen, eac, temp48a); + temp48blen = fast_expansion_sum_zeroelim(bcelen, bce, eablen, eab, temp48b); + for (i = 0; i < temp48blen; i++) { + temp48b[i] = -temp48b[i]; + } + eabclen = fast_expansion_sum_zeroelim(temp48alen, temp48a, + temp48blen, temp48b, eabc); + xlen = scale_expansion_zeroelim(eabclen, eabc, pd[0], temp192); + xlen = scale_expansion_zeroelim(xlen, temp192, pd[0], det384x); + ylen = scale_expansion_zeroelim(eabclen, eabc, pd[1], temp192); + ylen = scale_expansion_zeroelim(ylen, temp192, pd[1], det384y); + zlen = scale_expansion_zeroelim(eabclen, eabc, pd[2], temp192); + zlen = scale_expansion_zeroelim(zlen, temp192, pd[2], det384z); + xylen = fast_expansion_sum_zeroelim(xlen, det384x, ylen, det384y, detxy); + dlen = fast_expansion_sum_zeroelim(xylen, detxy, zlen, det384z, ddet); + + temp48alen = fast_expansion_sum_zeroelim(bcdlen, bcd, abdlen, abd, temp48a); + temp48blen = fast_expansion_sum_zeroelim(cdalen, cda, abclen, abc, temp48b); + for (i = 0; i < temp48blen; i++) { + temp48b[i] = -temp48b[i]; + } + abcdlen = fast_expansion_sum_zeroelim(temp48alen, temp48a, + temp48blen, temp48b, abcd); + xlen = scale_expansion_zeroelim(abcdlen, abcd, pe[0], temp192); + xlen = scale_expansion_zeroelim(xlen, temp192, pe[0], det384x); + ylen = scale_expansion_zeroelim(abcdlen, abcd, pe[1], temp192); + ylen = scale_expansion_zeroelim(ylen, temp192, pe[1], det384y); + zlen = scale_expansion_zeroelim(abcdlen, abcd, pe[2], temp192); + zlen = scale_expansion_zeroelim(zlen, temp192, pe[2], det384z); + xylen = fast_expansion_sum_zeroelim(xlen, det384x, ylen, det384y, detxy); + elen = fast_expansion_sum_zeroelim(xylen, detxy, zlen, det384z, edet); + + ablen = fast_expansion_sum_zeroelim(alen, adet, blen, bdet, abdet); + cdlen = fast_expansion_sum_zeroelim(clen, cdet, dlen, ddet, cddet); + cdelen = fast_expansion_sum_zeroelim(cdlen, cddet, elen, edet, cdedet); + deterlen = fast_expansion_sum_zeroelim(ablen, abdet, cdelen, cdedet, deter); + + return deter[deterlen - 1]; +} + +REAL insphereslow(REAL *pa, REAL *pb, REAL *pc, REAL *pd, REAL *pe) +{ + INEXACT REAL aex, bex, cex, dex, aey, bey, cey, dey, aez, bez, cez, dez; + REAL aextail, bextail, cextail, dextail; + REAL aeytail, beytail, ceytail, deytail; + REAL aeztail, beztail, ceztail, deztail; + REAL negate, negatetail; + INEXACT REAL axby7, bxcy7, cxdy7, dxay7, axcy7, bxdy7; + INEXACT REAL bxay7, cxby7, dxcy7, axdy7, cxay7, dxby7; + REAL axby[8], bxcy[8], cxdy[8], dxay[8], axcy[8], bxdy[8]; + REAL bxay[8], cxby[8], dxcy[8], axdy[8], cxay[8], dxby[8]; + REAL ab[16], bc[16], cd[16], da[16], ac[16], bd[16]; + int ablen, bclen, cdlen, dalen, aclen, bdlen; + REAL temp32a[32], temp32b[32], temp64a[64], temp64b[64], temp64c[64]; + int temp32alen, temp32blen, temp64alen, temp64blen, temp64clen; + REAL temp128[128], temp192[192]; + int temp128len, temp192len; + REAL detx[384], detxx[768], detxt[384], detxxt[768], detxtxt[768]; + int xlen, xxlen, xtlen, xxtlen, xtxtlen; + REAL x1[1536], x2[2304]; + int x1len, x2len; + REAL dety[384], detyy[768], detyt[384], detyyt[768], detytyt[768]; + int ylen, yylen, ytlen, yytlen, ytytlen; + REAL y1[1536], y2[2304]; + int y1len, y2len; + REAL detz[384], detzz[768], detzt[384], detzzt[768], detztzt[768]; + int zlen, zzlen, ztlen, zztlen, ztztlen; + REAL z1[1536], z2[2304]; + int z1len, z2len; + REAL detxy[4608]; + int xylen; + REAL adet[6912], bdet[6912], cdet[6912], ddet[6912]; + int alen, blen, clen, dlen; + REAL abdet[13824], cddet[13824], deter[27648]; + int deterlen; + int i; + + INEXACT REAL bvirt; + REAL avirt, bround, around; + INEXACT REAL c; + INEXACT REAL abig; + REAL a0hi, a0lo, a1hi, a1lo, bhi, blo; + REAL err1, err2, err3; + INEXACT REAL _i, _j, _k, _l, _m, _n; + REAL _0, _1, _2; + + Two_Diff(pa[0], pe[0], aex, aextail); + Two_Diff(pa[1], pe[1], aey, aeytail); + Two_Diff(pa[2], pe[2], aez, aeztail); + Two_Diff(pb[0], pe[0], bex, bextail); + Two_Diff(pb[1], pe[1], bey, beytail); + Two_Diff(pb[2], pe[2], bez, beztail); + Two_Diff(pc[0], pe[0], cex, cextail); + Two_Diff(pc[1], pe[1], cey, ceytail); + Two_Diff(pc[2], pe[2], cez, ceztail); + Two_Diff(pd[0], pe[0], dex, dextail); + Two_Diff(pd[1], pe[1], dey, deytail); + Two_Diff(pd[2], pe[2], dez, deztail); + + Two_Two_Product(aex, aextail, bey, beytail, + axby7, axby[6], axby[5], axby[4], + axby[3], axby[2], axby[1], axby[0]); + axby[7] = axby7; + negate = -aey; + negatetail = -aeytail; + Two_Two_Product(bex, bextail, negate, negatetail, + bxay7, bxay[6], bxay[5], bxay[4], + bxay[3], bxay[2], bxay[1], bxay[0]); + bxay[7] = bxay7; + ablen = fast_expansion_sum_zeroelim(8, axby, 8, bxay, ab); + Two_Two_Product(bex, bextail, cey, ceytail, + bxcy7, bxcy[6], bxcy[5], bxcy[4], + bxcy[3], bxcy[2], bxcy[1], bxcy[0]); + bxcy[7] = bxcy7; + negate = -bey; + negatetail = -beytail; + Two_Two_Product(cex, cextail, negate, negatetail, + cxby7, cxby[6], cxby[5], cxby[4], + cxby[3], cxby[2], cxby[1], cxby[0]); + cxby[7] = cxby7; + bclen = fast_expansion_sum_zeroelim(8, bxcy, 8, cxby, bc); + Two_Two_Product(cex, cextail, dey, deytail, + cxdy7, cxdy[6], cxdy[5], cxdy[4], + cxdy[3], cxdy[2], cxdy[1], cxdy[0]); + cxdy[7] = cxdy7; + negate = -cey; + negatetail = -ceytail; + Two_Two_Product(dex, dextail, negate, negatetail, + dxcy7, dxcy[6], dxcy[5], dxcy[4], + dxcy[3], dxcy[2], dxcy[1], dxcy[0]); + dxcy[7] = dxcy7; + cdlen = fast_expansion_sum_zeroelim(8, cxdy, 8, dxcy, cd); + Two_Two_Product(dex, dextail, aey, aeytail, + dxay7, dxay[6], dxay[5], dxay[4], + dxay[3], dxay[2], dxay[1], dxay[0]); + dxay[7] = dxay7; + negate = -dey; + negatetail = -deytail; + Two_Two_Product(aex, aextail, negate, negatetail, + axdy7, axdy[6], axdy[5], axdy[4], + axdy[3], axdy[2], axdy[1], axdy[0]); + axdy[7] = axdy7; + dalen = fast_expansion_sum_zeroelim(8, dxay, 8, axdy, da); + Two_Two_Product(aex, aextail, cey, ceytail, + axcy7, axcy[6], axcy[5], axcy[4], + axcy[3], axcy[2], axcy[1], axcy[0]); + axcy[7] = axcy7; + negate = -aey; + negatetail = -aeytail; + Two_Two_Product(cex, cextail, negate, negatetail, + cxay7, cxay[6], cxay[5], cxay[4], + cxay[3], cxay[2], cxay[1], cxay[0]); + cxay[7] = cxay7; + aclen = fast_expansion_sum_zeroelim(8, axcy, 8, cxay, ac); + Two_Two_Product(bex, bextail, dey, deytail, + bxdy7, bxdy[6], bxdy[5], bxdy[4], + bxdy[3], bxdy[2], bxdy[1], bxdy[0]); + bxdy[7] = bxdy7; + negate = -bey; + negatetail = -beytail; + Two_Two_Product(dex, dextail, negate, negatetail, + dxby7, dxby[6], dxby[5], dxby[4], + dxby[3], dxby[2], dxby[1], dxby[0]); + dxby[7] = dxby7; + bdlen = fast_expansion_sum_zeroelim(8, bxdy, 8, dxby, bd); + + temp32alen = scale_expansion_zeroelim(cdlen, cd, -bez, temp32a); + temp32blen = scale_expansion_zeroelim(cdlen, cd, -beztail, temp32b); + temp64alen = fast_expansion_sum_zeroelim(temp32alen, temp32a, + temp32blen, temp32b, temp64a); + temp32alen = scale_expansion_zeroelim(bdlen, bd, cez, temp32a); + temp32blen = scale_expansion_zeroelim(bdlen, bd, ceztail, temp32b); + temp64blen = fast_expansion_sum_zeroelim(temp32alen, temp32a, + temp32blen, temp32b, temp64b); + temp32alen = scale_expansion_zeroelim(bclen, bc, -dez, temp32a); + temp32blen = scale_expansion_zeroelim(bclen, bc, -deztail, temp32b); + temp64clen = fast_expansion_sum_zeroelim(temp32alen, temp32a, + temp32blen, temp32b, temp64c); + temp128len = fast_expansion_sum_zeroelim(temp64alen, temp64a, + temp64blen, temp64b, temp128); + temp192len = fast_expansion_sum_zeroelim(temp64clen, temp64c, + temp128len, temp128, temp192); + xlen = scale_expansion_zeroelim(temp192len, temp192, aex, detx); + xxlen = scale_expansion_zeroelim(xlen, detx, aex, detxx); + xtlen = scale_expansion_zeroelim(temp192len, temp192, aextail, detxt); + xxtlen = scale_expansion_zeroelim(xtlen, detxt, aex, detxxt); + for (i = 0; i < xxtlen; i++) { + detxxt[i] *= 2.0; + } + xtxtlen = scale_expansion_zeroelim(xtlen, detxt, aextail, detxtxt); + x1len = fast_expansion_sum_zeroelim(xxlen, detxx, xxtlen, detxxt, x1); + x2len = fast_expansion_sum_zeroelim(x1len, x1, xtxtlen, detxtxt, x2); + ylen = scale_expansion_zeroelim(temp192len, temp192, aey, dety); + yylen = scale_expansion_zeroelim(ylen, dety, aey, detyy); + ytlen = scale_expansion_zeroelim(temp192len, temp192, aeytail, detyt); + yytlen = scale_expansion_zeroelim(ytlen, detyt, aey, detyyt); + for (i = 0; i < yytlen; i++) { + detyyt[i] *= 2.0; + } + ytytlen = scale_expansion_zeroelim(ytlen, detyt, aeytail, detytyt); + y1len = fast_expansion_sum_zeroelim(yylen, detyy, yytlen, detyyt, y1); + y2len = fast_expansion_sum_zeroelim(y1len, y1, ytytlen, detytyt, y2); + zlen = scale_expansion_zeroelim(temp192len, temp192, aez, detz); + zzlen = scale_expansion_zeroelim(zlen, detz, aez, detzz); + ztlen = scale_expansion_zeroelim(temp192len, temp192, aeztail, detzt); + zztlen = scale_expansion_zeroelim(ztlen, detzt, aez, detzzt); + for (i = 0; i < zztlen; i++) { + detzzt[i] *= 2.0; + } + ztztlen = scale_expansion_zeroelim(ztlen, detzt, aeztail, detztzt); + z1len = fast_expansion_sum_zeroelim(zzlen, detzz, zztlen, detzzt, z1); + z2len = fast_expansion_sum_zeroelim(z1len, z1, ztztlen, detztzt, z2); + xylen = fast_expansion_sum_zeroelim(x2len, x2, y2len, y2, detxy); + alen = fast_expansion_sum_zeroelim(z2len, z2, xylen, detxy, adet); + + temp32alen = scale_expansion_zeroelim(dalen, da, cez, temp32a); + temp32blen = scale_expansion_zeroelim(dalen, da, ceztail, temp32b); + temp64alen = fast_expansion_sum_zeroelim(temp32alen, temp32a, + temp32blen, temp32b, temp64a); + temp32alen = scale_expansion_zeroelim(aclen, ac, dez, temp32a); + temp32blen = scale_expansion_zeroelim(aclen, ac, deztail, temp32b); + temp64blen = fast_expansion_sum_zeroelim(temp32alen, temp32a, + temp32blen, temp32b, temp64b); + temp32alen = scale_expansion_zeroelim(cdlen, cd, aez, temp32a); + temp32blen = scale_expansion_zeroelim(cdlen, cd, aeztail, temp32b); + temp64clen = fast_expansion_sum_zeroelim(temp32alen, temp32a, + temp32blen, temp32b, temp64c); + temp128len = fast_expansion_sum_zeroelim(temp64alen, temp64a, + temp64blen, temp64b, temp128); + temp192len = fast_expansion_sum_zeroelim(temp64clen, temp64c, + temp128len, temp128, temp192); + xlen = scale_expansion_zeroelim(temp192len, temp192, bex, detx); + xxlen = scale_expansion_zeroelim(xlen, detx, bex, detxx); + xtlen = scale_expansion_zeroelim(temp192len, temp192, bextail, detxt); + xxtlen = scale_expansion_zeroelim(xtlen, detxt, bex, detxxt); + for (i = 0; i < xxtlen; i++) { + detxxt[i] *= 2.0; + } + xtxtlen = scale_expansion_zeroelim(xtlen, detxt, bextail, detxtxt); + x1len = fast_expansion_sum_zeroelim(xxlen, detxx, xxtlen, detxxt, x1); + x2len = fast_expansion_sum_zeroelim(x1len, x1, xtxtlen, detxtxt, x2); + ylen = scale_expansion_zeroelim(temp192len, temp192, bey, dety); + yylen = scale_expansion_zeroelim(ylen, dety, bey, detyy); + ytlen = scale_expansion_zeroelim(temp192len, temp192, beytail, detyt); + yytlen = scale_expansion_zeroelim(ytlen, detyt, bey, detyyt); + for (i = 0; i < yytlen; i++) { + detyyt[i] *= 2.0; + } + ytytlen = scale_expansion_zeroelim(ytlen, detyt, beytail, detytyt); + y1len = fast_expansion_sum_zeroelim(yylen, detyy, yytlen, detyyt, y1); + y2len = fast_expansion_sum_zeroelim(y1len, y1, ytytlen, detytyt, y2); + zlen = scale_expansion_zeroelim(temp192len, temp192, bez, detz); + zzlen = scale_expansion_zeroelim(zlen, detz, bez, detzz); + ztlen = scale_expansion_zeroelim(temp192len, temp192, beztail, detzt); + zztlen = scale_expansion_zeroelim(ztlen, detzt, bez, detzzt); + for (i = 0; i < zztlen; i++) { + detzzt[i] *= 2.0; + } + ztztlen = scale_expansion_zeroelim(ztlen, detzt, beztail, detztzt); + z1len = fast_expansion_sum_zeroelim(zzlen, detzz, zztlen, detzzt, z1); + z2len = fast_expansion_sum_zeroelim(z1len, z1, ztztlen, detztzt, z2); + xylen = fast_expansion_sum_zeroelim(x2len, x2, y2len, y2, detxy); + blen = fast_expansion_sum_zeroelim(z2len, z2, xylen, detxy, bdet); + + temp32alen = scale_expansion_zeroelim(ablen, ab, -dez, temp32a); + temp32blen = scale_expansion_zeroelim(ablen, ab, -deztail, temp32b); + temp64alen = fast_expansion_sum_zeroelim(temp32alen, temp32a, + temp32blen, temp32b, temp64a); + temp32alen = scale_expansion_zeroelim(bdlen, bd, -aez, temp32a); + temp32blen = scale_expansion_zeroelim(bdlen, bd, -aeztail, temp32b); + temp64blen = fast_expansion_sum_zeroelim(temp32alen, temp32a, + temp32blen, temp32b, temp64b); + temp32alen = scale_expansion_zeroelim(dalen, da, -bez, temp32a); + temp32blen = scale_expansion_zeroelim(dalen, da, -beztail, temp32b); + temp64clen = fast_expansion_sum_zeroelim(temp32alen, temp32a, + temp32blen, temp32b, temp64c); + temp128len = fast_expansion_sum_zeroelim(temp64alen, temp64a, + temp64blen, temp64b, temp128); + temp192len = fast_expansion_sum_zeroelim(temp64clen, temp64c, + temp128len, temp128, temp192); + xlen = scale_expansion_zeroelim(temp192len, temp192, cex, detx); + xxlen = scale_expansion_zeroelim(xlen, detx, cex, detxx); + xtlen = scale_expansion_zeroelim(temp192len, temp192, cextail, detxt); + xxtlen = scale_expansion_zeroelim(xtlen, detxt, cex, detxxt); + for (i = 0; i < xxtlen; i++) { + detxxt[i] *= 2.0; + } + xtxtlen = scale_expansion_zeroelim(xtlen, detxt, cextail, detxtxt); + x1len = fast_expansion_sum_zeroelim(xxlen, detxx, xxtlen, detxxt, x1); + x2len = fast_expansion_sum_zeroelim(x1len, x1, xtxtlen, detxtxt, x2); + ylen = scale_expansion_zeroelim(temp192len, temp192, cey, dety); + yylen = scale_expansion_zeroelim(ylen, dety, cey, detyy); + ytlen = scale_expansion_zeroelim(temp192len, temp192, ceytail, detyt); + yytlen = scale_expansion_zeroelim(ytlen, detyt, cey, detyyt); + for (i = 0; i < yytlen; i++) { + detyyt[i] *= 2.0; + } + ytytlen = scale_expansion_zeroelim(ytlen, detyt, ceytail, detytyt); + y1len = fast_expansion_sum_zeroelim(yylen, detyy, yytlen, detyyt, y1); + y2len = fast_expansion_sum_zeroelim(y1len, y1, ytytlen, detytyt, y2); + zlen = scale_expansion_zeroelim(temp192len, temp192, cez, detz); + zzlen = scale_expansion_zeroelim(zlen, detz, cez, detzz); + ztlen = scale_expansion_zeroelim(temp192len, temp192, ceztail, detzt); + zztlen = scale_expansion_zeroelim(ztlen, detzt, cez, detzzt); + for (i = 0; i < zztlen; i++) { + detzzt[i] *= 2.0; + } + ztztlen = scale_expansion_zeroelim(ztlen, detzt, ceztail, detztzt); + z1len = fast_expansion_sum_zeroelim(zzlen, detzz, zztlen, detzzt, z1); + z2len = fast_expansion_sum_zeroelim(z1len, z1, ztztlen, detztzt, z2); + xylen = fast_expansion_sum_zeroelim(x2len, x2, y2len, y2, detxy); + clen = fast_expansion_sum_zeroelim(z2len, z2, xylen, detxy, cdet); + + temp32alen = scale_expansion_zeroelim(bclen, bc, aez, temp32a); + temp32blen = scale_expansion_zeroelim(bclen, bc, aeztail, temp32b); + temp64alen = fast_expansion_sum_zeroelim(temp32alen, temp32a, + temp32blen, temp32b, temp64a); + temp32alen = scale_expansion_zeroelim(aclen, ac, -bez, temp32a); + temp32blen = scale_expansion_zeroelim(aclen, ac, -beztail, temp32b); + temp64blen = fast_expansion_sum_zeroelim(temp32alen, temp32a, + temp32blen, temp32b, temp64b); + temp32alen = scale_expansion_zeroelim(ablen, ab, cez, temp32a); + temp32blen = scale_expansion_zeroelim(ablen, ab, ceztail, temp32b); + temp64clen = fast_expansion_sum_zeroelim(temp32alen, temp32a, + temp32blen, temp32b, temp64c); + temp128len = fast_expansion_sum_zeroelim(temp64alen, temp64a, + temp64blen, temp64b, temp128); + temp192len = fast_expansion_sum_zeroelim(temp64clen, temp64c, + temp128len, temp128, temp192); + xlen = scale_expansion_zeroelim(temp192len, temp192, dex, detx); + xxlen = scale_expansion_zeroelim(xlen, detx, dex, detxx); + xtlen = scale_expansion_zeroelim(temp192len, temp192, dextail, detxt); + xxtlen = scale_expansion_zeroelim(xtlen, detxt, dex, detxxt); + for (i = 0; i < xxtlen; i++) { + detxxt[i] *= 2.0; + } + xtxtlen = scale_expansion_zeroelim(xtlen, detxt, dextail, detxtxt); + x1len = fast_expansion_sum_zeroelim(xxlen, detxx, xxtlen, detxxt, x1); + x2len = fast_expansion_sum_zeroelim(x1len, x1, xtxtlen, detxtxt, x2); + ylen = scale_expansion_zeroelim(temp192len, temp192, dey, dety); + yylen = scale_expansion_zeroelim(ylen, dety, dey, detyy); + ytlen = scale_expansion_zeroelim(temp192len, temp192, deytail, detyt); + yytlen = scale_expansion_zeroelim(ytlen, detyt, dey, detyyt); + for (i = 0; i < yytlen; i++) { + detyyt[i] *= 2.0; + } + ytytlen = scale_expansion_zeroelim(ytlen, detyt, deytail, detytyt); + y1len = fast_expansion_sum_zeroelim(yylen, detyy, yytlen, detyyt, y1); + y2len = fast_expansion_sum_zeroelim(y1len, y1, ytytlen, detytyt, y2); + zlen = scale_expansion_zeroelim(temp192len, temp192, dez, detz); + zzlen = scale_expansion_zeroelim(zlen, detz, dez, detzz); + ztlen = scale_expansion_zeroelim(temp192len, temp192, deztail, detzt); + zztlen = scale_expansion_zeroelim(ztlen, detzt, dez, detzzt); + for (i = 0; i < zztlen; i++) { + detzzt[i] *= 2.0; + } + ztztlen = scale_expansion_zeroelim(ztlen, detzt, deztail, detztzt); + z1len = fast_expansion_sum_zeroelim(zzlen, detzz, zztlen, detzzt, z1); + z2len = fast_expansion_sum_zeroelim(z1len, z1, ztztlen, detztzt, z2); + xylen = fast_expansion_sum_zeroelim(x2len, x2, y2len, y2, detxy); + dlen = fast_expansion_sum_zeroelim(z2len, z2, xylen, detxy, ddet); + + ablen = fast_expansion_sum_zeroelim(alen, adet, blen, bdet, abdet); + cdlen = fast_expansion_sum_zeroelim(clen, cdet, dlen, ddet, cddet); + deterlen = fast_expansion_sum_zeroelim(ablen, abdet, cdlen, cddet, deter); + + return deter[deterlen - 1]; +} + +REAL insphereadapt(REAL *pa, REAL *pb, REAL *pc, REAL *pd, REAL *pe, + REAL permanent) +{ + INEXACT REAL aex, bex, cex, dex, aey, bey, cey, dey, aez, bez, cez, dez; + REAL det, errbound; + + INEXACT REAL aexbey1, bexaey1, bexcey1, cexbey1; + INEXACT REAL cexdey1, dexcey1, dexaey1, aexdey1; + INEXACT REAL aexcey1, cexaey1, bexdey1, dexbey1; + REAL aexbey0, bexaey0, bexcey0, cexbey0; + REAL cexdey0, dexcey0, dexaey0, aexdey0; + REAL aexcey0, cexaey0, bexdey0, dexbey0; + REAL ab[4], bc[4], cd[4], da[4], ac[4], bd[4]; + INEXACT REAL ab3, bc3, cd3, da3, ac3, bd3; + REAL abeps, bceps, cdeps, daeps, aceps, bdeps; + REAL temp8a[8], temp8b[8], temp8c[8], temp16[16], temp24[24], temp48[48]; + int temp8alen, temp8blen, temp8clen, temp16len, temp24len, temp48len; + REAL xdet[96], ydet[96], zdet[96], xydet[192]; + int xlen, ylen, zlen, xylen; + REAL adet[288], bdet[288], cdet[288], ddet[288]; + int alen, blen, clen, dlen; + REAL abdet[576], cddet[576]; + int ablen, cdlen; + REAL fin1[1152]; + int finlength; + + REAL aextail, bextail, cextail, dextail; + REAL aeytail, beytail, ceytail, deytail; + REAL aeztail, beztail, ceztail, deztail; + + INEXACT REAL bvirt; + REAL avirt, bround, around; + INEXACT REAL c; + INEXACT REAL abig; + REAL ahi, alo, bhi, blo; + REAL err1, err2, err3; + INEXACT REAL _i, _j; + REAL _0; + + aex = (REAL) (pa[0] - pe[0]); + bex = (REAL) (pb[0] - pe[0]); + cex = (REAL) (pc[0] - pe[0]); + dex = (REAL) (pd[0] - pe[0]); + aey = (REAL) (pa[1] - pe[1]); + bey = (REAL) (pb[1] - pe[1]); + cey = (REAL) (pc[1] - pe[1]); + dey = (REAL) (pd[1] - pe[1]); + aez = (REAL) (pa[2] - pe[2]); + bez = (REAL) (pb[2] - pe[2]); + cez = (REAL) (pc[2] - pe[2]); + dez = (REAL) (pd[2] - pe[2]); + + Two_Product(aex, bey, aexbey1, aexbey0); + Two_Product(bex, aey, bexaey1, bexaey0); + Two_Two_Diff(aexbey1, aexbey0, bexaey1, bexaey0, ab3, ab[2], ab[1], ab[0]); + ab[3] = ab3; + + Two_Product(bex, cey, bexcey1, bexcey0); + Two_Product(cex, bey, cexbey1, cexbey0); + Two_Two_Diff(bexcey1, bexcey0, cexbey1, cexbey0, bc3, bc[2], bc[1], bc[0]); + bc[3] = bc3; + + Two_Product(cex, dey, cexdey1, cexdey0); + Two_Product(dex, cey, dexcey1, dexcey0); + Two_Two_Diff(cexdey1, cexdey0, dexcey1, dexcey0, cd3, cd[2], cd[1], cd[0]); + cd[3] = cd3; + + Two_Product(dex, aey, dexaey1, dexaey0); + Two_Product(aex, dey, aexdey1, aexdey0); + Two_Two_Diff(dexaey1, dexaey0, aexdey1, aexdey0, da3, da[2], da[1], da[0]); + da[3] = da3; + + Two_Product(aex, cey, aexcey1, aexcey0); + Two_Product(cex, aey, cexaey1, cexaey0); + Two_Two_Diff(aexcey1, aexcey0, cexaey1, cexaey0, ac3, ac[2], ac[1], ac[0]); + ac[3] = ac3; + + Two_Product(bex, dey, bexdey1, bexdey0); + Two_Product(dex, bey, dexbey1, dexbey0); + Two_Two_Diff(bexdey1, bexdey0, dexbey1, dexbey0, bd3, bd[2], bd[1], bd[0]); + bd[3] = bd3; + + temp8alen = scale_expansion_zeroelim(4, cd, bez, temp8a); + temp8blen = scale_expansion_zeroelim(4, bd, -cez, temp8b); + temp8clen = scale_expansion_zeroelim(4, bc, dez, temp8c); + temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, + temp8blen, temp8b, temp16); + temp24len = fast_expansion_sum_zeroelim(temp8clen, temp8c, + temp16len, temp16, temp24); + temp48len = scale_expansion_zeroelim(temp24len, temp24, aex, temp48); + xlen = scale_expansion_zeroelim(temp48len, temp48, -aex, xdet); + temp48len = scale_expansion_zeroelim(temp24len, temp24, aey, temp48); + ylen = scale_expansion_zeroelim(temp48len, temp48, -aey, ydet); + temp48len = scale_expansion_zeroelim(temp24len, temp24, aez, temp48); + zlen = scale_expansion_zeroelim(temp48len, temp48, -aez, zdet); + xylen = fast_expansion_sum_zeroelim(xlen, xdet, ylen, ydet, xydet); + alen = fast_expansion_sum_zeroelim(xylen, xydet, zlen, zdet, adet); + + temp8alen = scale_expansion_zeroelim(4, da, cez, temp8a); + temp8blen = scale_expansion_zeroelim(4, ac, dez, temp8b); + temp8clen = scale_expansion_zeroelim(4, cd, aez, temp8c); + temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, + temp8blen, temp8b, temp16); + temp24len = fast_expansion_sum_zeroelim(temp8clen, temp8c, + temp16len, temp16, temp24); + temp48len = scale_expansion_zeroelim(temp24len, temp24, bex, temp48); + xlen = scale_expansion_zeroelim(temp48len, temp48, bex, xdet); + temp48len = scale_expansion_zeroelim(temp24len, temp24, bey, temp48); + ylen = scale_expansion_zeroelim(temp48len, temp48, bey, ydet); + temp48len = scale_expansion_zeroelim(temp24len, temp24, bez, temp48); + zlen = scale_expansion_zeroelim(temp48len, temp48, bez, zdet); + xylen = fast_expansion_sum_zeroelim(xlen, xdet, ylen, ydet, xydet); + blen = fast_expansion_sum_zeroelim(xylen, xydet, zlen, zdet, bdet); + + temp8alen = scale_expansion_zeroelim(4, ab, dez, temp8a); + temp8blen = scale_expansion_zeroelim(4, bd, aez, temp8b); + temp8clen = scale_expansion_zeroelim(4, da, bez, temp8c); + temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, + temp8blen, temp8b, temp16); + temp24len = fast_expansion_sum_zeroelim(temp8clen, temp8c, + temp16len, temp16, temp24); + temp48len = scale_expansion_zeroelim(temp24len, temp24, cex, temp48); + xlen = scale_expansion_zeroelim(temp48len, temp48, -cex, xdet); + temp48len = scale_expansion_zeroelim(temp24len, temp24, cey, temp48); + ylen = scale_expansion_zeroelim(temp48len, temp48, -cey, ydet); + temp48len = scale_expansion_zeroelim(temp24len, temp24, cez, temp48); + zlen = scale_expansion_zeroelim(temp48len, temp48, -cez, zdet); + xylen = fast_expansion_sum_zeroelim(xlen, xdet, ylen, ydet, xydet); + clen = fast_expansion_sum_zeroelim(xylen, xydet, zlen, zdet, cdet); + + temp8alen = scale_expansion_zeroelim(4, bc, aez, temp8a); + temp8blen = scale_expansion_zeroelim(4, ac, -bez, temp8b); + temp8clen = scale_expansion_zeroelim(4, ab, cez, temp8c); + temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, + temp8blen, temp8b, temp16); + temp24len = fast_expansion_sum_zeroelim(temp8clen, temp8c, + temp16len, temp16, temp24); + temp48len = scale_expansion_zeroelim(temp24len, temp24, dex, temp48); + xlen = scale_expansion_zeroelim(temp48len, temp48, dex, xdet); + temp48len = scale_expansion_zeroelim(temp24len, temp24, dey, temp48); + ylen = scale_expansion_zeroelim(temp48len, temp48, dey, ydet); + temp48len = scale_expansion_zeroelim(temp24len, temp24, dez, temp48); + zlen = scale_expansion_zeroelim(temp48len, temp48, dez, zdet); + xylen = fast_expansion_sum_zeroelim(xlen, xdet, ylen, ydet, xydet); + dlen = fast_expansion_sum_zeroelim(xylen, xydet, zlen, zdet, ddet); + + ablen = fast_expansion_sum_zeroelim(alen, adet, blen, bdet, abdet); + cdlen = fast_expansion_sum_zeroelim(clen, cdet, dlen, ddet, cddet); + finlength = fast_expansion_sum_zeroelim(ablen, abdet, cdlen, cddet, fin1); + + det = estimate(finlength, fin1); + errbound = isperrboundB * permanent; + if ((det >= errbound) || (-det >= errbound)) { + return det; + } + + Two_Diff_Tail(pa[0], pe[0], aex, aextail); + Two_Diff_Tail(pa[1], pe[1], aey, aeytail); + Two_Diff_Tail(pa[2], pe[2], aez, aeztail); + Two_Diff_Tail(pb[0], pe[0], bex, bextail); + Two_Diff_Tail(pb[1], pe[1], bey, beytail); + Two_Diff_Tail(pb[2], pe[2], bez, beztail); + Two_Diff_Tail(pc[0], pe[0], cex, cextail); + Two_Diff_Tail(pc[1], pe[1], cey, ceytail); + Two_Diff_Tail(pc[2], pe[2], cez, ceztail); + Two_Diff_Tail(pd[0], pe[0], dex, dextail); + Two_Diff_Tail(pd[1], pe[1], dey, deytail); + Two_Diff_Tail(pd[2], pe[2], dez, deztail); + if ((aextail == 0.0) && (aeytail == 0.0) && (aeztail == 0.0) + && (bextail == 0.0) && (beytail == 0.0) && (beztail == 0.0) + && (cextail == 0.0) && (ceytail == 0.0) && (ceztail == 0.0) + && (dextail == 0.0) && (deytail == 0.0) && (deztail == 0.0)) { + return det; + } + + errbound = isperrboundC * permanent + resulterrbound * Absolute(det); + abeps = (aex * beytail + bey * aextail) + - (aey * bextail + bex * aeytail); + bceps = (bex * ceytail + cey * bextail) + - (bey * cextail + cex * beytail); + cdeps = (cex * deytail + dey * cextail) + - (cey * dextail + dex * ceytail); + daeps = (dex * aeytail + aey * dextail) + - (dey * aextail + aex * deytail); + aceps = (aex * ceytail + cey * aextail) + - (aey * cextail + cex * aeytail); + bdeps = (bex * deytail + dey * bextail) + - (bey * dextail + dex * beytail); + det += (((bex * bex + bey * bey + bez * bez) + * ((cez * daeps + dez * aceps + aez * cdeps) + + (ceztail * da3 + deztail * ac3 + aeztail * cd3)) + + (dex * dex + dey * dey + dez * dez) + * ((aez * bceps - bez * aceps + cez * abeps) + + (aeztail * bc3 - beztail * ac3 + ceztail * ab3))) + - ((aex * aex + aey * aey + aez * aez) + * ((bez * cdeps - cez * bdeps + dez * bceps) + + (beztail * cd3 - ceztail * bd3 + deztail * bc3)) + + (cex * cex + cey * cey + cez * cez) + * ((dez * abeps + aez * bdeps + bez * daeps) + + (deztail * ab3 + aeztail * bd3 + beztail * da3)))) + + 2.0 * (((bex * bextail + bey * beytail + bez * beztail) + * (cez * da3 + dez * ac3 + aez * cd3) + + (dex * dextail + dey * deytail + dez * deztail) + * (aez * bc3 - bez * ac3 + cez * ab3)) + - ((aex * aextail + aey * aeytail + aez * aeztail) + * (bez * cd3 - cez * bd3 + dez * bc3) + + (cex * cextail + cey * ceytail + cez * ceztail) + * (dez * ab3 + aez * bd3 + bez * da3))); + if ((det >= errbound) || (-det >= errbound)) { + return det; + } + + return insphereexact(pa, pb, pc, pd, pe); +} + +REAL insphere(REAL *pa, REAL *pb, REAL *pc, REAL *pd, REAL *pe) +{ + REAL aex, bex, cex, dex; + REAL aey, bey, cey, dey; + REAL aez, bez, cez, dez; + REAL aexbey, bexaey, bexcey, cexbey, cexdey, dexcey, dexaey, aexdey; + REAL aexcey, cexaey, bexdey, dexbey; + REAL alift, blift, clift, dlift; + REAL ab, bc, cd, da, ac, bd; + REAL abc, bcd, cda, dab; + REAL aezplus, bezplus, cezplus, dezplus; + REAL aexbeyplus, bexaeyplus, bexceyplus, cexbeyplus; + REAL cexdeyplus, dexceyplus, dexaeyplus, aexdeyplus; + REAL aexceyplus, cexaeyplus, bexdeyplus, dexbeyplus; + REAL det; + REAL permanent, errbound; + + aex = pa[0] - pe[0]; + bex = pb[0] - pe[0]; + cex = pc[0] - pe[0]; + dex = pd[0] - pe[0]; + aey = pa[1] - pe[1]; + bey = pb[1] - pe[1]; + cey = pc[1] - pe[1]; + dey = pd[1] - pe[1]; + aez = pa[2] - pe[2]; + bez = pb[2] - pe[2]; + cez = pc[2] - pe[2]; + dez = pd[2] - pe[2]; + + aexbey = aex * bey; + bexaey = bex * aey; + ab = aexbey - bexaey; + bexcey = bex * cey; + cexbey = cex * bey; + bc = bexcey - cexbey; + cexdey = cex * dey; + dexcey = dex * cey; + cd = cexdey - dexcey; + dexaey = dex * aey; + aexdey = aex * dey; + da = dexaey - aexdey; + + aexcey = aex * cey; + cexaey = cex * aey; + ac = aexcey - cexaey; + bexdey = bex * dey; + dexbey = dex * bey; + bd = bexdey - dexbey; + + abc = aez * bc - bez * ac + cez * ab; + bcd = bez * cd - cez * bd + dez * bc; + cda = cez * da + dez * ac + aez * cd; + dab = dez * ab + aez * bd + bez * da; + + alift = aex * aex + aey * aey + aez * aez; + blift = bex * bex + bey * bey + bez * bez; + clift = cex * cex + cey * cey + cez * cez; + dlift = dex * dex + dey * dey + dez * dez; + + det = (dlift * abc - clift * dab) + (blift * cda - alift * bcd); + + aezplus = Absolute(aez); + bezplus = Absolute(bez); + cezplus = Absolute(cez); + dezplus = Absolute(dez); + aexbeyplus = Absolute(aexbey); + bexaeyplus = Absolute(bexaey); + bexceyplus = Absolute(bexcey); + cexbeyplus = Absolute(cexbey); + cexdeyplus = Absolute(cexdey); + dexceyplus = Absolute(dexcey); + dexaeyplus = Absolute(dexaey); + aexdeyplus = Absolute(aexdey); + aexceyplus = Absolute(aexcey); + cexaeyplus = Absolute(cexaey); + bexdeyplus = Absolute(bexdey); + dexbeyplus = Absolute(dexbey); + permanent = ((cexdeyplus + dexceyplus) * bezplus + + (dexbeyplus + bexdeyplus) * cezplus + + (bexceyplus + cexbeyplus) * dezplus) + * alift + + ((dexaeyplus + aexdeyplus) * cezplus + + (aexceyplus + cexaeyplus) * dezplus + + (cexdeyplus + dexceyplus) * aezplus) + * blift + + ((aexbeyplus + bexaeyplus) * dezplus + + (bexdeyplus + dexbeyplus) * aezplus + + (dexaeyplus + aexdeyplus) * bezplus) + * clift + + ((bexceyplus + cexbeyplus) * aezplus + + (cexaeyplus + aexceyplus) * bezplus + + (aexbeyplus + bexaeyplus) * cezplus) + * dlift; + errbound = isperrboundA * permanent; + if ((det > errbound) || (-det > errbound)) { + return det; + } + + return insphereadapt(pa, pb, pc, pd, pe, permanent); +} diff --git a/contrib/TetgenNew/refine.cxx b/contrib/TetgenNew/refine.cxx new file mode 100644 index 0000000000..6a13e9771d --- /dev/null +++ b/contrib/TetgenNew/refine.cxx @@ -0,0 +1,249 @@ +#ifndef refineCXX +#define refineCXX + +#include "tetgen.h" + +/////////////////////////////////////////////////////////////////////////////// +// // +// checkedge4encroach() Check a subsegment to see if it is encroached. // +// // +// If 'testpt' != NULL, only test if the segment is encroached by this point.// +// Otherwise, test all apexes of faces containing this segment. // +// // +// If 'enqflag' > 0, add the segment into list (badsegpool) if it is encroa- // +// ched. // +// // +/////////////////////////////////////////////////////////////////////////////// + +bool tetgenmesh::checkedge4encroach(face& seg, point testpt, int enqflag) +{ + badface *encseg; + triface neightet, spintet; + point pa, pb, pc, encpt; + REAL midpt[3], r, d, diff; + int encroached; + int i; + + seg.shver = 0; + pa = sorg(seg); + pb = sdest(seg); + + for (i = 0; i < 3; i++) { + midpt[i] = 0.5 * (pa[i] + pb[i]); + } + r = DIST(midpt, pa); + + encroached = 0; + encpt = NULL; + + if (testpt == NULL) { + // Check all apexes of faces containing [pa, pb]. + stpivot(seg, neightet); // sstpivot(seg, neightet); + spintet = neightet; + while (1) { + pc = apex(spintet); + if (pc != dummypoint) { + d = DIST(midpt, pc); + diff = fabs(r - d); + if ((diff / r) < b->epsilon) { + // testpt is on the diametric ball of [pa, pb]. + encroached = 0; + } else { + encroached = (d < r ? 1 : 0); + } + if (encroached) { + encpt = pc; // pc is encroached [pa. pb]. + break; + } + } + fnextself(spintet); + if (spintet.tet == neightet.tet) break; + } + } else { + d = DIST(midpt, testpt); + diff = fabs(r - d); + if ((diff / r) < b->epsilon) { + // testpt is on the diametric ball of [pa, pb]. + encroached = 0; + } else { + encroached = (d < r ? 1 : 0); + if (encroached) { + encpt = testpt; + } + } + } + + if (encroached && enqflag) { + if (b->verbose > 1) { + printf(" Queuing encroaching segment (%d, %d) ref (%d).\n", + pointmark(pa), pointmark(pb), pointmark(encpt)); + } + encseg = (badface *) badsegpool->alloc(); + encseg->ss = seg; + encseg->forg = pa; + encseg->fdest = pb; + encseg->foppo = encpt; // The encroaching point. + smarktest(seg); // Flag it to avoid multiple testing. + } + + return encroached > 0; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// repairencsegs() Repair all queued encroached subsegments. // +// // +// Encroached segments are repaired by inserting a vertex inside them. Each // +// newly inserted vertex may encroach other segments, or makeing them non- // +// Delaunay, these segments are also repaired. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::repairencsegs() +{ + badface *encloop; + triface searchtet; + face splitsh; + point newpt, refpt; + point pa, pb; + + while ((badsegpool->items > 0) && (b->steinerleft != 0)) { + + badsegpool->traversalinit(); + encloop = badfacetraverse(badsegpool); + while ((encloop != NULL) && (b->steinerleft != 0)) { + // assert(smarktested(encloop->ss)); + sunmarktest(encloop->ss); + + // Check if it is still the same segment when it is tested. + pa = sorg(encloop->ss); + pb = sdest(encloop->ss); + if ((encloop->forg == pa) && (encloop->fdest == pb)) { + + refpt = encloop->foppo; + if ((refpt == NULL) || getpointtype(refpt) == DEADVERTEX) { + // Check if this segment can be split. + assert(0); // Not handled yet. + } + // Create a new point in the segment. + makepoint(&newpt); + // getsegmentsplitpoint2(&(encloop->ss), refpt, newpt); + getsegmentsplitpoint3(&(encloop->ss), refpt, newpt); + setpointtype(newpt, STEINERVERTEX); + + // Decrease the number of allocated Steiner points (-S option). + if (b->steinerleft != -1) { + b->steinerleft--; + } + + // Get an adjacent tet for point location. + stpivot(encloop->ss, searchtet); + + // Split the segment by newpt. Two new subsegments and new subfaces + // are queued in subsegstack and subfacstack for recovery. + spivot(encloop->ss, splitsh); + sinsertvertex(newpt, &splitsh, &(encloop->ss), true, false); + + // Insert newpt into the DT. Since the mesh may be a CDT, always + // set visflag be true. Some existing egments and subfaces may be + // non-Delaunay due to this new vertex, they will be removed from + // the mesh, and are queued in subsegstack and subfacstack for + // recovery. + // Newly encroached segments, subfaces, and badly-shaped tets are + // queued in badsegpool, badsubpool, and badtetpool, resp. + insertvertex(newpt, &searchtet, true, true, false, false); + + // Recover missing subsegments. + assert(subsegstack->objects > 0); + delaunizesegments(); + // Recover missing subfaces. + assert(subfacstack->objects > 0); + constrainedfacets(); + } + + // Deallocate the badface. + badfacedealloc(badsegpool, encloop); + // Get the next badface. + encloop = badfacetraverse(badsegpool); + } // while (encloop != NULL) + + } // while (badsegpool->items > 0) +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// enforcequality() Create quality conforming Delaunay mesh. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::enforcequality() +{ + face shloop; + REAL bakeps; + long bakptcount; + + if (!b->quiet) { + printf("Conforming Delaunay meshing.\n"); + } + + bakeps = b->epsilon; // Bakup the epsilon. + b->epsilon = 0; + + // Initialize the pool for storing encroched segments. + badsegpool = new memorypool(sizeof(badface), SUBPERBLOCK, POINTER, 0); + + // Initialize arrays. + tg_crosstets = new arraypool(sizeof(triface), 10); + tg_topnewtets = new arraypool(sizeof(triface), 10); + tg_botnewtets = new arraypool(sizeof(triface), 10); + tg_topfaces = new arraypool(sizeof(triface), 10); + tg_botfaces = new arraypool(sizeof(triface), 10); + tg_midfaces = new arraypool(sizeof(triface), 10); + tg_toppoints = new arraypool(sizeof(point), 8); + tg_botpoints = new arraypool(sizeof(point), 8); + tg_facpoints = new arraypool(sizeof(point), 8); + tg_facfaces = new arraypool(sizeof(face), 10); + tg_topshells = new arraypool(sizeof(face), 10); + tg_botshells = new arraypool(sizeof(face), 10); + + // Find all encroached segments. + subsegpool->traversalinit(); + shloop.sh = shellfacetraverse(subsegpool); + while (shloop.sh != NULL) { + checkedge4encroach(shloop, NULL, 1); + shloop.sh = shellfacetraverse(subsegpool); + } + + if (b->verbose && (badsegpool->items > 0)) { + printf(" Splitting encroached segments.\n"); + } + bakptcount = pointpool->items; + + // Fix encroached segments. + repairencsegs(); + // At this point, no segments should be encroached. + + if (b->verbose && ((pointpool->items - bakptcount) > 0)) { + printf(" %ld Steiner points.\n", pointpool->items - bakptcount); + } + + // Delete arrays. + delete tg_crosstets; + delete tg_topnewtets; + delete tg_botnewtets; + delete tg_topfaces; + delete tg_botfaces; + delete tg_midfaces; + delete tg_toppoints; + delete tg_botpoints; + delete tg_facpoints; + delete tg_facfaces; + delete tg_topshells; + delete tg_botshells; + + delete badsegpool; + + b->epsilon = bakeps; // Restore the epsilon. +} + +#endif // #ifndef refineCXX diff --git a/contrib/TetgenNew/surface.cxx b/contrib/TetgenNew/surface.cxx new file mode 100644 index 0000000000..e9a72b232d --- /dev/null +++ b/contrib/TetgenNew/surface.cxx @@ -0,0 +1,1795 @@ +#ifndef surfaceCXX +#define surfaceCXX + +#include "tetgen.h" + +/////////////////////////////////////////////////////////////////////////////// +// // +// calculateabovepoint() Calculate a point above a facet in 'dummypoint'. // +// // +/////////////////////////////////////////////////////////////////////////////// + +bool tetgenmesh::calculateabovepoint(arraypool *facpoints, point *ppa, + point *ppb, point *ppc) +{ + point *ppt, pa, pb, pc; + REAL v1[3], v2[3], n[3]; + REAL lab, len, A, area; + REAL x, y, z; + int i; + + ppt = (point *) fastlookup(facpoints, 0); + pa = *ppt; // a is the first point. + + // Get a point b s.t. the length of [a, b] is maximal. + lab = 0; + for (i = 1; i < facpoints->objects; i++) { + ppt = (point *) fastlookup(facpoints, i); + x = (*ppt)[0] - pa[0]; + y = (*ppt)[1] - pa[1]; + z = (*ppt)[2] - pa[2]; + len = x * x + y * y + z * z; + if (len > lab) { + lab = len; + pb = *ppt; + } + } + lab = sqrt(lab); + if (lab == 0) { + if (!b->quiet) { + printf("Warning: All points of a facet are coincident with %d.\n", + pointmark(pa)); + } + return false; + } + + // Get a point c s.t. the area of [a, b, c] is maximal. + v1[0] = pb[0] - pa[0]; + v1[1] = pb[1] - pa[1]; + v1[2] = pb[2] - pa[2]; + A = 0; + for (i = 1; i < facpoints->objects; i++) { + ppt = (point *) fastlookup(facpoints, i); + v2[0] = (*ppt)[0] - pa[0]; + v2[1] = (*ppt)[1] - pa[1]; + v2[2] = (*ppt)[2] - pa[2]; + CROSS(v1, v2, n); + area = DOT(n, n); + if (area > A) { + A = area; + pc = *ppt; + } + } + if (A == 0) { + // All points are collinear. No above point. + if (!b->quiet) { + printf("Warning: All points of a facet are collinaer with [%d, %d].\n", + pointmark(pa), pointmark(pb)); + } + return false; + } + + // Calculate an above point of this facet. + facenormal(pa, pb, pc, n, 1); + len = sqrt(DOT(n, n)); + n[0] /= len; + n[1] /= len; + n[2] /= len; + lab /= 2.0; + dummypoint[0] = 0.5 * (pa[0] + pb[0]) + lab * n[0]; + dummypoint[1] = 0.5 * (pa[1] + pb[1]) + lab * n[1]; + dummypoint[2] = 0.5 * (pa[2] + pb[2]) + lab * n[2]; + + if (ppa != NULL) { + // Return the three points. + *ppa = pa; + *ppb = pb; + *ppc = pc; + } + + return true; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// slocate() Locate a point in a surface triangulation. // +// // +// Search the point (p) from the input 'searchsh' (it should not be NULL). // +// // +// It is assumed that 'dummypoint' lies above the facet of the triangulation,// +// hence a CCW test (2D orientation) is equal to a below-plane (3D) test. // +// // +// The returned value inducates the following cases: // +// - ONVERTEX, p is the origin of 'searchsh'. // +// - ONEDGE, p lies on the edge of 'searchsh'. // +// - ONFACE, p lies in the interior of 'searchsh'. // +// - OUTSIDE, p lies outside of the triangulation, p is on the left-hand // +// side of the edge 'searchsh'(s), i.e., org(s), dest(s), p are CW. // +// // +// If 'cflag' is not TRUE, the triangulation may not be convex. Stop search // +// when a segment is met and return OUTSIDE. // +// // +/////////////////////////////////////////////////////////////////////////////// + +enum tetgenmesh::location tetgenmesh::slocate(point searchpt, face* searchsh, + bool cflag) +{ + face neighsh; + face checkseg; + point pa, pb, pc, pd; + REAL ori, ori_bc, ori_ca; + REAL dist_bc, dist_ca; + int i; + + enum {MOVE_BC, MOVE_CA} nextmove; + + shellface sptr; + + // Adjust the face orientation s.t. 'dummypt' lies above to it. + pa = sorg(*searchsh); + pb = sdest(*searchsh); + pc = sapex(*searchsh); + ori = orient3d(pa, pb, pc, dummypoint); + assert(ori != 0); // SELF_CHECK + if (ori > 0) { + sesymself(*searchsh); // Reverse the face orientation. + } + + // Find an edge of the face s.t. p lies on its right-hand side (CCW). + for (i = 0; i < 3; i++) { + pa = sorg(*searchsh); + pb = sdest(*searchsh); + ori = orient3d(pa, pb, dummypoint, searchpt); + if (ori > 0) break; + senextself(*searchsh); + } + assert(i < 3); // SELF_CHECK + + while (1) { + + pc = sapex(*searchsh); + + if (pc == searchpt) { + senext2self(*searchsh); + return ONVERTEX; + } + + ori_bc = orient3d(pb, pc, dummypoint, searchpt); + ori_ca = orient3d(pc, pa, dummypoint, searchpt); + + if (ori_bc < 0) { + if (ori_ca < 0) { // (--) + // Any of the edges is a viable move. + senext(*searchsh, neighsh); // At edge [b, c]. + spivotself(neighsh); + if (neighsh.sh != NULL) { + pd = sapex(neighsh); + dist_bc = NORM2(searchpt[0] - pd[0], searchpt[1] - pd[1], + searchpt[2] - pd[2]); + } else { + dist_bc = NORM2(xmax - xmin, ymax - ymin, zmax - zmin); + } + senext2(*searchsh, neighsh); // At edge [c, a]. + spivotself(neighsh); + if (neighsh.sh != NULL) { + pd = sapex(neighsh); + dist_ca = NORM2(searchpt[0] - pd[0], searchpt[1] - pd[1], + searchpt[2] - pd[2]); + } else { + dist_ca = dist_bc; + } + if (dist_ca < dist_bc) { + nextmove = MOVE_CA; + } else { + nextmove = MOVE_BC; + } + } else { // (-#) + // Edge [b, c] is viable. + nextmove = MOVE_BC; + } + } else { + if (ori_ca < 0) { // (#-) + // Edge [c, a] is viable. + nextmove = MOVE_CA; + } else { + if (ori_bc > 0) { + if (ori_ca > 0) { // (++) + return ONFACE; // Inside [a, b, c]. + } else { // (+0) + senext2self(*searchsh); // On edge [c, a]. + return ONEDGE; + } + } else { // ori_bc == 0 + if (ori_ca > 0) { // (0+) + senextself(*searchsh); // On edge [b, c]. + return ONEDGE; + } else { // (00) + // On vertex c. Should be checked in above. + assert(0); // SELF_CHECK + } + } + } + } + + // Move to the next face. + if (nextmove == MOVE_BC) { + senextself(*searchsh); + } else { + senext2self(*searchsh); + } + if (!cflag) { + // NON-convex case. Chekc if we will cross a boundary. + sspivot(*searchsh, checkseg); + if (checkseg.sh != NULL) { + return OUTSIDE; // Do not cross a boundary edge. + } + } + spivot(*searchsh, neighsh); + if (neighsh.sh == NULL) { + return OUTSIDE; // A hull edge. + } + // Adjust the edge orientation. + if (sorg(neighsh) != sdest(*searchsh)) { + sesymself(neighsh); + } + assert(sorg(neighsh) == sdest(*searchsh)); // SELF_CHECK + + // Update the newly discovered face and its endpoints. + *searchsh = neighsh; + pa = sorg(*searchsh); + pb = sdest(*searchsh); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// sinsertvertex() Insert a vertex into a triangulation of a facet. // +// // +// The new point (p) will be located. Searching from 'splitsh'. If 'splitseg'// +// is not NULL, p is on a segment, no search is needed. // +// // +// If 'cflag' is not TRUE, the triangulation may be not convex. Don't insert // +// p if it is found in outside. // +// // +/////////////////////////////////////////////////////////////////////////////// + +enum tetgenmesh::location tetgenmesh::sinsertvertex(point insertpt, + face *splitsh, face *splitseg, bool bwflag, bool cflag) +{ + face *abfaces, *parysh, *pssub; + face neighsh, newsh, casout, casin; + face aseg, bseg, aoutseg, boutseg; + face checkseg; + triface neightet, spintet; + point pa, pb, pc; + enum location loc; + REAL sign, ori, area; + int n, s, i, j; + + tetrahedron ptr; + shellface sptr; + + if (splitseg != NULL) { + spivot(*splitseg, *splitsh); + loc = ONEDGE; + } else { + assert(splitsh->sh != NULL); // SELF_CHECK + loc = slocate(insertpt, splitsh, false); + } + + // Return if p lies on a vertex. + if (loc == ONVERTEX) return loc; + + if (loc == OUTSIDE) { + // Return if 'cflag' is not set. + if (!cflag) return loc; + } + + if (loc == ONEDGE) { + if (splitseg == NULL) { + // Do not split a segment. + sspivot(*splitsh, checkseg); + if (checkseg.sh != NULL) return loc; // return ONSUBSEG; + // Check if this edge is on the hull. + spivot(*splitsh, neighsh); + if (neighsh.sh == NULL) { + // A convex hull edge. The new point is on the hull. + loc = OUTSIDE; + } + } + } + + if (b->verbose > 1) { + pa = sorg(*splitsh); + pb = sdest(*splitsh); + pc = sapex(*splitsh); + printf(" Insert point %d (%d, %d, %d) loc %d\n", pointmark(insertpt), + pointmark(pa), pointmark(pb), pointmark(pc), (int) loc); + } + + // Does 'insertpt' lie on a segment? + if (splitseg != NULL) { + splitseg->shver = 0; + pa = sorg(*splitseg); + // Count the number of faces at segment [a, b]. + n = 0; + neighsh = *splitsh; + do { + spivotself(neighsh); + n++; + } while ((neighsh.sh != NULL) && (neighsh.sh != splitsh->sh)); + // n is at least 1. + abfaces = new face[n]; + // Collect faces at seg [a, b]. + abfaces[0] = *splitsh; + if (sorg(abfaces[0]) != pa) sesymself(abfaces[0]); + for (i = 1; i < n; i++) { + spivot(abfaces[i - 1], abfaces[i]); + if (sorg(abfaces[i]) != pa) sesymself(abfaces[i]); + } + } + + // Initialize the cavity. + if (loc == ONEDGE) { + smarktest(*splitsh); + caveshlist->newindex((void **) &parysh); + *parysh = *splitsh; + if (splitseg != NULL) { + for (i = 1; i < n; i++) { + smarktest(abfaces[i]); + caveshlist->newindex((void **) &parysh); + *parysh = abfaces[i]; + } + } else { + spivot(*splitsh, neighsh); + if (neighsh.sh != NULL) { + smarktest(neighsh); + caveshlist->newindex((void **) &parysh); + *parysh = neighsh; + } + } + } else if (loc == ONFACE) { + smarktest(*splitsh); + caveshlist->newindex((void **) &parysh); + *parysh = *splitsh; + } else { // loc == OUTSIDE; + // This is only possible when T is convex. + assert(cflag); // SELF_CHECK + // Assume p is on top of the edge ('splitsh'). Find a right-most edge + // which is visible by p. + neighsh = *splitsh; + while (1) { + senext2self(neighsh); + spivot(neighsh, casout); + if (casout.sh == NULL) { + // A convex hull edge. Is it visible by p. + pa = sorg(neighsh); + pb = sdest(neighsh); + ori = orient3d(pa, pb, dummypoint, insertpt); + if (ori < 0) { + *splitsh = neighsh; // Update 'splitsh'. + } else { + break; // 'splitsh' is the right-most visible edge. + } + } else { + if (sorg(casout) != sdest(neighsh)) sesymself(casout); + neighsh = casout; + } + } + // Create new triangles for all visible edges of p (from right to left). + casin.sh = NULL; // No adjacent face at right. + pa = sorg(*splitsh); + pb = sdest(*splitsh); + while (1) { + // Create a new subface on top of the (visible) edge. + makeshellface(subfacepool, &newsh); + setshvertices(newsh, pb, pa, insertpt); + setshellmark(newsh, getshellmark(*splitsh)); + if (checkconstraints) { + area = areabound(*splitsh); + areabound(newsh) = area; + } + // Connect the new subface to the bottom subfaces. + sbond1(newsh, *splitsh); + sbond1(*splitsh, newsh); + // Connect the new subface to its right-adjacent subface. + if (casin.sh != NULL) { + senext(newsh, casout); + sbond1(casout, casin); + sbond1(casin, casout); + } + // The left-adjacent subface has not been created yet. + senext2(newsh, casin); + // Add the new face into list. + smarktest(newsh); + caveshlist->newindex((void **) &parysh); + *parysh = newsh; + // Move to the convex hull edge at the left of 'splitsh'. + neighsh = *splitsh; + while (1) { + senextself(neighsh); + spivot(neighsh, casout); + if (casout.sh == NULL) { + *splitsh = neighsh; + break; + } + if (sorg(casout) != sdest(neighsh)) sesymself(casout); + neighsh = casout; + } + // A convex hull edge. Is it visible by p. + pa = sorg(*splitsh); + pb = sdest(*splitsh); + ori = orient3d(pa, pb, dummypoint, insertpt); + if (ori >= 0) break; + } + } + + // Form the Bowyer-Watson cavity. + for (i = 0; i < caveshlist->objects; i++) { + parysh = (face *) fastlookup(caveshlist, i); + for (j = 0; j < 3; j++) { + sspivot(*parysh, checkseg); + if (checkseg.sh == NULL) { + spivot(*parysh, neighsh); + if (neighsh.sh != NULL) { + if (!smarktested(neighsh)) { + if (bwflag) { + pa = sorg(neighsh); + pb = sdest(neighsh); + pc = sapex(neighsh); + sign = incircle3d(pa, pb, pc, insertpt); + if (sign < 0) { + smarktest(neighsh); + caveshlist->newindex((void **) &pssub); + *pssub = neighsh; + } + } else { + sign = 1; // A boundary edge. + } + } else { + sign = -1; // Not a boundary edge. + } + } else { + if (loc == OUTSIDE) { + // It is a boundary edge if it does not contain insertp. + if ((sorg(*parysh)==insertpt) || (sdest(*parysh)==insertpt)) { + sign = -1; // Not a boundary edge. + } else { + sign = 1; // A boundary edge. + } + } else { + sign = 1; // A boundary edge. + } + } + } else { + sign = 1; // A segment! + } + if (sign >= 0) { + // Add a boundary edge. + caveshbdlist->newindex((void **) &pssub); + *pssub = *parysh; + } + senextself(*parysh); + } + } + + // Creating new subfaces. + for (i = 0; i < caveshbdlist->objects; i++) { + parysh = (face *) fastlookup(caveshbdlist, i); + sspivot(*parysh, checkseg); + if ((parysh->shver & 01) != 0) sesymself(*parysh); + pa = sorg(*parysh); + pb = sdest(*parysh); + // Create a new subface. + makeshellface(subfacepool, &newsh); + setshvertices(newsh, pa, pb, insertpt); + setshellmark(newsh, getshellmark(*parysh)); + if (checkconstraints) { + area = areabound(*parysh); + areabound(newsh) = area; + } + // Connect newsh to outer subfaces. + spivot(*parysh, casout); + if (casout.sh != NULL) { + casin = casout; + if (checkseg.sh != NULL) { + spivot(casin, neighsh); + while (neighsh.sh != parysh->sh) { + casin = neighsh; + spivot(casin, neighsh); + } + } + sbond1(newsh, casout); + sbond1(casin, newsh); + } + if (checkseg.sh != NULL) { + ssbond(newsh, checkseg); + } + // Connect oldsh <== newsh (for connecting adjacent new subfaces). + sbond1(*parysh, newsh); + } + + // Set a handle for searching. + recentsh = newsh; + + // Connect adjacent new subfaces together. + for (i = 0; i < caveshbdlist->objects; i++) { + // Get an old subface at edge [a, b]. + parysh = (face *) fastlookup(caveshbdlist, i); + sspivot(*parysh, checkseg); + spivot(*parysh, newsh); // The new subface [a, b, p]. + senextself(newsh); // At edge [b, p]. + spivot(newsh, neighsh); + if (neighsh.sh == NULL) { + // Find the adjacent new subface at edge [b, p]. + pb = sdest(*parysh); + neighsh = *parysh; + while (1) { + senextself(neighsh); + spivotself(neighsh); + if (neighsh.sh == NULL) break; + if (!smarktested(neighsh)) break; + if (sdest(neighsh) != pb) sesymself(neighsh); + } + if (neighsh.sh != NULL) { + // Now 'neighsh' is a new subface at edge [b, #]. + if (sorg(neighsh) != pb) sesymself(neighsh); + assert(sorg(neighsh) == pb); // SELF_CHECK + assert(sapex(neighsh) == insertpt); // SELF_CHECK + senext2self(neighsh); // Go to the open edge [p, b]. + spivot(neighsh, casout); // SELF_CHECK + assert(casout.sh == NULL); // SELF_CHECK + sbond2(newsh, neighsh); + } else { + assert(loc == OUTSIDE); // SELF_CHECK + } + } + spivot(*parysh, newsh); // The new subface [a, b, p]. + senext2self(newsh); // At edge [p, a]. + spivot(newsh, neighsh); + if (neighsh.sh == NULL) { + // Find the adjacent new subface at edge [p, a]. + pa = sorg(*parysh); + neighsh = *parysh; + while (1) { + senext2self(neighsh); + spivotself(neighsh); + if (neighsh.sh == NULL) break; + if (!smarktested(neighsh)) break; + if (sorg(neighsh) != pa) sesymself(neighsh); + } + if (neighsh.sh != NULL) { + // Now 'neighsh' is a new subface at edge [#, a]. + if (sdest(neighsh) != pa) sesymself(neighsh); + assert(sdest(neighsh) == pa); // SELF_CHECK + assert(sapex(neighsh) == insertpt); // SELF_CHECK + senextself(neighsh); // Go to the open edge [a, p]. + spivot(neighsh, casout); // SELF_CHECK + assert(casout.sh == NULL); // SELF_CHECK + sbond2(newsh, neighsh); + } else { + assert(loc == OUTSIDE); // SELF_CHECK + } + } + } + + if (checksubfaces) { + // Add all new subfaces into list. + for (i = 0; i < caveshbdlist->objects; i++) { + // Get an old subface at edge [a, b]. + parysh = (face *) fastlookup(caveshbdlist, i); + spivot(*parysh, newsh); // The new subface [a, b, p]. + if (b->verbose > 1) { + printf(" Queue a new subface (%d, %d, %d).\n", + pointmark(sorg(newsh)), pointmark(sdest(newsh)), + pointmark(sapex(newsh))); + } + subfacstack->newindex((void **) &pssub); + *pssub = newsh; + } + } + + if (splitseg != NULL) { + // Split the segment [a, b]. + aseg = *splitseg; + pa = sorg(aseg); + pb = sdest(aseg); + if (b->verbose > 1) { + printf(" Split seg (%d, %d) by %d.\n", pointmark(pa), pointmark(pb), + pointmark(insertpt)); + } + // Detach the adjacent tets from this segment. + stpivot(aseg, neightet); + if (neightet.tet != NULL) { + // It should not be a dead tet. + assert(neightet.tet[4] != NULL); // SELF_CHECK + assert(((org(neightet) == pa) && (dest(neightet) == pb)) || + ((org(neightet) == pb) && (dest(neightet) == pa))); // SELF_CHECK + spintet = neightet; + while (1) { + tssdissolve(spintet); + fnextself(spintet); + if (spintet.tet == neightet.tet) break; + } + // Clean the seg-to-tet pointer. + stdissolve(aseg); + } + // Insert the new point p. + makeshellface(subsegpool, &bseg); + setshvertices(bseg, insertpt, pb, NULL); + setsdest(aseg, insertpt); + setshellmark(bseg, getshellmark(aseg)); + if (checkconstraints) { + areabound(bseg) = areabound(aseg); + } + // Connect [p, b]<->[b, #]. + senext(aseg, aoutseg); + spivotself(aoutseg); + if (aoutseg.sh != NULL) { + senext(bseg, boutseg); + sbond2(boutseg, aoutseg); + } + // Connect [a, p] <-> [p, b]. + senext(aseg, aoutseg); + senext2(bseg, boutseg); + sbond2(aoutseg, boutseg); + // Connect subsegs [a, p] and [p, b] to the true new subfaces. + for (i = 0; i < n; i++) { + spivot(abfaces[i], newsh); // The faked new subface. + if (sorg(newsh) != pa) sesymself(newsh); + senext2(newsh, neighsh); // The edge [p, a] in newsh + spivot(neighsh, casout); + ssbond(casout, aseg); + senext(newsh, neighsh); // The edge [b, p] in newsh + spivot(neighsh, casout); + ssbond(casout, bseg); + } + if (n > 1) { + // Create the two face rings at [a, p] and [p, b]. + for (i = 0; i < n; i++) { + spivot(abfaces[i], newsh); // The faked new subface. + if (sorg(newsh) != pa) sesymself(newsh); + spivot(abfaces[(i + 1) % n], neighsh); // The next faked new subface. + if (sorg(neighsh) != pa) sesymself(neighsh); + senext2(newsh, casout); // The edge [p, a] in newsh. + senext2(neighsh, casin); // The edge [p, a] in neighsh. + spivotself(casout); + spivotself(casin); + sbond1(casout, casin); // Let the i's face point to (i+1)'s face. + senext(newsh, casout); // The edge [b, p] in newsh. + senext(neighsh, casin); // The edge [b, p] in neighsh. + spivotself(casout); + spivotself(casin); + sbond1(casout, casin); + } + } else { + // Only one subface contains this segment. + // assert(n == 1); + spivot(abfaces[0], newsh); // The faked new subface. + if (sorg(newsh) != pa) sesymself(newsh); + senext2(newsh, casout); // The edge [p, a] in newsh. + spivotself(casout); + sdissolve(casout); // Disconnect to faked subface. + senext(newsh, casout); // The edge [b, p] in newsh. + spivotself(casout); + sdissolve(casout); // Disconnect to faked subface. + } + // Delete the faked new subfaces. + for (i = 0; i < n; i++) { + spivot(abfaces[i], newsh); // The faked new subface. + shellfacedealloc(subfacepool, newsh.sh); + } + if (checksubsegs) { + // Add two subsegs into stack (for recovery). + if (!sinfected(aseg)) { + s = randomnation(subsegstack->objects + 1); + subsegstack->newindex((void **) &parysh); + *parysh = * (face *) fastlookup(subsegstack, s); + sinfect(aseg); + parysh = (face *) fastlookup(subsegstack, s); + *parysh = aseg; + } + assert(!sinfected(bseg)); // SELF_CHECK + s = randomnation(subsegstack->objects + 1); + subsegstack->newindex((void **) &parysh); + *parysh = * (face *) fastlookup(subsegstack, s); + sinfect(bseg); + parysh = (face *) fastlookup(subsegstack, s); + *parysh = bseg; + } + delete [] abfaces; + } + + // Delete the old subfaces. + for (i = 0; i < caveshlist->objects; i++) { + parysh = (face *) fastlookup(caveshlist, i); + if (checksubfaces) { + // Disconnect in the neighbor tets. + stpivot(*parysh, neightet); + if (neightet.tet != NULL) { + tsdissolve(neightet); + symself(neightet); + tsdissolve(neightet); + } + } + shellfacedealloc(subfacepool, parysh->sh); + } + + // Clean the working lists. + caveshlist->restart(); + caveshbdlist->restart(); + + return loc; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// sscoutsegment() Look for a segment in surface triangulation. // +// // +// The segment is given by the origin of 'searchsh' and 'endpt'. Assume the // +// orientation of 'searchsh' is CCW w.r.t. the above point. // +// // +// If an edge in T is found matching this segment, the segment is "locaked" // +// in T at the edge. Otherwise, flip the first edge in T that the segment // +// crosses. Continue the search from the flipped face. // +// // +/////////////////////////////////////////////////////////////////////////////// + +enum tetgenmesh::intersection tetgenmesh::sscoutsegment(face *searchsh, + point endpt) +{ + face flipshs[2], neighsh; + face newseg, checkseg; + point startpt, pa, pb, pc, pd; + enum intersection dir; + REAL ori_ab, ori_ca; + REAL dist_b, dist_c; + + enum {MOVE_AB, MOVE_CA} nextmove; + + shellface sptr; + + // The origin of 'searchsh' is fixed. + startpt = sorg(*searchsh); // pa = startpt; + + if (b->verbose > 1) { + printf(" Scout segment (%d, %d).\n", pointmark(startpt), + pointmark(endpt)); + } + + // Search an edge in 'searchsh' on the path of this segment. + while (1) { + + pb = sdest(*searchsh); + if (pb == endpt) { + dir = SHAREEDGE; // Found! + break; + } + + pc = sapex(*searchsh); + if (pc == endpt) { + senext2self(*searchsh); + sesymself(*searchsh); + dir = SHAREEDGE; // Found! + break; + } + + ori_ab = orient3d(startpt, pb, dummypoint, endpt); + ori_ca = orient3d(pc, startpt, dummypoint, endpt); + + if (ori_ab < 0) { + if (ori_ca < 0) { // (--) + // Both sides are viable moves. + spivot(*searchsh, neighsh); // At edge [a, b]. + assert(neighsh.sh != NULL); // SELF_CHECK + pd = sapex(neighsh); + dist_b = NORM2(endpt[0] - pd[0], endpt[1] - pd[1], endpt[2] - pd[2]); + senext2(*searchsh, neighsh); // At edge [c, a]. + spivotself(neighsh); + assert(neighsh.sh != NULL); // SELF_CHECK + pd = sapex(neighsh); + dist_c = NORM2(endpt[0] - pd[0], endpt[1] - pd[1], endpt[2] - pd[2]); + if (dist_c < dist_b) { + nextmove = MOVE_CA; + } else { + nextmove = MOVE_AB; + } + } else { // (-#) + nextmove = MOVE_AB; + } + } else { + if (ori_ca < 0) { // (#-) + nextmove = MOVE_CA; + } else { + if (ori_ab > 0) { + if (ori_ca > 0) { // (++) + // The segment intersects with edge [b, c]. + dir = ACROSSEDGE; + break; + } else { // (+0) + // The segment collinear with edge [c, a]. + senext2self(*searchsh); + sesymself(*searchsh); + dir = ACROSSVERT; + break; + } + } else { + if (ori_ca > 0) { // (0+) + // The segment collinear with edge [a, b]. + dir = ACROSSVERT; + break; + } else { // (00) + // startpt == endpt. Not possible. + assert(0); // SELF_CHECK + } + } + } + } + + // Move 'searchsh' to the next face, keep the origin unchanged. + if (nextmove == MOVE_AB) { + spivot(*searchsh, neighsh); + if (sorg(neighsh) != pb) sesymself(neighsh); + senext(neighsh, *searchsh); + } else { + senext2(*searchsh, neighsh); + spivotself(neighsh); + if (sdest(neighsh) != pc) sesymself(neighsh); + *searchsh = neighsh; + } + assert(sorg(*searchsh) == startpt); // SELF_CHECK + + } // while + + if (dir == SHAREEDGE) { + // Insert the segment into the triangulation. + makeshellface(subsegpool, &newseg); + setshvertices(newseg, startpt, endpt, NULL); + ssbond(*searchsh, newseg); + spivot(*searchsh, neighsh); + if (neighsh.sh != NULL) { + ssbond(neighsh, newseg); + } + return dir; + } + + if (dir == ACROSSVERT) { + // A point is found collinear with this segment. + return dir; + } + + if (dir == ACROSSEDGE) { + // Edge [b, c] intersects with the segment. + senext(*searchsh, flipshs[0]); + sspivot(flipshs[0], checkseg); + if (checkseg.sh != NULL) { + printf("Error: Invalid PLC.\n"); + pb = sorg(flipshs[0]); + pc = sdest(flipshs[0]); + printf(" Two segments (%d, %d) and (%d, %d) intersect.\n", + pointmark(startpt), pointmark(endpt), pointmark(pb), pointmark(pc)); + terminatetetgen(2); + } + // Flip edge [b, c], queue unflipped edges (for Delaunay checks). + spivot(flipshs[0], flipshs[1]); + assert(flipshs[1].sh != NULL); // SELF_CHECK + if (sorg(flipshs[1]) != sdest(flipshs[0])) sesymself(flipshs[1]); + flip22(flipshs, 1); + // The flip may create an invered triangle, check it. + pa = sapex(flipshs[1]); + pb = sapex(flipshs[0]); + pc = sorg(flipshs[0]); + pd = sdest(flipshs[0]); + // Check if pa and pb are on the different sides of [pc, pd]. + // Re-use ori_ab, ori_ca for the tests. + ori_ab = orient3d(pc, pd, dummypoint, pb); + ori_ca = orient3d(pd, pc, dummypoint, pa); + assert(ori_ab * ori_ca != 0); // SELF_CHECK + if (ori_ab < 0) { + if (b->verbose > 1) { + printf(" Queue an inversed triangle (%d, %d, %d) %d\n", + pointmark(pc), pointmark(pd), pointmark(pb), pointmark(pa)); + } + futureflip = flipshpush(futureflip, &flipshs[0]); + } else if (ori_ca < 0) { + if (b->verbose > 1) { + printf(" Queue an inversed triangle (%d, %d, %d) %d\n", + pointmark(pd), pointmark(pc), pointmark(pa), pointmark(pb)); + } + futureflip = flipshpush(futureflip, &flipshs[1]); + } + // Set 'searchsh' s.t. its origin is 'startpt'. + *searchsh = flipshs[0]; + assert(sorg(*searchsh) == startpt); + } + + return sscoutsegment(searchsh, endpt); +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// scarveholes() Remove triangles not in the facet. // +// // +// This routine re-uses the two global arrays: caveshlist and caveshbdlist. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::scarveholes(int holes, REAL* holelist) +{ + face *parysh, searchsh, neighsh; + face checkseg; + enum location loc; + int i, j; + + // Get all triangles. Infect unprotected convex hull triangles. + smarktest(recentsh); + caveshlist->newindex((void **) &parysh); + *parysh = recentsh; + for (i = 0; i < caveshlist->objects; i++) { + parysh = (face *) fastlookup(caveshlist, i); + searchsh = *parysh; + searchsh.shver = 0; + for (j = 0; j < 3; j++) { + spivot(searchsh, neighsh); + // Is this side on the convex hull? + if (neighsh.sh != NULL) { + if (!smarktested(neighsh)) { + smarktest(neighsh); + caveshlist->newindex((void **) &parysh); + *parysh = neighsh; + } + } else { + // A hull side. Check if it is protected by a segment. + sspivot(searchsh, checkseg); + if (checkseg.sh == NULL) { + // Not protected. Save this face. + if (!sinfected(searchsh)) { + sinfect(searchsh); + caveshbdlist->newindex((void **) &parysh); + *parysh = searchsh; + } + } + } + senextself(searchsh); + } + } + + // Infect the triangles in the holes. + for (i = 0; i < 3 * holes; i += 3) { + searchsh = recentsh; + loc = slocate(&(holelist[i]), &searchsh, true); + if (loc != OUTSIDE) { + sinfect(searchsh); + caveshbdlist->newindex((void **) &parysh); + *parysh = searchsh; + } + } + + // Find and infect all exterior triangles. + for (i = 0; i < caveshbdlist->objects; i++) { + parysh = (face *) fastlookup(caveshbdlist, i); + searchsh = *parysh; + searchsh.shver = 0; + for (j = 0; j < 3; j++) { + spivot(searchsh, neighsh); + if (neighsh.sh != NULL) { + sspivot(searchsh, checkseg); + if (checkseg.sh == NULL) { + if (!sinfected(neighsh)) { + sinfect(neighsh); + caveshbdlist->newindex((void **) &parysh); + *parysh = neighsh; + } + } else { + sdissolve(neighsh); // Disconnect a protected face. + } + } + senextself(searchsh); + } + } + + // Delete exterior triangles, unmark interior triangles. + for (i = 0; i < caveshlist->objects; i++) { + parysh = (face *) fastlookup(caveshlist, i); + if (sinfected(*parysh)) { + shellfacedealloc(subfacepool, parysh->sh); + } else { + sunmarktest(*parysh); + } + } + + caveshlist->restart(); + caveshbdlist->restart(); +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// triangulate() Create a CDT for the facet. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::triangulate(int shmark, arraypool* ptlist, arraypool* conlist, + int holes, REAL* holelist) +{ + face searchsh, newsh; + face newseg; + point pa, pb, pc, *ppt, *cons; + enum location loc; + int i; + + if (b->verbose > 1) { + printf(" %ld vertices, %ld segments",ptlist->objects,conlist->objects); + if (holes > 0) { + printf(", %d holes", holes); + } + printf(", shmark: %d.\n", shmark); + } + + if (ptlist->objects < 3l) { + return; // No enough points. Do nothing. + } + if (conlist->objects < 3l) { + return; // No enough segments. Do nothing. + } + + if (ptlist->objects == 3l) { + // The facet has only one triangle. + pa = * (point *) fastlookup(ptlist, 0); + pb = * (point *) fastlookup(ptlist, 1); + pc = * (point *) fastlookup(ptlist, 2); + makeshellface(subfacepool, &newsh); + setshvertices(newsh, pa, pb, pc); + setshellmark(newsh, shmark); + // Create three new segments. + for (i = 0; i < 3; i++) { + makeshellface(subsegpool, &newseg); + setshvertices(newseg, sorg(newsh), sdest(newsh), NULL); + ssbond(newsh, newseg); + senextself(newsh); + } + if (getpointtype(pa) == VOLVERTEX) { + setpointtype(pa, FACETVERTEX); + } + if (getpointtype(pb) == VOLVERTEX) { + setpointtype(pb, FACETVERTEX); + } + if (getpointtype(pc) == VOLVERTEX) { + setpointtype(pc, FACETVERTEX); + } + return; + } + + // Calulcate an above point of this facet. + if (!calculateabovepoint(ptlist, &pa, &pb, &pc)) { + return; // The point set is degenerate. + } + + // Create an initial triangulation. + makeshellface(subfacepool, &newsh); + setshvertices(newsh, pa, pb, pc); + setshellmark(newsh, shmark); + recentsh = newsh; + + if (getpointtype(pa) == VOLVERTEX) { + setpointtype(pa, FACETVERTEX); + } + if (getpointtype(pb) == VOLVERTEX) { + setpointtype(pb, FACETVERTEX); + } + if (getpointtype(pc) == VOLVERTEX) { + setpointtype(pc, FACETVERTEX); + } + + // Incrementally build the triangulation. + pinfect(pa); + pinfect(pb); + pinfect(pc); + for (i = 0; i < ptlist->objects; i++) { + ppt = (point *) fastlookup(ptlist, i); + if (!pinfected(*ppt)) { + searchsh = recentsh; + loc = sinsertvertex(*ppt, &searchsh, NULL, true, true); + if (getpointtype(*ppt) == VOLVERTEX) { + setpointtype(*ppt, FACETVERTEX); + } + } else { + puninfect(*ppt); // This point has inserted. + } + } + + // Insert the segments. + for (i = 0; i < conlist->objects; i++) { + cons = (point *) fastlookup(conlist, i); + searchsh = recentsh; + loc = slocate(cons[0], &searchsh, true); + assert(loc == ONVERTEX); // SELF_CHECK + // Recover the segment. Some edges may be flipped. + sscoutsegment(&searchsh, cons[1]); + if (futureflip != NULL) { + // Recover locally Delaunay edges. + lawsonflip(); + } + } + + // Remove exterior and hole triangles. + scarveholes(holes, holelist); +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// unifysubfaces() Unify two identical subfaces. // +// // +// Two subfaces, f1 [a, b, c] and f2 [a, b, d], share the same edge [a, b]. // +// If c = d, then f1 and f2 are identical. In such case, f2 is deleted, all // +// connections to f2 are re-directed to f1. Otherwise, these two subfaces // +// intersect, and the mesher is stopped. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::unifysubfaces(face *f1, face *f2) +{ + face casout, casin, neighsh; + face sseg, checkseg; + point pa, pb, pc, pd; + int i; + + assert(f1->sh != f2->sh); // SELF_CHECK + + pa = sorg(*f1); + pb = sdest(*f1); + pc = sapex(*f1); + + assert(sorg(*f2) == pa); // SELF_CHECK + assert(sdest(*f2) == pb); // SELF_CHECK + pd = sapex(*f2); + + if (pc != pd) { + printf("Error: Invalid PLC! Two coplanar subfaces intersect.\n"); + printf(" 1st (#%4d): (%d, %d, %d)\n", getshellmark(*f1), + pointmark(pa), pointmark(pb), pointmark(pc)); + printf(" 2nd (#%4d): (%d, %d, %d)\n", getshellmark(*f2), + pointmark(pa), pointmark(pb), pointmark(pd)); + terminatetetgen(2); + } + + // f1 and f2 are identical, replace f2 by f1. + if (!b->quiet) { + printf("Warning: Facet #%d is duplicated with Facet #%d. Removed!\n", + getshellmark(*f2), getshellmark(*f1)); + } + + // Make possible disconnections/reconnections at neighbors of f2. + for (i = 0; i < 3; i++) { + spivot(*f1, casout); + if (casout.sh == NULL) { + // f1 has no adjacent subfaces yet. + spivot(*f2, casout); + if (casout.sh != NULL) { + // Re-direct the adjacent connections of f2 to f1. + casin = casout; + spivot(casin, neighsh); + while (neighsh.sh != f2->sh) { + casin = neighsh; + spivot(casin, neighsh); + } + // Connect casout <= f1 <= casin. + sbond1(*f1, casout); + sbond1(casin, *f1); + } + } + sspivot(*f2, sseg); + if (sseg.sh != NULL) { + // f2 has a segment. It must be different to f1's. + sspivot(*f1, checkseg); // SELF_CHECK + if (checkseg.sh != NULL) { // SELF_CHECK + assert(checkseg.sh != sseg.sh); // SELF_CHECK + } + // Disconnect bonds of subfaces to this segment. + spivot(*f2, casout); + if (casout.sh != NULL) { + casin = casout; + ssdissolve(casin); + spivot(casin, neighsh); + while (neighsh.sh != f2->sh) { + casin = neighsh; + ssdissolve(casin); + spivot(casin, neighsh); + } + } + // Delete the segment. + shellfacedealloc(subsegpool, sseg.sh); + } + spivot(*f2, casout); + if (casout.sh != NULL) { + // Find the subface (casin) pointing to f2. + casin = casout; + spivot(casin, neighsh); + while (neighsh.sh != f2->sh) { + casin = neighsh; + spivot(casin, neighsh); + } + // Disconnect f2 <= casin. + sdissolve(casin); + } + senextself(*f1); + senextself(*f2); + } // i + + // Delete f2. + shellfacedealloc(subfacepool, f2->sh); +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// unifysegments() Remove redundant segments and create face links. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::unifysegments() +{ + badface *facelink, *newlinkitem, *f1, *f2; + face *facperverlist, sface; + face subsegloop, testseg; + point torg, tdest; + REAL ori1, ori2, ori3; + REAL n1[3], n2[3]; + int *idx2faclist; + int segmarker; + int idx, k, m; + + if (b->verbose > 1) { + printf(" Unifying segments.\n"); + } + + // Create a mapping from vertices to subfaces. + makepoint2submap(subfacepool, idx2faclist, facperverlist); + + segmarker = 1; + subsegloop.shver = 0; + subsegpool->traversalinit(); + subsegloop.sh = shellfacetraverse(subsegpool); + while (subsegloop.sh != (shellface *) NULL) { + torg = sorg(subsegloop); + tdest = sdest(subsegloop); + + idx = pointmark(torg) - in->firstnumber; + // Loop through the set of subfaces containing 'torg'. Get all the + // subfaces containing the edge (torg, tdest). Save and order them + // in 'sfacelist', the ordering is defined by the right-hand rule + // with thumb points from torg to tdest. + for (k = idx2faclist[idx]; k < idx2faclist[idx + 1]; k++) { + sface = facperverlist[k]; + // The face may be deleted if it is a duplicated face. + if (sface.sh[3] == NULL) continue; + // Search the edge torg->tdest. + assert(sorg(sface) == torg); // SELF_CHECK + if (sdest(sface) != tdest) { + senext2self(sface); + sesymself(sface); + } + if (sdest(sface) != tdest) continue; + + // Save the face f in facelink. + if (flippool->items >= 2) { + f1 = facelink; + for (m = 0; m < flippool->items - 1; m++) { + f2 = f1->nextitem; + ori1 = orient3d(torg, tdest, sapex(f1->ss), sapex(f2->ss)); + ori2 = orient3d(torg, tdest, sapex(f1->ss), sapex(sface)); + if (ori1 > 0) { + // apex(f2) is below f1. + if (ori2 > 0) { + // apex(f) is below f1 (see Fig.1). + ori3 = orient3d(torg, tdest, sapex(f2->ss), sapex(sface)); + if (ori3 > 0) { + // apex(f) is below f2, insert it. + break; + } else if (ori3 < 0) { + // apex(f) is above f2, continue. + } else { // ori3 == 0; + // f is coplanar and codirection with f2. + unifysubfaces(&(f2->ss), &sface); + break; + } + } else if (ori2 < 0) { + // apex(f) is above f1 below f2, inset it (see Fig. 2). + break; + } else { // ori2 == 0; + // apex(f) is coplanar with f1 (see Fig. 5). + ori3 = orient3d(torg, tdest, sapex(f2->ss), sapex(sface)); + if (ori3 > 0) { + // apex(f) is below f2, insert it. + break; + } else { + // f is coplanar and codirection with f1. + unifysubfaces(&(f1->ss), &sface); + break; + } + } + } else if (ori1 < 0) { + // apex(f2) is above f1. + if (ori2 > 0) { + // apex(f) is below f1, continue (see Fig. 3). + } else if (ori2 < 0) { + // apex(f) is above f1 (see Fig.4). + ori3 = orient3d(torg, tdest, sapex(f2->ss), sapex(sface)); + if (ori3 > 0) { + // apex(f) is below f2, insert it. + break; + } else if (ori3 < 0) { + // apex(f) is above f2, continue. + } else { // ori3 == 0; + // f is coplanar and codirection with f2. + unifysubfaces(&(f2->ss), &sface); + break; + } + } else { // ori2 == 0; + // f is coplanar and with f1 (see Fig. 6). + ori3 = orient3d(torg, tdest, sapex(f2->ss), sapex(sface)); + if (ori3 > 0) { + // f is also codirection with f1. + unifysubfaces(&(f1->ss), &sface); + break; + } else { + // f is above f2, continue. + } + } + } else { // ori1 == 0; + // apex(f2) is coplanar with f1. By assumption, f1 is not + // coplanar and codirection with f2. + if (ori2 > 0) { + // apex(f) is below f1, continue (see Fig. 7). + } else if (ori2 < 0) { + // apex(f) is above f1, insert it (see Fig. 7). + break; + } else { // ori2 == 0. + // apex(f) is coplanar with f1 (see Fig. 8). + // f is either codirection with f1 or is codirection with f2. + facenormal(torg, tdest, sapex(f1->ss), n1, 1); + facenormal(torg, tdest, sapex(sface), n2, 1); + if (DOT(n1, n2) > 0) { + unifysubfaces(&(f1->ss), &sface); + } else { + unifysubfaces(&(f2->ss), &sface); + } + break; + } + } + // Go to the next item; + f1 = f2; + } // for (m = 0; ...) + if (sface.sh[3] != NULL) { + // Insert sface between f1 and f2. + newlinkitem = (badface *) flippool->alloc(); + newlinkitem->ss = sface; + newlinkitem->nextitem = f1->nextitem; + f1->nextitem = newlinkitem; + } + } else if (flippool->items == 1) { + f1 = facelink; + // Make sure that f is not coplanar and codirection with f1. + ori1 = orient3d(torg, tdest, sapex(f1->ss), sapex(sface)); + if (ori1 == 0) { + // f is coplanar with f1 (see Fig. 8). + facenormal(torg, tdest, sapex(f1->ss), n1, 1); + facenormal(torg, tdest, sapex(sface), n2, 1); + if (DOT(n1, n2) > 0) { + // The two faces are codirectional as well. + unifysubfaces(&(f1->ss), &sface); + } + } + // Add this face to link if it is not deleted. + if (sface.sh[3] != NULL) { + // Add this face into link. + newlinkitem = (badface *) flippool->alloc(); + newlinkitem->ss = sface; + newlinkitem->nextitem = NULL; + f1->nextitem = newlinkitem; + } + } else { + // The first face. + newlinkitem = (badface *) flippool->alloc(); + newlinkitem->ss = sface; + newlinkitem->nextitem = NULL; + facelink = newlinkitem; + } + } // for (k = idx2faclist[idx]; ...) + + if (b->verbose > 1) { + printf(" Found %ld segments at (%d %d).\n", flippool->items, + pointmark(torg), pointmark(tdest)); + } + + // Set the connection between this segment and faces containing it, + // at the same time, remove redundant segments. + f1 = facelink; + for (k = 0; k < flippool->items; k++) { + sspivot(f1->ss, testseg); + // If 'testseg' is not 'subsegloop' and is not dead, it is redundant. + if ((testseg.sh != subsegloop.sh) && (testseg.sh[3] != NULL)) { + shellfacedealloc(subsegpool, testseg.sh); + } + // Bonds the subface and the segment together. + ssbond(f1->ss, subsegloop); + f1 = f1->nextitem; + } + + // Create the face ring at the segment. + if (flippool->items > 1) { + f1 = facelink; + for (k = 1; k <= flippool->items; k++) { + k < flippool->items ? f2 = f1->nextitem : f2 = facelink; + if (b->verbose > 2) { + printf(" Bond subfaces (%d, %d, %d) and (%d, %d, %d).\n", + pointmark(torg), pointmark(tdest), pointmark(sapex(f1->ss)), + pointmark(torg), pointmark(tdest), pointmark(sapex(f2->ss))); + } + sbond1(f1->ss, f2->ss); + f1 = f2; + } + } + + // Set the unique segment marker into the unified segment. + setshellmark(subsegloop, segmarker); + segmarker++; + flippool->restart(); + + subsegloop.sh = shellfacetraverse(subsegpool); + } + + delete [] idx2faclist; + delete [] facperverlist; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// mergefacets() Merge adjacent coplanar facets. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::mergefacets() +{ + arraypool *ptlist; + face parentsh, neighsh, neineighsh; + face segloop; + point eorg, edest, *parypt; + REAL ori, ori1, ori2; + bool mergeflag, aboveflag; + int* segspernodelist; + int fidx1, fidx2; + int i, j; + + if (b->verbose > 1) { + printf(" Merging adjacent coplanar facets.\n"); + } + + // Initialize 'segspernodelist'. + segspernodelist = new int[pointpool->items + 1]; + for (i = 0; i < pointpool->items + 1; i++) segspernodelist[i] = 0; + + // Allocate a list for calculate an above point. + ptlist = new arraypool(sizeof(point *), 4); + + // Loop all segments, counter the number of segments sharing each vertex. + subsegpool->traversalinit(); + segloop.sh = shellfacetraverse(subsegpool); + while (segloop.sh != (shellface *) NULL) { + // Increment the number of sharing segments for each endpoint. + for (i = 0; i < 2; i++) { + j = pointmark((point) segloop.sh[3 + i]); + segspernodelist[j]++; + } + segloop.sh = shellfacetraverse(subsegpool); + } + + // Loop all segments, merge adjacent coplanar facets. + subsegpool->traversalinit(); + segloop.sh = shellfacetraverse(subsegpool); + while (segloop.sh != (shellface *) NULL) { + eorg = sorg(segloop); + edest = sdest(segloop); + spivot(segloop, parentsh); + spivot(parentsh, neighsh); + if (neighsh.sh != NULL) { + spivot(neighsh, neineighsh); + if (parentsh.sh == neineighsh.sh) { + // Exactly two subfaces at this segment. + fidx1 = getshellmark(parentsh) - 1; + fidx2 = getshellmark(neighsh) - 1; + // Possible to merge them if they are not in the same facet. + if (fidx1 != fidx2) { + // Test if they are coplanar wrt the tolerance. + ori = orient3d(eorg, edest, sapex(parentsh), sapex(neighsh)); + if ((ori == 0) || iscoplanar(eorg, edest, sapex(parentsh), + sapex(neighsh), ori)) { + // Found two adjacent coplanar facets. + // Only can remove the segment if both apexes are on the + // different sides of the edge [eorg, edest]. + ptlist->newindex((void **) &parypt); + *parypt = eorg; + ptlist->newindex((void **) &parypt); + *parypt = edest; + ptlist->newindex((void **) &parypt); + *parypt = sapex(parentsh); + ptlist->newindex((void **) &parypt); + *parypt = sapex(neighsh); + aboveflag = calculateabovepoint(ptlist, NULL, NULL, NULL); + if (aboveflag) { + ori1 = orient3d(eorg, edest, dummypoint, sapex(parentsh)); + ori2 = orient3d(eorg, edest, dummypoint, sapex(neighsh)); + } else { + ori1 = ori2 = 1.0; // Bad data. + } + if (ori1 * ori2 < 0) { + mergeflag = ((in->facetmarkerlist == NULL) || + (in->facetmarkerlist[fidx1] == in->facetmarkerlist[fidx2])); + if (mergeflag) { + if (b->verbose > 1) { + printf(" Removing segment (%d, %d).\n", pointmark(eorg), + pointmark(edest)); + } + ssdissolve(parentsh); + ssdissolve(neighsh); + shellfacedealloc(subsegpool, segloop.sh); + j = pointmark(eorg); + segspernodelist[j]--; + if (segspernodelist[j] == 0) { + setpointtype(eorg, FACETVERTEX); + } + j = pointmark(edest); + segspernodelist[j]--; + if (segspernodelist[j] == 0) { + setpointtype(edest, FACETVERTEX); + } + // Add the edge to flip stack. + futureflip = flipshpush(futureflip, &parentsh); + } + } + ptlist->restart(); // For the next test. + } + } + } + } // if (neighsh.sh != NULL) + segloop.sh = shellfacetraverse(subsegpool); + } + + if (futureflip != NULL) { + // Do Delaunay flip. + lawsonflip(); + } + + delete ptlist; + delete [] segspernodelist; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// markacutevertices() Classify vertices as ACUTEVERTEXs or RIDGEVERTEXs. // +// // +// Initially, all segment vertices are marked as VOLVERTEX (after calling // +// incrementaldelaunay()). // +// // +// A segment vertex is ACUTEVERTEX if it two segments incident it form an // +// interior angle less than 60 degree, otherwise, it is a RIDGEVERTEX. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::markacutevertices() +{ + point pa, pb, pc; + REAL anglimit, ang; + bool acuteflag; + int acutecount; + int idx, i, j; + + face* segperverlist; + int* idx2seglist; + + if (b->verbose) { + printf(" Marking acute vertices.\n"); + } + + // Construct a map from points to segments. + makepoint2submap(subsegpool, idx2seglist, segperverlist); + + anglimit = PI / 3.0; // 60 degree. + acutecount = 0; + + // Loop over the set of vertices. + pointpool->traversalinit(); + pa = pointtraverse(); + while (pa != NULL) { + idx = pointmark(pa) - in->firstnumber; + // Mark it if it is an endpoint of some segments. + if (idx2seglist[idx + 1] > idx2seglist[idx]) { + acuteflag = false; + // Do a brute-force pair-pair check. + for (i = idx2seglist[idx]; i < idx2seglist[idx + 1] && !acuteflag; i++) { + pb = sdest(segperverlist[i]); + for (j = i + 1; j < idx2seglist[idx + 1] && !acuteflag; j++) { + pc = sdest(segperverlist[j]); + ang = interiorangle(pa, pb, pc, NULL); + acuteflag = ang < anglimit; + } + } + // Now mark the vertex. + if (b->verbose > 1) { + printf(" Mark %d as %s.\n", pointmark(pa), acuteflag ? + "ACUTEVERTEX" : "RIDGEVERTEX"); + } + setpointtype(pa, acuteflag ? ACUTEVERTEX : RIDGEVERTEX); + acutecount += (acuteflag ? 1 : 0); + } + pa = pointtraverse(); + } + + if (b->verbose) { + printf(" %d acute vertices.\n", acutecount); + } + + delete [] idx2seglist; + delete [] segperverlist; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// meshsurface() Create a surface mesh of the input PLC. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::meshsurface() +{ + arraypool *ptlist, *conlist; + point *idx2verlist; + point tstart, tend, *pnewpt, *cons; + tetgenio::facet *f; + tetgenio::polygon *p; + int end1, end2; + int shmark, i, j; + + if (!b->quiet) { + printf("Creating surface mesh.\n"); + } + + // Create a map from indices to points. + makeindex2pointmap(idx2verlist); + + // Initialize arrays (block size: 2^8 = 256). + ptlist = new arraypool(sizeof(point *), 8); + conlist = new arraypool(2 * sizeof(point *), 8); + + // Loop the facet list, triangulate each facet. + for (shmark = 1; shmark <= in->numberoffacets; shmark++) { + + // Get a facet F. + f = &in->facetlist[shmark - 1]; + + // Process the duplicated points first, they are marked with type + // DUPLICATEDVERTEX. If p and q are duplicated, and p'index > q's, + // then p is substituted by q. + if (dupverts > 0l) { + // Loop all polygons of this facet. + for (i = 0; i < f->numberofpolygons; i++) { + p = &(f->polygonlist[i]); + // Loop other vertices of this polygon. + for (j = 0; j < p->numberofvertices; j++) { + end1 = p->vertexlist[j]; + tstart = idx2verlist[end1]; + if (getpointtype(tstart) == DUPLICATEDVERTEX) { + // Reset the index of vertex-j. + tend = point2ppt(tstart); + end2 = pointmark(tend); + p->vertexlist[j] = end2; + } + } + } + } + + // Loop polygons of F, get the set of vertices and segments. + for (i = 0; i < f->numberofpolygons; i++) { + // Get a polygon. + p = &(f->polygonlist[i]); + // Get the first vertex. + end1 = p->vertexlist[0]; + if ((end1 < in->firstnumber) || + (end1 >= in->firstnumber + in->numberofpoints)) { + if (!b->quiet) { + printf("Warning: Invalid the 1st vertex %d of polygon", end1); + printf(" %d in facet %d.\n", i + 1, shmark); + } + continue; // Skip this polygon. + } + tstart = idx2verlist[end1]; + // Add tstart to V if it haven't been added yet. + if (!pinfected(tstart)) { + pinfect(tstart); + ptlist->newindex((void **) &pnewpt); + *pnewpt = tstart; + } + // Loop other vertices of this polygon. + for (j = 1; j <= p->numberofvertices; j++) { + // get a vertex. + if (j < p->numberofvertices) { + end2 = p->vertexlist[j]; + } else { + end2 = p->vertexlist[0]; // Form a loop from last to first. + } + if ((end2 < in->firstnumber) || + (end2 >= in->firstnumber + in->numberofpoints)) { + if (!b->quiet) { + printf("Warning: Invalid vertex %d in polygon %d", end2, i + 1); + printf(" in facet %d.\n", shmark); + } + } else { + if (end1 != end2) { + // 'end1' and 'end2' form a segment. + tend = idx2verlist[end2]; + // Add tstart to V if it haven't been added yet. + if (!pinfected(tend)) { + pinfect(tend); + ptlist->newindex((void **) &pnewpt); + *pnewpt = tend; + } + // Save the segment in S (conlist). + conlist->newindex((void **) &cons); + cons[0] = tstart; + cons[1] = tend; + // Set the start for next continuous segment. + end1 = end2; + tstart = tend; + } else { + // Two identical vertices mean an isolated vertex of F. + if (p->numberofvertices > 2) { + // This may be an error in the input, anyway, we can continue + // by simply skipping this segment. + if (!b->quiet) { + printf("Warning: Polygon %d has two identical verts", i + 1); + printf(" in facet %d.\n", shmark); + } + } + // Ignore this vertex. + } + } + // Is the polygon degenerate (a segment or a vertex)? + if (p->numberofvertices == 2) break; + } + } + // Unmark vertices. + for (i = 0; i < ptlist->objects; i++) { + pnewpt = (point *) fastlookup(ptlist, i); + puninfect(*pnewpt); + } + + // Triangulate F into a CDT. + triangulate(shmark, ptlist, conlist, f->numberofholes, f->holelist); + + // Clear working lists. + ptlist->restart(); + conlist->restart(); + } + + delete ptlist; + delete conlist; + delete [] idx2verlist; + + // Remove redundant segments and build the face links. + unifysegments(); + + if (b->object == tetgenbehavior::STL) { + // Remove redundant vertices (for .stl input mesh). + jettisonnodes(); + } + + if (!b->nomerge && !b->nobisect) { + // Merge adjacent coplanar facets. + mergefacets(); + } + + // Mark acutes vertices. + markacutevertices(); + + // The total number of iunput segments. + insegments = subsegpool->items; +} + +#endif // #ifndef surfaceCXX \ No newline at end of file diff --git a/contrib/TetgenNew/tetgen.h b/contrib/TetgenNew/tetgen.h new file mode 100644 index 0000000000..b7e971f039 --- /dev/null +++ b/contrib/TetgenNew/tetgen.h @@ -0,0 +1,1922 @@ +/////////////////////////////////////////////////////////////////////////////// +// // +// TetGen // +// // +// A Quality Tetrahedral Mesh Generator and 3D Delaunay Triangulator // +// // +// Develop version // +// Start: August 9, 2008 // +// // +// Copyright (C) 2002--2008 // +// Hang Si // +// Research Group: Numerical Mathematics and Scientific Computing // +// Weierstrass Institute for Applied Analysis and Stochastics // +// Mohrenstr. 39, 10117 Berlin, Germany // +// si@wias-berlin.de // +// // +// TetGen is freely available through the website: http://tetgen.berlios.de. // +// 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 + +/////////////////////////////////////////////////////////////////////////////// +// // +// tetgen.h // +// // +// Header file of the TetGen library. Also is the user-level header file. // +// // +/////////////////////////////////////////////////////////////////////////////// + +// Header files for using the C/C++ standard library. + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <math.h> +#include <time.h> +#include <assert.h> + +/////////////////////////////////////////////////////////////////////////////// +// // +// TetGen Library // +// // +// The library consists of three classes: tetgenio, tetgenbehavior, and // +// tetgenmesh. Tetgenio provides interfaces for passing data into and out of // +// the library; tetgenbehavior keeps the runtime options and thus controls // +// the behaviors of TetGen; tetgenmesh implements mesh data strcutures and // +// algorithms for generating meshes. // +// // +// There are few global functions. tetrahedralize() is provided for calling // +// TetGen from another program. Two functions: orient3d() and insphere() are // +// incorporated from a public C code provided by Shewchuk. They performing // +// exact geometrical tests. // +// // +/////////////////////////////////////////////////////////////////////////////// + +// To compile TetGen as a library instead of an executable program, define +// the TETLIBRARY symbol. + +// #define TETLIBRARY + +// Uncomment the following line to disable assert macros. These macros are +// inserted in places where I hope to catch bugs. + +// #define NDEBUG + +// To insert lots of self-checks for internal errors, define the SELF_CHECK +// symbol. This will slow down the program significantly. + +// #define SELF_CHECK + +// For single precision ( which will save some memory and reduce paging), +// define the symbol SINGLE by using the -DSINGLE compiler switch or by +// writing "#define SINGLE" below. +// +// For double precision ( which will allow you to refine meshes to a smaller +// edge length), leave SINGLE undefined. + +// #define SINGLE + +#ifdef SINGLE + #define REAL float +#else + #define REAL double +#endif // not defined SINGLE + +// Maximum number of characters in a file name (including the null). + +enum {FILENAMESIZE = 1024}; + +// Maximum numbers of chars in a line read from a file (incl. the null). + +enum {INPUTLINESIZE = 1024}; + +/////////////////////////////////////////////////////////////////////////////// +// // +// tetgenio Passing data into and out of the library. // +// // +// This class contains a collection of arrays which stores points, facets, // +// tetrahedra, and so forth. TetGen will read and write these arrays accord- // +// ing to the options specified in a tetgenbehavior object. The arrays are // +// corresponding to the fields defined in TetGen's input/output file formats.// +// If you want to use the library of TetGen, It is necessary to understand // +// TetGen's input/output file formats (see user's manual). // +// // +// Once an object of tetgenio is declared, no array is created. One has to // +// allocate enough memory for them, e.g., use the "new" operator in C++. On // +// deletion of the object, the memory occupied by these arrays needs to be // +// freed. Routine deinitialize() will be automatically called. It will de- // +// allocate the memory for an array if it is not a NULL. However, it assumes // +// that the memory is allocated by the C++ "new" operator. If you use malloc // +// (), you should free() them and set the pointers to NULLs before reaching // +// deinitialize(). // +// // +// In all cases, the first item in an array is stored starting at index [0]. // +// However, that item is item number `firstnumber' which may be '0' or '1'. // +// Be sure to set the 'firstnumber' be '1' if your indices pointing into the // +// pointlist is starting from '1'. Default, it is '0'. // +// // +// Tetgenio also contains routines for reading and writing TetGen's files as // +// well. Both the library of TetGen and TetView use these routines to parse // +// input files, i.e., .node, .poly, .smesh, .ele, .face, and .edge files. // +// // +/////////////////////////////////////////////////////////////////////////////// + +class tetgenio { + + public: + + // The polygon structure. A "polygon" is a planar polygon which may be + // non-convex but contains no holes. + // 'vertexlist' is a list of vertices (indices) of the polygon odered in + // either counterclockwise or clockwise way. + + typedef struct { + int *vertexlist; + int numberofvertices; + } polygon; + + static void init(polygon* p) { + p->vertexlist = (int *) NULL; + p->numberofvertices = 0; + } + + // The facet structure. A "facet" is a facet. It may be non-convex and + // contains arbitrary number of holes. Hence it consistes of a list of + // polygons and a list holes. + + typedef struct { + polygon *polygonlist; + int numberofpolygons; + REAL *holelist; + int numberofholes; + } facet; + + static void init(facet* f) { + f->polygonlist = (polygon *) NULL; + f->numberofpolygons = 0; + f->holelist = (REAL *) NULL; + f->numberofholes = 0; + } + + // A 'voroedge' is an edge of the Voronoi diagram. It corresponds to a + // Delaunay face. Each voroedge is either a line segment connecting + // two Voronoi vertices or a ray starting from a Voronoi vertex to an + // "infinite vertex". 'v1' and 'v2' are two indices pointing to the + // list of Voronoi vertices. 'v1' must be non-negative, while 'v2' may + // be -1 if it is a ray, in this case, the unit normal of this ray is + // given in 'vnormal'. + + typedef struct { + int v1, v2; + REAL vnormal[3]; + } voroedge; + + // A 'vorofacet' is an facet of the Voronoi diagram. It corresponds to a + // Delaunay edge. Each Voronoi facet is a convex polygon formed by a + // list of Voronoi edges, it may not be closed. 'c1' and 'c2' are two + // indices pointing into the list of Voronoi cells, i.e., the two cells + // share this facet. 'elist' is an array of indices pointing into the + // list of Voronoi edges, 'elist[0]' saves the number of Voronoi edges + // (including rays) of this facet. + + typedef struct { + int c1, c2; + int *elist; + } vorofacet; + + // The periodic boundary condition group data structure. A "pbcgroup" + // contains the definition of a pbc and the list of pbc point pairs. + // 'fmark1' and 'fmark2' are the facetmarkers of the two pbc facets f1 + // and f2, respectively. 'transmat' is the transformation matrix which + // maps a point in f1 into f2. An array of pbc point pairs are saved + // in 'pointpairlist'. The first point pair is at indices [0] and [1], + // followed by remaining pairs. Two integers per pair. + + typedef struct { + int fmark1, fmark2; + REAL transmat[4][4]; + int numberofpointpairs; + int *pointpairlist; + } pbcgroup; + + public: + + // Items are numbered starting from 'firstnumber' (0 or 1), default is 0. + int firstnumber; + // Dimension of the mesh (2 or 3), default is 3. + int mesh_dim; + // Does the lines in .node file contain index or not, default is TRUE. + bool useindex; + + // 'pointlist': The array of point coordinates. The first point's x + // coordinate is at index [0] and its y coordinate at index [1], its + // z coordinate is at index [2], followed by the coordinates of the + // remaining points. Each point occupies three REALs. + // 'pointattributelist': An array of point attributes. Each point's + // attributes occupy 'numberofpointattributes' REALs. + // 'pointmtrlist': An array of metric tensors at points. Each point's + // tensor occupies 'numberofpointmtr' REALs. + // 'pointmarkerlist': An array of point markers; one int per point. + + REAL *pointlist; + REAL *pointattributelist; + REAL *pointmtrlist; + int *pointmarkerlist; + int numberofpoints; + int numberofpointattributes; + int numberofpointmtrs; + + // 'tetrahedronlist': The array of tetrahedra. The first tet's first + // node is at index [0], followed by its other nodes, optionally + // followed by quadratc nodes of the tet. Each tet occupies + // 'numberofcorners' (4 or 6) ints. + // 'tetrahedronattributelist': The array of tet attributes. Each tet + // has `numberoftetrahedronattributes' REALs. + // 'tetrahedronvolumelist': The array of constraints, i.e. tetrahedron's + // maximum volume; one REAL per element. Input only. + // 'neighborlist': The array of element neighbors; 'numberofcorners' + // ints per element. Output only. + + int *tetrahedronlist; + REAL *tetrahedronattributelist; + REAL *tetrahedronvolumelist; + int *neighborlist; + int numberoftetrahedra; + int numberofcorners; + int numberoftetrahedronattributes; + + // `facetlist': The 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': The array of volume holes. The first hole's x, y and z + // coordinates are at indices [0], [1] and [2], followed by the + // remaining holes. Three REALs per hole. + + REAL *holelist; + int numberofholes; + + // `regionlist': The array of regional attributes and volume constraints. + // The first constraint's x, y and z coordinates are at indices [0], + // [1] and [2], followed by the regional attribute at index [3], foll- + // owed by the maximum volume at index [4]. Five REALs per constraint. + // Note that each regional attribute is used only if you select the `A' + // switch, and each volume constraint is used only if you select the + // `a' switch (with no number following). + + REAL *regionlist; + int numberofregions; + + // `facetconstraintlist': The array of facet maximal area constraints. + // Two REALs per constraint. The first one is the facet marker (cast + // it to int), the second is its maximum area bound. + // Note the 'facetconstraintlist' is used only for the 'q' switch. + + REAL *facetconstraintlist; + int numberoffacetconstraints; + + // `segmentconstraintlist': The array of segment max. length constraints. + // Three REALs per constraint. The first two are the indices (pointing + // into 'pointlist') of the endpoints of the segment, the third is its + // maximum length bound. + // Note the 'segmentconstraintlist' is used only for the 'q' switch. + + REAL *segmentconstraintlist; + int numberofsegmentconstraints; + + // 'pbcgrouplist': The array of periodic boundary condition groups. + + pbcgroup *pbcgrouplist; + int numberofpbcgroups; + + // `trifacelist': The array of triangluar face list. The first face's + // endpoints are at indices [0], [1] and [2], followed by the + // remaining faces. Three ints per face. + // `adjtetlist': The array of adjacent tets. Each face has at most two + // adjacent tets, the first face's adjacent tets are at [0], [1]. Two + // ints per face. A '-1' indicates outside (no adj. tet). This list + // is output when '-nn' switch is used. + // `trifacemarkerlist': The array of face markers; one int per face. + + int *trifacelist; + int *adjtetlist; + int *trifacemarkerlist; + int numberoftrifaces; + + // `edgelist': The array of edges (or segments). The first edge's + // endpoints are at indices [0] and [1], followed by the remaining + // edges. Two ints per edge. + // `edgemarkerlist': The array of edge markers; one int per edge. + + int *edgelist; + int *edgemarkerlist; + int numberofedges; + + // 'vpointlist': The array of Voronoi vertex coordinates (like pointlist). + // 'vedgelist': The array of Voronoi edges. Each entry is a 'voroedge'. + // 'vfacetlist': The array of Voronoi facets. Each entry is a 'vorofacet'. + // 'vcelllist': The array of Voronoi cells. Each entry is an array of + // indices pointing into 'vfacetlist'. The 0th entry is used to store + // the length of this array. + + REAL *vpointlist; + voroedge *vedgelist; + vorofacet *vfacetlist; + int **vcelllist; + int numberofvpoints; + int numberofvedges; + int numberofvfacets; + int numberofvcells; + + public: + + // Initialize routine. + void initialize(); + void deinitialize(); + + // Input & output routines. + bool load_node_call(FILE* infile, int markers, const char* nodefilename); + bool load_node(const char* filename); + bool load_pbc(const char* filename); + bool load_var(const char* filename); + bool load_mtr(const char* filename); + bool load_poly(const char* filename); + bool load_off(const char* filename); + bool load_ply(const char* filename); + bool load_stl(const char* filename); + bool load_medit(const char* filename); + bool load_vtk(const char* filename); + bool load_plc(const char* filename, int object); + bool load_tetmesh(const char* filename); + bool load_voronoi(const char* filename); + void save_nodes(const char* filename); + void save_elements(const char* filename); + void save_faces(const char* filename); + void save_edges(const char* filename); + void save_neighbors(const char* filename); + void save_poly(const char* filename); + + // Read line and parse string functions. + char *readline(char* string, FILE* infile, int *linenumber); + char *findnextfield(char* string); + char *readnumberline(char* string, FILE* infile, const char* infilename); + char *findnextnumber(char* string); + + // Constructor and destructor. + tetgenio() {initialize();} + ~tetgenio() {deinitialize();} +}; + +/////////////////////////////////////////////////////////////////////////////// +// // +// tetgenbehavior Parsing command line switches and file names. // +// // +// It includes a list of variables corresponding to the commandline switches // +// for control the behavior of TetGen. These varibales are all initialized // +// to their default values. // +// // +// Use function parse_commandline() to set the vaules of the variables. It // +// accepts the standard parameters (e.g., 'argc' and 'argv') that pass to C // +// & C++ main() function. Alternatively a string which contains the command // +// line options can be used as its parameter. Please refer to the User's // +// manula for the availble options of TetGen. // +// // +/////////////////////////////////////////////////////////////////////////////// + +class tetgenbehavior { // Begin of class tetgenbehavior + + public: + + // Labels defining the input objects supported by TetGen. They are + // identified from the file extension of the inputs. + // - NODES, a list of nodes (.node); + // - POLY, a piecewise linear complex (.poly or .smesh); + // - OFF, a polyhedron (.off, Geomview's file format); + // - PLY, a polyhedron (.ply, file format from gatech); + // - STL, a surface mesh (.stl, stereolithography format); + // - MEDIT, a surface mesh (.mesh, Medit's file format); + // - MESH, a tetrahedral mesh (.ele). + // If no extension is available, the imposed commandline switch + // (-p or -r) implies the type of the object. + + enum objecttype {NONE, NODES, POLY, OFF, PLY, STL, MEDIT, VTK, MESH}; + + // Variables of command line switches. Each variable corresponds to a + // switch. Most of them are initialized to 0. + + int plc; // -p + int quality; // -q + int refine; // -r + int coarse; // -R + int metric; // -m + int varvolume; // -a without a number + int fixedvolume; // -a with a number + int bowyerwatson; // -b + int convexity; // -c + int insertaddpoints; // -i + int regionattrib; // -A + int conformdel; // -D + int diagnose; // -d + int zeroindex; // -z + int order; // -o + int facesout; // -f + int edgesout; // -e + int neighout; // -n + int voroout; // -v + int meditview; // -g + int gidview; // -G + int geomview; // -O + int nobound; // -B + int nonodewritten; // -N + int noelewritten; // -E + int nofacewritten; // -F + int noiterationnum; // -I + int nomerge; // -M + int nobisect; // -Y + int nojettison; // -J + int docheck; // -C + int quiet; // -Q + int verbose; // -V + int useshelles; // -p, -r, -q, -d, or -R + long steinerleft; // -S with a number + REAL minratio; // number after -q + REAL goodratio; // number calculated from 'minratio' + REAL minangle; // minimum angle bound + REAL goodangle; // cosine squared of minangle + REAL maxvolume; // number after -a + REAL mindihedral; // number after -qq + REAL maxdihedral; // number after -qqq + REAL epsilon; // number after -T + enum objecttype object; // determined by -p, or -r + + // Variables used to save command line switches and in/out file names. + char commandline[1024]; + char infilename[1024]; + char outfilename[1024]; + char addinfilename[1024]; + char bgmeshfilename[1024]; + + void versioninfo(); + void syntax(); + void usage(); + + // Command line parse routine. + bool parse_commandline(int argc, char **argv); + bool parse_commandline(char *switches) { + return parse_commandline(0, &switches); + } + + tetgenbehavior(); + ~tetgenbehavior() {} +}; + +/////////////////////////////////////////////////////////////////////////////// +// // +// Geometric predicates // +// // +// Return one of the values +1, 0, and -1 on basic geometric questions such // +// as the orientation of point sets, in-circle, and in-sphere tests. They // +// are basic units for implmenting geometric algorithms. TetGen uses two 3D // +// geometric predicates: the orientation and in-sphere tests. // +// // +// Orientation test: let a, b, c be a sequence of 3 non-collinear points in // +// R^3. They defines a unique hypeplane H. Let H+ and H- be the two spaces // +// separated by H, which are defined as follows (using the left-hand rule): // +// make a fist using your left hand in such a way that your fingers follow // +// the order of a, b and c, then your thumb is pointing to H+. Given any // +// point d in R^3, the orientation test returns +1 if d lies in H+, -1 if d // +// lies in H-, or 0 if d lies on H. // +// // +// In-sphere test: let a, b, c, d be 4 non-coplanar points in R^3. They // +// defines a unique circumsphere S. Given any point e in R^3, the in-sphere // +// test returns +1 if e lies inside S, or -1 if e lies outside S, or 0 if e // +// lies on S. // +// // +// The correctness of geometric predicates is crucial for the control flow // +// and hence for the correctness and robustness of an implementation of a // +// geometric algorithm. The following routines use arbitrary precision // +// floating-point arithmetic. They are fast and robust. It is provided by J. // +// Schewchuk in public domain (http://www.cs.cmu.edu/~quake/robust.html). // +// The source code are found in a separate file "predicates.cxx". // +// // +/////////////////////////////////////////////////////////////////////////////// + +REAL exactinit(); +REAL orient3d(REAL *pa, REAL *pb, REAL *pc, REAL *pd); +REAL insphere(REAL *pa, REAL *pb, REAL *pc, REAL *pd, REAL *pe); + +/////////////////////////////////////////////////////////////////////////////// +// // +// Class tetgenmesh // +// // +// An object of tetgenmesh can be used to store a triangular or tetrahedral // +// mesh and its settings. TetGen's functions operates on one mesh each time. // +// This type allows reusing of the same function for different meshes. // +// // +// The mesh data structure (tetrahedron-based and triangle-edge data struct- // +// ures) are declared. There are other accessary data type defined as well, // +// for efficient memory management and link list operations, etc. // +// // +// All algorithms TetGen used are implemented in this data type as member // +// functions. References of these algorithms can be found in user's manual. // +// // +/////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +class tetgenmesh { // Begin of class tetgenmesh +/////////////////////////////////////////////////////////////////////////////// + +public: + +// For efficiency, a variety of data structures are allocated in bulk. +// The following constants determine how many of each structure is +// allocated at once. + +enum {VERPERBLOCK = 4092, SUBPERBLOCK = 4092, ELEPERBLOCK = 8188}; + +// Labels that signify whether a record consists primarily of pointers +// or of floating-point words. Used for data alignment in memorypool. + +enum wordtype {POINTER, FLOATINGPOINT}; + +// Labels that signify the type of a vertex. + +enum verttype {UNUSEDVERTEX, DUPLICATEDVERTEX, VOLVERTEX, RIDGEVERTEX, + ACUTEVERTEX, FACETVERTEX, STEINERVERTEX, DEADVERTEX}; + +// Labels that signify the result of point location. + +enum location {INTET, ONFACE, ONEDGE, ONVERTEX, OUTSIDE, ENCSEGMENT, ENCFACE}; + +// Labels that signify the result of intersection tests. + +enum intersection {DISJOINT, INTERSECT, SHAREVERT, SHAREEDGE, SHAREFACE, + TOUCHEDGE, TOUCHFACE, ACROSSVERT, ACROSSEDGE, ACROSSFACE, ACROSSTET, + TRIEDGEINT, EDGETRIINT, COLLISIONFACE, ACROSSSUBSEG, ACROSSSUBFACE}; + +/////////////////////////////////////////////////////////////////////////////// +// // +// Mesh data structures // +// // +// There are four types of mesh elements: tetrahedra, subfaces, subsegments, // +// and points, where subfaces and subsegments are triangles and edges which // +// appear on boundaries. The elements of all the four types consist of a // +// tetrahedral mesh of a 3D domain. TetGen uses three data types: 'tetra- // +// hedron', 'shellface', and 'point'. A 'tetrahedron' is a tetrahedron; a // +// 'shellface' represents either a subface or a subsegment; and a 'point' // +// is a point. These three data types, linked by pointers comprise a mesh. // +// // +/////////////////////////////////////////////////////////////////////////////// + +// The tetrahedron data structure. +typedef REAL **tetrahedron; + +// The shellface data structure. +typedef REAL **shellface; + +// The point data structure. +typedef REAL *point; + +/////////////////////////////////////////////////////////////////////////////// +// // +// Mesh handles // +// // +// Two special data types, 'triface' and 'face' are defined for maintaining // +// and updating meshes. They are like pointers (or handles), which allow you // +// to hold one particular part of the mesh, i.e., a tetrahedron, a triangle, // +// an edge and a vertex. However, these data types do not themselves store // +// any part of the mesh. The mesh is made of the data types defined above. // +// // +/////////////////////////////////////////////////////////////////////////////// + +// A 'triface' holds a tetrahedron 'tet'. In particular, it holds one +// edge of a face of the tetrahedron. Since each tetrahedron has 4 +// faces and each face has 6 directed edges, hence there are total +// 24 different edges in 'tet'. Each edge of 'tet' is represented by +// a pair (loc, ver), where 'loc' (range from 0 to 3) indicates a face +// of 'tet', and 'ver' (range from 0 to 5) indicates a directed edge +// edge of that face. + +class triface { + + public: + + tetrahedron* tet; + int loc, ver; + + // Constructors; + triface() : tet(0), loc(0), ver(0) {} + // Operators; + triface& operator=(const triface& t) { + tet = t.tet; loc = t.loc; ver = t.ver; + return *this; + } + bool operator==(triface& t) { + return tet == t.tet && loc == t.loc && ver == t.ver; + } + bool operator!=(triface& t) { + return tet != t.tet || loc != t.loc || ver != t.ver; + } +}; + +// A 'face' holds a shellface 'sh'. In particular, it holds one directed +// edge of the shellface. There are 6 directed edges in a triangle. +// 'shver' (range from 0 to 5) indicates a directed edge in 'sh'. + +class face { + + public: + + shellface *sh; + int shver; + + // Constructors; + face() : sh(0), shver(0) {} + // Operators; + face& operator=(const face& s) { + sh = s.sh; shver = s.shver; + return *this; + } + bool operator==(face& s) {return (sh == s.sh) && (shver == s.shver);} + bool operator!=(face& s) {return (sh != s.sh) || (shver != s.shver);} +}; + +/////////////////////////////////////////////////////////////////////////////// +// // +// Arraypool A dynamic array // +// // +// Each arraypool contains an array of pointers to a number of blocks. Each // +// block contains the same fixed number of objects. Each index of the array // +// addesses a particular object in the pool. The most significant bits add- // +// ress the index of the block containing the object. The less significant // +// bits address this object within the block. // +// // +// 'objectbytes' is the size of one object in blocks; 'log2objectsperblock' // +// is the base-2 logarithm of 'objectsperblock'; 'objects' counts the number // +// of allocated objects; 'totalmemory' is the totoal memorypool in bytes. // +// // +/////////////////////////////////////////////////////////////////////////////// + +class arraypool { + + public: + + int objectbytes; + int objectsperblock; + int log2objectsperblock; + int toparraylen; + char **toparray; + unsigned 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(); +}; + +/////////////////////////////////////////////////////////////////////////////// +// // +// Memorypool A dynamic pool of memory // +// // +// A type used to allocate memory 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. // +// // +// itemwordtype is set to POINTER or FLOATINGPOINT, and is used to suggest // +// what sort of word the record is primarily made up of. alignbytes // +// determines how new records should be aligned in memory. itembytes and // +// itemwords are the length of a record in bytes (after rounding up) and // +// words. itemsperblock is the number of items allocated at once in a // +// single block. items is the number of currently allocated items. // +// maxitems is the maximum number of items that have been allocated at // +// once; it is the current number of items plus the number of records kept // +// on deaditemstack. // +// // +/////////////////////////////////////////////////////////////////////////////// + +class memorypool { + + public: + + void **firstblock, **nowblock; + void *nextitem; + void *deaditemstack; + void **pathblock; + void *pathitem; + wordtype itemwordtype; + int alignbytes; + int itembytes, itemwords; + int itemsperblock; + long items, maxitems; + int unallocateditems; + int pathitemsleft; + + void poolinit(int, int, enum wordtype, int); + void restart(); + void *alloc(); + void dealloc(void*); + void traversalinit(); + void *traverse(); + + memorypool() {} + memorypool(int, int, enum wordtype, int); + ~memorypool(); +}; + +/////////////////////////////////////////////////////////////////////////////// +// // +// The badface structure // +// // +// A multiple usages structure. Despite of its name, a 'badface' can be used // +// to represent the following objects: // +// - a face of a tetrahedron which is (possibly) non-Delaunay; // +// - an encroached subsegment or subface; // +// - a bad-quality tetrahedron, i.e, has too large radius-edge ratio; // +// - a sliver, i.e., has good radius-edge ratio but nearly zero volume; // +// - a degenerate tetrahedron (see routine checkdegetet()). // +// - a recently flipped face (saved for undoing the flip later). // +// // +// It has the following fields: 'tt' holds a tetrahedron; 'ss' holds a sub- // +// segment or subface; 'cent' is the circumcent of 'tt' or 'ss', 'key' is a // +// special value depending on the use, it can be either the square of the // +// radius-edge ratio of 'tt' or the flipped type of 'tt'; 'forg', 'fdest', // +// 'fapex', and 'foppo' are vertices saved for checking the object in 'tt' // +// or 'ss' is still the same when it was stored; 'noppo' is the fifth vertex // +// of a degenerate point set. 'previtem' and 'nextitem' implement a double // +// link for managing many basfaces. // +// // +/////////////////////////////////////////////////////////////////////////////// + +struct badface { + triface tt; + face ss; + REAL key; + REAL cent[3]; + point forg, fdest, fapex, foppo, noppo; + struct badface *previtem, *nextitem; +}; + +/////////////////////////////////////////////////////////////////////////////// +// // +// Fast Lookup Tables // +// // +// The mesh data structures additionally store geometric informations which // +// help for fast queries. // +// // +/////////////////////////////////////////////////////////////////////////////// + +// For enext() primitive, uses 'ver' as the key. +static int ve[6], ve2[6]; + +// For sorg(), sdest, spaex(), use 'shver' as the key. +static int vo[6], vd[6], va[6]; + +// For bond(), t1 and t2 are two symmetric faces sharing the same edges, it +// takes t1's and t2's edge versions as the first and second keys, returns +// t2's edge corresponds to t1's 0th edge. +// Note: the returned edge version is in {0, 2, 4}. The same follows. +static int verver2zero[6][6]; + +// For bond(), t1's edge corresponds to t2's 0th edge, it takes t1's edge +// version as the key, returns t2's edge corresponds to t1's 0th edge. +static int ver2zero[6]; + +// For fnext(), symedge(), t1 and t2 share the same face, and t2's edge +// corresponds to t1's 0th edge, it takes t1's and t2's edge versions as +// the first and second keys, return t2's edge corresponds to t1's edge. +static int zero2ver[6][6]; + +// For org(), dest() and apex() primitives, use ('loc', 'ver') as the key. +static int locver2org[4][6]; +static int locver2dest[4][6]; +static int locver2apex[4][6]; + +// For oppo() primitives, uses 'loc' as the key. +static int loc2oppo[4]; + +// For fnext() primitives, uses ('loc' * 8 + 'ver') as the key. Returns +// an array containing a new ('loc', 'ver'). +// Note: Only valid for 'ver' equals one of {0, 2, 4}. +static int locver2nextf[32]; + +// Twos maps between ('loc', 'ver') and the edge number (from 0 to 5). +static int locver2edge[4][6]; +static int edge2locver[6][2]; + +// The map from a given face ('loc') to the other three faces in the tet. +// and the map from a given face's edge ('loc', 'ver') to other two +// faces in the tet opposite to this edge. (used in speeding the Bowyer- +// Watson cavity construction). +static int locpivot[4][3]; +static int locverpivot[4][6][2]; + +static int mi1mo3[3]; + +/////////////////////////////////////////////////////////////////////////////// +// // +// Mesh manipulation primitives // +// // +// Mesh navigation and updating are accomplished through a set of mesh // +// manipulation primitives which operate on trifaces and faces. They are // +// introduced below. // +// // +/////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +// // +// Primitives for tetrahedron // +// // +// Each tetrahedron contains four pointers to its adjacent tetrahedra, with // +// their face indices (loc in [0, 3]) and edge versions (ver in [0, 5]). To // +// save memory, all informations of an adjacent tetrahedron are compressed // +// in a single pointer. To make this possible, all tetrahedra are aligned to // +// 16-byte boundaries, so that the last four bits of each pointer are zeros. // +// Each face indice (loc) is compressed into the last two bits, each edge // +// version (ver) is compressed into the second last two bits of the pointer, // +// respectively. encode() and decode() functions implement this feature. // +// // +/////////////////////////////////////////////////////////////////////////////// + +// encode() -- to compress a triface 't' into a single pointer. +// 't.ver' is in [0, 5], first we map it to [0, 2], this is equal to +// divide it by 2 (>> 1), next we shift it to the second last two bits +// of the pointer, this is equal to multiply it by 4 (<< 2). +// Note: Do not combine the bit options for (t).ver. + +#define encode(t) (tetrahedron) ((unsigned long) (t).tet | \ + ((unsigned long) (((t).ver >> 1) << 2)) | (unsigned long) (t).loc) + +// decode() -- to decompose a pointer 'ptr' into a triface 't'. +// For obtaining t.ver, we first extract it (& 12l), then shift it to +// the right by 2 bits (>> 2), then multiply it by 2 (<< 1), hence +// t.ver is in {0,2,4}. Combining the last two operations, we only +// need to divide it by 2 (>> 1). + +#define decode(ptr, t) \ + (t).loc = (int) ((unsigned long) (ptr) & (unsigned long) 3l);\ + (t).ver = (int) (((unsigned long) (ptr) & (unsigned long) 12l) >> 1);\ + (t).tet = (tetrahedron *) ((unsigned long) (ptr) & ~(unsigned long) 15l) + +// sym() -- given triface 't1', get a triface 't2', where 't1' and 't2' +// are the same face in two adjacent tetrahedra. But they may not be +// the same edge. (Refer to bond() function.) + +#define sym(t1, t2) decode((t1).tet[(t1).loc], (t2)) + +#define symself(t) \ + ptr = (t).tet[(t).loc];\ + decode(ptr, (t)) + +// symedge() -- given triface 't1', get a triface 't2', where 't1' and 't2' +// are the same face and the same edge in two adjacent tetrahedra. +// Require "tetrahedron ptr;" and "int tver". + +#define symedge(t1, t2) \ + decode((t1).tet[(t1).loc], (t2));\ + (t2).ver = zero2ver[(t1).ver][(t2).ver] + +#define symedgeself(t) \ + ptr = (t).tet[(t).loc];\ + tver = (t).ver;\ + decode(ptr, (t));\ + (t).ver = zero2ver[tver][(t).ver]; + +// org(), dest(), apex(), oppo() -- return the origin, destination, apex, +// and opposite vertices of the triface. + +#define org(t) (point) (t).tet[locver2org[(t).loc][(t).ver] + 4] + +#define dest(t) (point) (t).tet[locver2dest[(t).loc][(t).ver] + 4] + +#define apex(t) (point) (t).tet[locver2apex[(t).loc][(t).ver] + 4] + +#define oppo(t) (point) (t).tet[loc2oppo[(t).loc] + 4] + +#define setorg(t, p) \ + (t).tet[locver2org[(t).loc][(t).ver] + 4] = (tetrahedron) (p) + +#define setdest(t, p) \ + (t).tet[locver2dest[(t).loc][(t).ver] + 4] = (tetrahedron) (p) + +#define setapex(t, p) \ + (t).tet[locver2apex[(t).loc][(t).ver] + 4] = (tetrahedron) (p) + +#define setoppo(t, p) (t).tet[loc2oppo[(t).loc] + 4] = (tetrahedron) (p) + +#define setvertices(t, p1, p2, p3, p4) \ + setorg(t, p1); \ + setdest(t, p2); \ + setapex(t, p3); \ + setoppo(t, p4) + +// esym(), enext(), enext2() -- primitives for moving edges in face. +// The face remains the same. + +#define esym(t1, t2) \ + (t2).tet = (t1).tet; (t2).loc = (t1).loc;\ + (t2).ver = (t1).ver + (((t1).ver & 01) ? -1 : 1) + +#define esymself(t) (t).ver += (((t).ver & 01) ? -1 : 1) + +#define enext(t1, t2) \ + (t2).tet = (t1).tet; (t2).loc = (t1).loc;\ + (t2).ver = ve[(t1).ver] + +#define enextself(t) (t).ver = ve[(t).ver] + +#define enext2(t1, t2) \ + (t2).tet = (t1).tet; (t2).loc = (t1).loc;\ + (t2).ver = ve2[(t1).ver] + +#define enext2self(t) (t).ver = ve2[(t).ver] + +// enextfnext(), enext2fnext() -- primitives for moving faces in tet. +// the tetrahedron remains the same. +// Note: The input edge version is in {0, 2, 4}. + +#define enext0fnext(t1, t2) \ + iptr = &(locver2nextf[(t1).loc * 8 + (t1).ver]);\ + (t2).tet = (t1).tet;\ + (t2).loc = iptr[0];\ + (t2).ver = iptr[1] + +#define enext0fnextself(t) \ + iptr = &(locver2nextf[(t).loc * 8 + (t).ver]);\ + (t).loc = iptr[0];\ + (t).ver = iptr[1] + +#define enextfnext(t1, t2) \ + iptr = &(locver2nextf[(t1).loc * 8 + ve[(t1).ver]]);\ + (t2).tet = (t1).tet;\ + (t2).loc = iptr[0];\ + (t2).ver = iptr[1] + +#define enextfnextself(t) \ + iptr = &(locver2nextf[(t).loc * 8 + ve[(t).ver]]);\ + (t).loc = iptr[0];\ + (t).ver = iptr[1] + +#define enext2fnext(t1, t2) \ + iptr = &(locver2nextf[(t1).loc * 8 + ve2[(t1).ver]]);\ + (t2).tet = (t1).tet;\ + (t2).loc = iptr[0];\ + (t2).ver = iptr[1] + +#define enext2fnextself(t) \ + iptr = &(locver2nextf[(t).loc * 8 + ve2[(t).ver]]);\ + (t).loc = iptr[0];\ + (t).ver = iptr[1] + +// fnext() -- given an edge (i, j) of a face (i, j, k1) = t1, find the +// next face t2 in the face ring of (i, j), i.e. a face (i, j, k2), +// where (i, j, k1) and (i, j, k2) belong to two tetrahedra. + +void fnext(triface& t1, triface& t2) { + int *iptr; + if (t1.ver & 01) { + // Get the adjacent tet. + decode(t1.tet[t1.loc], t2); + // Adjust the edge (see Fig. fnext-base). + t2.ver = zero2ver[t1.ver][t2.ver]; + // Go to the next face in t2. + iptr = &(locver2nextf[t2.loc * 8 + t2.ver]); + t2.loc = iptr[0]; + t2.ver = iptr[1]; + } else { + // Go to the next face in t1. + iptr = &(locver2nextf[t1.loc * 8 + t1.ver]); + // Get the adjacent tet. + decode(t1.tet[iptr[0]], t2); + // Adjust the edge (see Fig. fnext-base). + t2.ver = zero2ver[iptr[1]][t2.ver]; + } +} + +void fnextself(triface& t) { + tetrahedron ptr; + int *iptr, tver; + if (t.ver & 01) { + ptr = t.tet[t.loc]; + tver = t.ver; + decode(ptr, t); + t.ver = zero2ver[tver][t.ver]; + iptr = &(locver2nextf[t.loc * 8 + t.ver]); + t.loc = iptr[0]; + t.ver = iptr[1]; + } else { + iptr = &(locver2nextf[t.loc * 8 + t.ver]); + ptr = t.tet[iptr[0]]; + decode(ptr, t); + t.ver = zero2ver[iptr[1]][t.ver]; + } +} + +// bond() -- to setup the connections between 't1.loc' <==> 't2.loc'. +// From t1 <-- t2, we bond the edge in 't2' corresponding to the 0-th +// edge in 't1', and vice versa for t1 --> t2. +// NOTE: We assume that t1 and t2 refer to the same edge on input. + +void bond(triface& t1, triface& t2) { + // We will modify edge vers, backup them. + int t1ver = t1.ver, t2ver = t2.ver; + // Find t2's edge corresponds to the t1's 0th edge. + t2.ver = verver2zero[t1.ver][t2.ver]; + // t1 <-- t2 + t1.tet[t1.loc] = encode(t2); + // Find t1's edge corresponds to t2's 0th edge. + t1.ver = ver2zero[t2.ver]; + // t1 --> t2 + t2.tet[t2.loc] = encode(t1); + // Restore the original vers. + t1.ver = t1ver; t2.ver = t2ver; +} + +// elemmarker() -- to read or set element's marker. + +#define elemmarker(ptr) ((int *) (ptr))[elemmarkerindex] + +// elemattribute() -- to check or set element attributes. + +#define elemattribute(ptr, num) (((REAL *) (ptr))[elemattribindex + num]) + +#define setelemattribute(ptr, num, value) \ + ((REAL *) (ptr))[elemattribindex + num] = value + +// volumebound() -- to check or set element's maximum volume bound. + +#define volumebound(ptr) (((REAL *) (ptr))[volumeboundindex]) + +#define setvolumebound(ptr, value) \ + ((REAL *) (ptr))[volumeboundindex] = value + +// infect(), infected(), uninfect() -- primitives to flag or unflag a +// tetrahedron. The last bit of the element marker is flagged (1) +// or unflagged (0). + +#define infect(t) elemmarker((t).tet) |= 1 + +#define uninfect(t) elemmarker((t).tet) &= ~1 + +#define infected(t) ((elemmarker((t).tet) & 1) != 0) + +// marktest(), marktested(), unmarktest() -- primitives to flag or unflag a +// tetrahedron. The last second bit of the element marker is marked (1) +// or unmarked (0). +// One needs them in forming Bowyer-Watson cavity, to mark a tetrahedron if +// it has been checked (for Delaunay case) so later check can be avoided. + +#define marktest(t) elemmarker((t).tet) |= 2 + +#define unmarktest(t) elemmarker((t).tet) &= ~2 + +#define marktested(t) ((elemmarker((t).tet) & 2) != 0) + +// markface(), unmarkface(), facemarked() -- primitives to flag or unflag a +// face of a tetrahedron. From the last 3rd to 6th bits are used for +// face markers, e.g., the last third bit corresponds to loc = 0. +// One use of the face marker is in flip algorithm. Each queued face (check +// for locally Delaunay) is marked. + +#define markface(t) elemmarker((t).tet) |= (4<<(t).loc) + +#define unmarkface(t) elemmarker((t).tet) &= ~(4<<(t).loc) + +#define facemarked(t) ((elemmarker((t).tet) & (4<<(t).loc)) != 0) + +// markedge(), unmarkedge(), edgemarked() -- primitives to flag or unflag an +// edge of a tetrahedron. From the last 7th to 12th bits are used for +// edge markers, e.g., the last 7th bit corresponds to the 0th edge, etc. +// Remark: The last 7th bit is marked by 2^6 = 64. + +#define markedge(t) elemmarker((t).tet) |= (64<<locver2edge[(t).loc][(t).ver]) + +#define unmarkedge(t) \ + elemmarker((t).tet) &= ~(64<<locver2edge[(t).loc][(t).ver]) + +#define edgemarked(t) \ + ((elemmarker((t).tet) & (64<<locver2edge[(t).loc][(t).ver])) != 0) + +/////////////////////////////////////////////////////////////////////////////// +// // +// Primitives for shellfaces // +// // +// Each shellface contains three pointers to its adjacent shellfaces, with // +// their edge versions (shver in [0, 5]). To save memory, all informations // +// of an adjacent shellface are compressed in a single pointer. To make this // +// possible, all shellface are aligned to 8-byte boundaries. // +// // +/////////////////////////////////////////////////////////////////////////////// + +// sencode(), sdecode -- to compress/uncompress a face/pointer. +// The pointer 's.sh' is assumed to be 8-byte aligned. + +#define sencode(s) \ + (shellface) ((unsigned long) (s).sh | (unsigned long) (s).shver) + +#define sdecode(sptr, s) \ + (s).shver = (int) ((unsigned long) (sptr) & (unsigned long) 7l);\ + (s).sh = (shellface *) ((unsigned long) (sptr) & ~ (unsigned long) 7l) + +// sorg(), sdest(), sapex() -- return the origin, destination, apex, +// of the subface. + +#define sorg(s) (point) (s).sh[vo[(s).shver] + 3] + +#define sdest(s) (point) (s).sh[vd[(s).shver] + 3] + +#define sapex(s) (point) (s).sh[va[(s).shver] + 3] + +#define setsdest(s, p) (s).sh[vd[(s).shver] + 3] = (shellface) (p) + +#define setsapex(s, p) (s).sh[va[(s).shver] + 3] = (shellface) (p) + +#define setshvertices(s, p1, p2, p3) \ + (s).sh[vo[(s).shver] + 3] = (shellface) (p1); \ + (s).sh[vd[(s).shver] + 3] = (shellface) (p2); \ + (s).sh[va[(s).shver] + 3] = (shellface) (p3) + +// sesym() - change the origin and destination of the face edge. + +#define sesym(s1, s2) \ + (s2).sh = (s1).sh;\ + (s2).shver = (s1).shver + (((s1).shver & 01) ? -1 : 1); + +#define sesymself(s) \ + (s).shver += (((s).shver & 01) ? -1 : 1); + +// senext(), senext2() -- go to the next (or the previous) face edge. + +#define senext(s1, s2) \ + (s2).sh = (s1).sh;\ + (s2).shver = ve[(s1).shver] + +#define senextself(s) \ + (s).shver = ve[(s).shver] + +#define senext2(s1, s2) \ + (s2).sh = (s1).sh;\ + (s2).shver = ve2[(s1).shver] + +#define senext2self(s) \ + (s).shver = ve2[(s).shver] + +// The general rule to connect two subfaces s1 and s2 is: Let s1's edge +// be a->b, which is in s1's 0th edge ring, then the edge b->a of s2 is +// bonded to s1. The same rule for connecting a subface and a subseg. + +// sbond1() -- s1 and s2 share an edge, connect s1 <-- s2, i.e., s1 knows +// its neighbor is s2. sbond2() connects s1 <==> s2. + +#define sbond1(s1, s2) (s1).sh[(s1).shver >> 1] = sencode(s2); + +#define sbond2(s1, s2) \ + (s1).sh[(s1).shver >> 1] = sencode(s2);\ + (s2).sh[(s2).shver >> 1] = sencode(s1) + +// sdisolve() -- dissolve a subface-subface connection (at one side). + +#define sdissolve(s) (s).sh[(s).shver >> 1] = NULL; + +// ssbond() -- connect a subface (s) and a subsegment (seg) together. +// NOTE: we allow that 'seg.sh' should not be NULL. + +#define ssbond(s, seg) \ + (s).sh[((s).shver >> 1) + 6] = sencode(seg);\ + if ((seg).sh != NULL) (seg).sh[0] = sencode(s) + +// ssdisolve() -- dissolve a subface-subsegment connection (at subface side). + +#define ssdissolve(s) (s).sh[((s).shver >> 1) + 6] = NULL; + +// spivot() -- find the next face in the face ring. + +#define spivot(s1, s2) sdecode((s1).sh[(s1).shver >> 1], s2) + +#define spivotself(s) \ + sptr = (s).sh[(s).shver >> 1];\ + sdecode(sptr, s) + +// sspivot() -- find the abutting subsegment (seg) at the face (s). + +#define sspivot(s, seg) sdecode((s).sh[((s).shver >> 1) + 6], seg) + +// shellmark() -- set or read the shell mark. + +// #define shellmark(s) ((int *) ((s).sh))[shmarkindex] + +// The last two bits of the int ((int *) ((s).sh))[shmarkindex] are used +// by sinfect() and smarktest(). + +int getshellmark(face& s) { + return (((int *) ((s).sh))[shmarkindex]) >> 2; +} + +void setshellmark(face& s, int mark) { + ((int *) ((s).sh))[shmarkindex] = (mark << 2) + + ((((int *) ((s).sh))[shmarkindex]) & 3); +} + +// areabound() -- set of read the maximal area bound. + +#define areabound(s) ((REAL *) ((s).sh))[areaboundindex] + +// sinfect(), sinfected(), suninfect() -- primitives to flag or unflag a +// subface. The last bit of ((int *) ((s).sh))[shmarkindex] is flaged. + +#define sinfect(s) \ + ((int *) ((s).sh))[shmarkindex] = (((int *) ((s).sh))[shmarkindex] | 1) + +#define suninfect(s) \ + ((int *) ((s).sh))[shmarkindex] = (((int *) ((s).sh))[shmarkindex] & ~1) + +#define sinfected(s) ((((int *) ((s).sh))[shmarkindex] & 1) != 0) + +// smarktest(), smarktested(), sunmarktest() -- primitives to flag or unflag +// a subface. The last 2nd bit of ((int *) ((s).sh))[shmarkindex] is flaged. + +#define smarktest(s) \ + ((int *) ((s).sh))[shmarkindex] = (((int *) ((s).sh))[shmarkindex] | 2) + +#define sunmarktest(s) \ + ((int *) ((s).sh))[shmarkindex] = (((int *) ((s).sh))[shmarkindex] & ~2) + +#define smarktested(s) ((((int *) ((s).sh))[shmarkindex] & 2) != 0) + +// farsorg(), farsdest() -- s is a subsegment, return the origin or +// destination of the segment containing s. +// Note: here we assume that two subsegment (a->p) and (p->b) bonded +// at p as: (p->nil->a) and (nil->p->b), in flipn2nf(). + +point farsorg(face& s) { + face travesh, neighsh; + shellface sptr; + travesh = s; + while (1) { + senext2(travesh, neighsh); + spivotself(neighsh); + if (neighsh.sh == NULL) break; + if (sorg(neighsh) != sorg(travesh)) sesymself(neighsh); + assert(sorg(neighsh) == sorg(travesh)); // SELF_CHECK + senext2(neighsh, travesh); + } + return sorg(travesh); +} + +point farsdest(face& s) { + face travesh, neighsh; + shellface sptr; + travesh = s; + while (1) { + senext(travesh, neighsh); + spivotself(neighsh); + if (neighsh.sh == NULL) break; + if (sdest(neighsh) != sdest(travesh)) sesymself(neighsh); + assert(sdest(neighsh) == sdest(travesh)); // SELF_CHECK + senext(neighsh, travesh); + } + return sdest(travesh); +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// Interactions between tetrahedra and subfaces and subsegments. // +// // +/////////////////////////////////////////////////////////////////////////////// + +// tspivot(), stpivot -- given a tet's face t (or a subface s), find the +// abutting subface (or tet). + +void tspivot(triface& t, face& s) { + if ((t).tet[9] != NULL) { + sdecode(((shellface *) (t).tet[9])[(t).loc], s); + } else { + (s).sh = NULL; + } +} + +#define stpivot(s, t) decode((tetrahedron) (s).sh[9], t) + +// tsbond() -- connect a tet and subface. + +void tsbond(triface& t, face& s) { + if ((t).tet[9] == NULL) { + // Allocate space for this tet. + (t).tet[9] = (tetrahedron) tet2subpool->alloc(); + // NULL all fields in this space. + for (int i = 0; i < 4; i++) { + ((shellface *) (t).tet[9])[i] = NULL; + } + } + // Bond t <==> s. + ((shellface *) (t).tet[9])[(t).loc] = sencode(s); + (s).sh[9] = (shellface) encode(t); +} + +// tsdissolve() -- dissolve a tet-subface bond at the tet side. + +void tsdissolve(triface& t) { + if ((t).tet[9] != NULL) { + ((shellface *) (t).tet[9])[(t).loc] = NULL; + } +} + +// stdissolve() -- dissolbe a tet-subface bond at the subface side. + +#define stdissolve(s) (s).sh[9] = NULL; + +// tsspivot() -- given a tet's edge t, return a subsegment s at this edge. +// t and s is bonded through tssbond1(). if s.sh == NULL, the edge is +// not a subsegment. + +void tsspivot(triface& t, face& s) { + if ((t).tet[8] != NULL) { + sdecode(((shellface *) (t).tet[8])[locver2edge[(t).loc][(t).ver]], s); + } else { + (s).sh = NULL; + } +} + +// tssbond1() -- bond a tet edge and a segment (only at tet's edge). + +void tssbond1(triface& t, face& s) { + if ((t).tet[8] == NULL) { + // Allocate space for this tet. + (t).tet[8] = (tetrahedron) tet2segpool->alloc(); + // NULL all fields in this space. + for (int i = 0; i < 6; i++) { + ((shellface *) (t).tet[8])[i] = NULL; + } + } + // Bond the segment. + ((shellface *) (t).tet[8])[locver2edge[(t).loc][(t).ver]] = sencode((s)); +} + +// sstbond() -- bond a tet edge to a segment (only at the segment side). + +#define sstbond(s, t) (s).sh[9] = (shellface) encode(t) + +// tssdissolve() -- dissolve a tet-seg bond at the tet edge side. + +void tssdissolve(triface& t) { + if ((t).tet[8] != NULL) { + ((shellface *) (t).tet[8])[locver2edge[(t).loc][(t).ver]] = NULL; + } +} + +// sstdissolve() -- dissolve a tet-seg bond at the segment side. +// Use stdissolve(). + +/////////////////////////////////////////////////////////////////////////////// +// // +// Primitives for points // +// // +/////////////////////////////////////////////////////////////////////////////// + +#define pointmark(pt) ((int *) (pt))[pointmarkindex] + +#define point2tet(pt) ((tetrahedron *) (pt))[point2tetindex] + +// Given a point 'pa', return a tet 'searchtet' whose origin is pa. + +void point2tetorg(point pa, triface& searchtet) +{ + int i; + + // Search a tet whose origin is pa. + decode(point2tet(pa), searchtet); + assert(searchtet.tet != NULL); // SELF_CHECK + for (i = 4; i < 8; i++) { + if ((point) searchtet.tet[i] == pa) { + // Found. Set pa as its origin. + switch (i) { + case 4: searchtet.loc = 0; searchtet.ver = 0; break; + case 5: searchtet.loc = 0; searchtet.ver = 2; break; + case 6: searchtet.loc = 0; searchtet.ver = 4; break; + case 7: searchtet.loc = 1; searchtet.ver = 2; break; + } + break; + } + } + assert(i < 8); // SELF_CHECK +} + +#define point2ppt(pt) ((point *) (pt))[point2tetindex + 1] + +// #define pointtype(pt) ((enum verttype *) (pt))[pointmarkindex + 1] + +enum verttype getpointtype(point pt) { + return (enum verttype) (((int *) (pt))[pointmarkindex + 1] >> 1); +} + +void setpointtype(point pt, enum verttype type) { + ((int *) (pt))[pointmarkindex + 1] = + ((int) type << 1) + (((int *) (pt))[pointmarkindex + 1] & 1); +} + +// pinfect(), puninfect(), pinfected() -- primitives to flag or unflag +// a point. The last bit of the integer '[pointindex+1]' is flaged. + +#define pinfect(pt) \ + ((int *) (pt))[pointmarkindex + 1] = ((int *) (pt))[pointmarkindex + 1] | 1 + +#define puninfect(pt) \ + ((int *) (pt))[pointmarkindex + 1] = ((int *) (pt))[pointmarkindex + 1] & ~1 + +#define pinfected(pt) ((((int *) (pt))[pointmarkindex + 1] & 1) != 0) + +/////////////////////////////////////////////////////////////////////////////// +// // +// Primitives for arraypools. // +// // +/////////////////////////////////////////////////////////////////////////////// + +// fastlookup() -- A fast, unsafe operation. Return the pointer to the object +// with a given index. Note: The object's block must have been allocated, +// i.e., by the function newindex(). + +#define fastlookup(pool, index) \ + (void *) ((pool)->toparray[(index) >> (pool)->log2objectsperblock] + \ + ((index) & ((pool)->objectsperblock - 1)) * (pool)->objectbytes) + +/////////////////////////////////////////////////////////////////////////////// +// // +// Class Variables. // +// // +/////////////////////////////////////////////////////////////////////////////// + +// Pointer to the input data (a PLC or a mesh). +tetgenio *in; + +// Pointer to the options (and filenames). +tetgenbehavior *b; + +// Pools of tetrahedra, shellfaces, points, etc. +memorypool *tetrahedronpool; +memorypool *subsegpool, *subfacepool; +memorypool *tet2segpool, *tet2subpool; +memorypool *pointpool; +memorypool *flippool; +memorypool *badsegpool, *badsubpool, *badtetpool; + +// A dummy point at infinity (see [Guibas & Stolfi 85]). All the hull +// tetrahedra having this point as a vertex. +point dummypoint; + +// Statck and queue (use flippool) for flips. +badface *futureflip; + +// Arrays used by Bowyer-Watson algorithm. +arraypool *cavetetlist, *cavebdrylist, *caveoldtetlist; +arraypool *caveshlist, *caveshbdlist; + +// Stacks used by the boundary recovery algorithm. +arraypool *subsegstack, *subfacstack; + +// Arrays used for facet recovery. +arraypool *tg_crosstets, *tg_topnewtets, *tg_botnewtets; +arraypool *tg_topfaces, *tg_botfaces, *tg_midfaces; +arraypool *tg_topshells, *tg_botshells, *tg_facfaces; +arraypool *tg_toppoints, *tg_botpoints, *tg_facpoints; + +// Variables for accessing data fields (initialized in initializepools()). +int point2tetindex, pointmarkindex; +int elemmarkerindex; +int elemattribindex, volumeboundindex, highorderindex; +int shmarkindex, areaboundindex; + +// The number of hull tetrahedra (= the number of outer boundary faces). +long hullsize; + +// Current random number seed, number of random samples (for point location). +unsigned long randomseed, samples; + +// Pointers to a recently visited tetrahedron and subface. +triface recenttet; +face recentsh; + +// Two handles used in facet recovery (formcavity and fillcavity). +triface firsttopface, firstbotface; + +// The size of bounding boxes. +REAL xmax, xmin, ymax, ymin, zmax, zmin; + +// The number of duplicated vertices, mesh edges, and input segments. +long dupverts, meshedges, meshsubedges, insegments; + +// Flags to check imposed constraints, subfaces, subsegs. +int checkconstraints, checksubfaces, checksubsegs, checkpbcs; + +// Algorithm statistical counters. +long ptloc_count, ptloc_max_count; +long orient3dcount; +long inspherecount, insphere_sos_count; +long maxbowatcavsize, totalbowatcavsize, totaldeadtets; +long flip14count, flip26count, flipn2ncount; +long flip23count, flip32count, flipnmcount; +long flip13count, flip22count, flipn2nfcount; +REAL tloctime, tfliptime, tinserttime; +long triedgcount, triedgcopcount, trivercopcount; +long across_face_count, across_edge_count, across_max_count; +long r1count, r2count, r3count; +long maxcavsize, maxregionsize; +long cavitycount, ndelaunayedgecount, cavityexpcount; + +/////////////////////////////////////////////////////////////////////////////// +// // +// Functions for using memorypools. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void initializepools(); +void tetrahedrondealloc(tetrahedron*); +tetrahedron *tetrahedrontraverse(); +tetrahedron *alltetrahedrontraverse(); +void shellfacedealloc(memorypool*, shellface*); +shellface *shellfacetraverse(memorypool*); +void badfacedealloc(memorypool*, badface*); +badface *badfacetraverse(memorypool*); +void pointdealloc(point); +point pointtraverse(); +void maketetrahedron(triface*); +void makeshellface(memorypool*, face*); +void makepoint(point*); +void makeindex2pointmap(point*&); +void makepoint2submap(memorypool*, int*&, face*&); +void makepoint2tetmap(); + +/////////////////////////////////////////////////////////////////////////////// +// // +// Linear algebra operators. // +// // +/////////////////////////////////////////////////////////////////////////////// + +#define NORM2(x, y, z) ((x) * (x) + (y) * (y) + (z) * (z)) + +#define DIST(p1, p2) \ + sqrt(NORM2((p2)[0] - (p1)[0], (p2)[1] - (p1)[1], (p2)[2] - (p1)[2])) + +#define DOT(v1, v2) \ + ((v1)[0] * (v2)[0] + (v1)[1] * (v2)[1] + (v1)[2] * (v2)[2]) + +#define CROSS(v1, v2, n) \ + (n)[0] = (v1)[1] * (v2)[2] - (v2)[1] * (v1)[2];\ + (n)[1] = -((v1)[0] * (v2)[2] - (v2)[0] * (v1)[2]);\ + (n)[2] = (v1)[0] * (v2)[1] - (v2)[0] * (v1)[1] + +#define SETVECTOR3(V, a0, a1, a2) (V)[0] = (a0); (V)[1] = (a1); (V)[2] = (a2) + +#define SWAP2(a0, a1, tmp) (tmp) = (a0); (a0) = (a1); (a1) = (tmp) + +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 predicates, advanced tests, intersections. // +// // +/////////////////////////////////////////////////////////////////////////////// + +static REAL PI; + +REAL interiorangle(point o, point p1, point p2, REAL* n); +void facenormal(point, point, point, REAL *n, int pivot); +void circumsphere(point, point, point, point, REAL* cent, REAL* radius); + +REAL insphere_sos(point pa, point pb, point pc, point pd, point pe); +REAL incircle3d(point pa, point pb, point pc, point pd); +bool iscoplanar(point k, point l, point m, point n, REAL ori); + +int tri_edge_2d(point,point,point,point,point,point,int,int*,int*); +int tri_edge_test(point,point,point,point,point,point,int,int*,int*); +int tri_tri_2d(point,point,point,point,point,point,point,int,int*,int*); +int tri_tri_test(point,point,point,point,point,point,point,int,int*,int*); + +/////////////////////////////////////////////////////////////////////////////// +// // +// Mesh transformation operations. // +// // +// Mesh transformation operations translate an old set of tetrahedra into a // +// new set of tetrahedra in the same region of the tetrahedralization. Such // +// operations include face/edge flips, vertex insertion/deletions. // +// // +/////////////////////////////////////////////////////////////////////////////// + +badface* flipshpush(badface* flipstack, face* flipedge); +void flip13(point newpt, face* splitface, int flipflag); +void flipn2nf(point newpt, face* splitedges, int flipflag); +void flip22(face* flipfaces, int flipflag); +void lawsonflip(); + +badface* flippush(badface* flipstack, triface* flipface, point pushpt); +void flip14(point newpt, triface* splittet, int flipflag); +void flip26(point newpt, triface* splitface, int flipflag); +void flipn2n(point newpt, triface* splitedge, int flipflag); +void flip23(triface* fliptets, int hullflag, int flipflag); +void flip32(triface* fliptets, int hullflag, int flipflag); +//bool flipnm(int n, triface* fliptets, int flipflag); +void lawsonflip3d(int flipflag); + +/////////////////////////////////////////////////////////////////////////////// +// // +// Jump-and-Walk point location algorithm. // +// // +// The following functions implemented the randomized jump-and-walk point // +// location algorithm of Muecke, Saias, and Zhu [MueckeSaiasZhu96]. // +// // +/////////////////////////////////////////////////////////////////////////////// + +unsigned long randomnation(unsigned long choices); +void randomsample(point searchpt, triface* searchtet); +enum location locate(point searchpt, triface* searchtet); + +/////////////////////////////////////////////////////////////////////////////// +// // +// Incremental Delaunay tetrahedralization algorithms. // +// // +// Bowyer and Watson's incrmental insertion algorithm [Bowyer81, Watson81], // +// and Edelsbrunner and Shah's incrmental flip algorithm [Edelsbrunner96]. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void initialDT(point pa, point pb, point pc, point pd); +enum location insertvertex(point, triface*, bool, bool, bool, bool); +void flipinsertvertex(point, triface*, int); +void incrementaldelaunay(); + +/////////////////////////////////////////////////////////////////////////////// +// // +// Surface mesh routines. // +// // +/////////////////////////////////////////////////////////////////////////////// + +bool calculateabovepoint(arraypool*, point*, point*, point*); +enum location slocate(point, face*, bool); +enum location sinsertvertex(point, face*, face*, bool, bool); +enum intersection sscoutsegment(face* searchsh, point endpt); +void scarveholes(int, REAL*); +void triangulate(int, arraypool*, arraypool*, int, REAL*); + +void unifysubfaces(face*, face*); +void unifysegments(); +void mergefacets(); +void markacutevertices(); +void meshsurface(); + +/////////////////////////////////////////////////////////////////////////////// +// // +// Boundary recovery functions. // +// // +/////////////////////////////////////////////////////////////////////////////// + +enum intersection finddirection(triface* searchtet, point endpt); +enum intersection scoutsegment(face* sseg, triface* searchtet, point* refpt); +void getsegmentsplitpoint(face* sseg, point refpt, REAL* vt); +void getsegmentsplitpoint2(face* sseg, point refpt, REAL* vt); +void getsegmentsplitpoint3(face* sseg, point refpt, REAL* vt); +void delaunizesegments(); + +enum intersection scoutsubface(face* ssub, triface* searchtet); +enum intersection scoutcrosstet(face* ssub, triface* searchtet, arraypool*); +void recoversubfacebyflips(face* pssub, triface* crossface, arraypool*); +void formcavity(face*, arraypool*, arraypool*, arraypool*, arraypool*, + arraypool*, arraypool*); +bool delaunizecavity(arraypool*, arraypool*, arraypool*, arraypool*, + arraypool*, arraypool*); +bool fillcavity(arraypool*, arraypool*, arraypool*, arraypool*); +void carvecavity(arraypool*, arraypool*, arraypool*); +void restorecavity(arraypool*, arraypool*, arraypool*); +void splitsubedge(point, face*, arraypool*, arraypool*); +void constrainedfacets(); + +enum intersection scoutsegment2(face*, triface*, arraypool*); +bool tetrasegcavity(face*, arraypool*, arraypool*, arraypool*, arraypool*, + arraypool*, arraypool*, arraypool*, arraypool*); +bool recoversegments(arraypool*, arraypool*, arraypool*, arraypool*, + arraypool*, arraypool*); +void constrainedsegments(); + +void formskeleton(); +void carveholes(); + +/////////////////////////////////////////////////////////////////////////////// +// // +// Mesh refinement functions. // +// // +/////////////////////////////////////////////////////////////////////////////// + +bool checkedge4encroach(face& seg, point testpt, int enqflag); +void repairencsegs(); + +void enforcequality(); + +/////////////////////////////////////////////////////////////////////////////// +// // +// Mesh input & output functions. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void transfernodes(); +void reconstructmesh(); +void jettisonnodes(); +void highorder(); +void numberedges(); +void numbersubedges(); +void outnodes(tetgenio* out); +void outelements(tetgenio* out); +void outfaces(tetgenio* out); +void outhullfaces(tetgenio* out); +void outsubfaces(tetgenio* out); +void outedges(tetgenio* out); +void outsubsegments(tetgenio* out); +void outneighbors(tetgenio* out); +void outvoronoi(tetgenio* out); + +/////////////////////////////////////////////////////////////////////////////// +// // +// Mesh statistic functions. // +// // +/////////////////////////////////////////////////////////////////////////////// + +int checkmesh(); +int checkshells(int); +int checkdelaunay(int); +int checksegments(); +int checkconforming(); +void algorithmstatistics(); +void qualitystatistics(); +void statistics(); + +/////////////////////////////////////////////////////////////////////////////// +// // +// Class Constructor and Destructor. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void initialize() +{ + in = (tetgenio *) NULL; + b = (tetgenbehavior *) NULL; + tetrahedronpool = (memorypool *) NULL; + subfacepool = subsegpool = (memorypool *) NULL; + tet2segpool = tet2subpool = (memorypool *) NULL; + pointpool = (memorypool *) NULL; + flippool = (memorypool *) NULL; + badsegpool = badsubpool = badtetpool = (memorypool *) NULL; + dummypoint = (point) NULL; + futureflip = (badface *) NULL; + cavetetlist = cavebdrylist = caveoldtetlist = (arraypool *) NULL; + caveshlist = caveshbdlist = (arraypool *) NULL; + subsegstack = subfacstack = (arraypool *) NULL; + arraypool *tg_crosstets, *tg_topnewtets, *tg_botnewtets; + tg_topfaces = tg_botfaces = tg_midfaces = NULL; + tg_topshells = tg_botshells = tg_facfaces = NULL; + tg_toppoints = tg_botpoints = tg_facpoints = NULL; + point2tetindex = pointmarkindex = 0; + elemmarkerindex = 0; + elemattribindex = volumeboundindex = highorderindex = 0; + hullsize = 0l; + randomseed = samples = 1l; + recenttet.tet = (tetrahedron *) NULL; + recenttet.loc = recenttet.ver = 0; + xmax = xmin = ymax = ymin = zmax = zmin = 0.0; + dupverts = meshedges = meshsubedges = insegments = 0l; + checkconstraints = checksubfaces = checksubsegs = checkpbcs = 0; + ptloc_count = ptloc_max_count = 0l; + orient3dcount = 0l; + inspherecount = insphere_sos_count = 0l; + maxbowatcavsize = totalbowatcavsize = totaldeadtets = 0l; + flip14count = flip26count = flipn2ncount = 0l; + flip23count = flip32count = flipnmcount = 0l; + flip13count = flip22count = flipn2nfcount = 0l; + tloctime = tfliptime = tinserttime = 0.0; + triedgcount = triedgcopcount = trivercopcount = 0l; + across_face_count = across_edge_count = across_max_count = 0l; + r1count = r2count = r3count = 0l; + maxcavsize = maxregionsize = 0l; + cavitycount = ndelaunayedgecount = cavityexpcount = 0l; +} + +void deinitialize() +{ + in = (tetgenio *) NULL; + b = (tetgenbehavior *) NULL; + if (pointpool != (memorypool *) NULL) { + delete pointpool; + delete [] dummypoint; + } + if (tetrahedronpool != (memorypool *) NULL) { + delete tetrahedronpool; + delete cavetetlist; + delete cavebdrylist; + delete caveoldtetlist; + delete flippool; + } + if (subfacepool != (memorypool *) NULL) { + delete subfacepool; + delete subsegpool; + delete tet2segpool; + delete tet2subpool; + delete subsegstack; + delete subfacstack; + delete caveshlist; + delete caveshbdlist; + } + futureflip = (badface *) NULL; +} + +tetgenmesh() +{ + initialize(); +} + +~tetgenmesh() +{ + deinitialize(); +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// Debug functions. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void ptet(triface* t); +void psh(face* s); +void pteti(int i, int j, int k, int l); +void pface(int i, int j, int k); +bool pedge(int i, int j); +void psubface(int i, int j, int k); +int psubseg(int i, int j); +int pmark(point p); +void pvert(point p); +int pverti(int i); +REAL test_orient3d(int i, int j, int k, int l); +REAL test_insphere(int i, int j, int k, int l, int m); +int test_tritri(int a, int b, int c, int p, int q, int r); +void print_cavebdrylist(); +void print_flipstack(); +void print_tetarray(arraypool* tetarray, bool nohulltet); +void print_facearray(arraypool* facearray); +void print_subfacearray(arraypool* subfacearray); +void dump_cavity(arraypool *topfaces, arraypool *botfaces); +void dump_facetof(face* pssub, char* filename); +void dump_cavitynewtets(); + +/////////////////////////////////////////////////////////////////////////////// +}; // End of class tetgenmesh; +/////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +// // +// terminatetetgen() Terminate TetGen with a given exit code. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void terminatetetgen(int x); + +/////////////////////////////////////////////////////////////////////////////// +// // +// tetrahedralize() Interface for using TetGen's library to generate // +// Delaunay tetrahedralizations, constrained Delaunay // +// tetrahedralizations, quality tetrahedral meshes. // +// // +// 'in' is an object of 'tetgenio' which contains a PLC you want to tetrahed-// +// ralize or a previously generated tetrahedral mesh you want to refine. It // +// must not be a NULL. 'out' is another object of 'tetgenio' for storing the // +// generated tetrahedral mesh. It can be a NULL. If so, the output will be // +// saved to file(s). If 'bgmin' != NULL, it contains a background mesh which // +// defines a mesh size distruction function. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetrahedralize(tetgenbehavior *b, tetgenio *in, tetgenio *out, + tetgenio *addin = NULL, tetgenio *bgmin = NULL); + +void tetrahedralize(char *switches, tetgenio *in, tetgenio *out, + tetgenio *addin = NULL, tetgenio *bgmin = NULL); + +#endif // #ifndef tetgenH -- GitLab